1 /* Written by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import iv
.autocomplete
: acbuild
= autocomplete
;
34 // ////////////////////////////////////////////////////////////////////////// //
37 return "/home/ketmar/back/D/prj/nyatools/mono/injed.so";
39 import std
.file
: thisExePath
;
40 import std
.path
: buildPath
, dirName
;
41 return buildPath(thisExePath
.dirName
, "mono", "injed.so");
46 // ////////////////////////////////////////////////////////////////////////// //
47 alias string
[] delegate (const(char)[] pfx
) BuildACListCB
;
49 __gshared BuildACListCB
[string
] knownACB
;
52 // ////////////////////////////////////////////////////////////////////////// //
53 string
[] acAtFieldsCB (const(char)[] pfx
) {
54 import std
.algorithm
: startsWith
;
55 if (monoClasses
.length
== 0) return null; // no wai
56 //writeln("fields; pfx=[", pfx, "]");
58 if (pfx
.length
== 0) {
59 foreach (const ref cls
; monoClasses
) lst
~= cls
.fullname
;
62 foreach (const ref cls
; monoClasses
) if (cls
.name
.startsWith(pfx
)) lst
~= cls
.name
;
64 foreach (const ref cls
; monoClasses
) if (cls
.fullnamenoasm
.startsWith(pfx
)) lst
~= cls
.fullnamenoasm
;
66 foreach (const ref cls
; monoClasses
) if (cls
.fullname
!= cls
.fullnamenoasm
&& cls
.fullname
.startsWith(pfx
)) lst
~= cls
.fullname
;
73 // ////////////////////////////////////////////////////////////////////////// //
74 shared static this () {
75 import std
.functional
: toDelegate
;
76 knownACB
["@fields"] = toDelegate(&acAtFieldsCB
);
80 // ////////////////////////////////////////////////////////////////////////// //
81 class EditLineAC
: EditLine
{
84 override void autocomplete () {
85 if (!line
.canAutocomplete
) { beep
; return; }
86 int wc
= line
.wordCount
;
88 auto acwp
= line
.acWordPos
;
89 auto acnum
= line
.acWordNum
;
92 // we want autocomplete command
93 lst
= acbuild(line
.word(0).idup
,
115 } else if (acnum
== 1) {
116 // autocomplete argument
117 if (auto cb
= line
.word(0) in knownACB
) {
118 //clearOutput(); wrt("FOUND ACCB! ["); wrt(line[acwp]); wrt("]\n");
119 lst
= (*cb
)(line
[acwp
]);
120 if (lst
.length
== 0) { beep
; return; }
121 lst
= acbuild(line
[acwp
].idup
, lst
);
131 if (lst
.length
== 0) { beep
; return; }
132 if (lst
.length
== 1) {
133 if (!line
.acWordReplaceSpaced(lst
[0])) { beep
; return; }
134 } else if (lst
.length
> 1) {
135 if (!line
.acWordReplace(lst
[0])) { beep
; return; }
137 auto len
= (lst
.length
> 24 ?
24 : lst
.length
);
138 wrt("====================\n");
139 foreach (auto s
; lst
[1..len
]) { wrt(s
); wrt("\r\n"); }
140 if (len
< lst
.length
) { wrt("..."); wrtuint(cast(int)lst
.length
-len
); wrt(" more...\n"); }
146 // ////////////////////////////////////////////////////////////////////////// //
147 __gshared EditLineAC lineedit
;
150 // ////////////////////////////////////////////////////////////////////////// //
151 void cleanupMemory () {
152 { import core
.memory
: GC
; GC
.collect
; GC
.minimize
; }
156 // ////////////////////////////////////////////////////////////////////////// //
157 EditLine
.Result
readLine (uint vidx
) {
158 import std
.format
: format
;
159 if (lineedit
is null) lineedit
= new EditLineAC();
160 string tn
= taskNames
[vidx
];
162 lineedit
.prompt
= "(%s)[%s] %s>".format(vidx
+1, foundAddrs
[vidx
].count
, tn
);
164 lineedit
.prompt
= "(%s)[%s]>".format(vidx
+1, foundAddrs
[vidx
].count
);
166 auto res
= lineedit
.readline();
172 // ////////////////////////////////////////////////////////////////////////// //
173 __gshared AssemblyInfo
[] monoAsms
;
174 __gshared NCClassInfo
[] monoClasses
;
183 // return index or -1
184 int findClassByName (const(char)[] name
) {
187 uint cid
= name
.xto
!uint;
188 foreach (immutable idx
, const ref cls
; monoClasses
) if (cls
.id
== cid
) return cast(int)idx
;
189 if (cid
< monoClasses
.length
) return cid
;
190 } catch (Exception
) {}
192 foreach (immutable idx
, const ref cls
; monoClasses
) {
193 if (cls
.fullname
== name
) return cast(int)idx
; // exact match
196 foreach (immutable idx
, const ref cls
; monoClasses
) {
197 if (cls
.isNameEqu(name
)) {
198 if (res
!= -1) return -1;
208 monoAsms
= injSock
.rpcall
!enumAssemblies();
209 writeln(monoAsms
.length
, " mono assemblies found");
212 monoClasses
= injSock
.rpcall
!getClassList();
213 writeln(injSock
.bytesReceived
, " bytes received");
214 foreach (ref cls
; monoClasses
) {
215 foreach (const ref ass
; monoAsms
) {
216 if (cls
.asmid
== ass
.id
) {
217 cls
.asmname
= ass
.name
;
222 cls
.fullname
= cls
.asmname
;
223 if (cls
.nspace
.length
) { if (cls
.fullname
.length
) cls
.fullname
~= '.'; cls
.fullname
~= cls
.nspace
; }
224 if (cls
.name
.length
) { if (cls
.fullname
.length
) cls
.fullname
~= '.'; cls
.fullname
~= cls
.name
; }
225 // build full name w/o assembly
226 cls
.fullnamenoasm
= cls
.nspace
;
227 if (cls
.name
.length
) { if (cls
.fullnamenoasm
.length
) cls
.fullnamenoasm
~= '.'; cls
.fullnamenoasm
~= cls
.name
; }
229 writeln(monoClasses
.length
, " mono classes found");
234 // ////////////////////////////////////////////////////////////////////////// //
235 __gshared
uint preypid
;
236 __gshared MemoryRegionInfo
[] memregs
;
237 __gshared string nyaSockName
;
238 __gshared UDSocket nyaSock
, injSock
;
239 __gshared
bool codeInjected
= false;
241 __gshared InjCodeInfo injInfo
;
244 void restart (uint newpid
) {
245 import core
.sys
.posix
.unistd
: getpid
;
246 import std
.string
: format
;
247 __gshared
uint counter
= 0;
251 if (newpid
< 1) throw new Exception("invalid pid");
254 nyaSockName
= "/nya-tools/injected/%s_%s_%s".format(getpid(), newpid
, counter
++);
255 nyaSock
.create(nyaSockName
);
256 codeInjected
= false;
260 // ////////////////////////////////////////////////////////////////////////// //
263 __gshared AddrStore
[MaxVals
] foundAddrs
;
264 __gshared string
[MaxVals
] taskNames
;
267 // ////////////////////////////////////////////////////////////////////////// //
269 const(char)[] commaed (ulong l
) {
270 __gshared
uint bufnum
;
271 __gshared
char[256][32] bufs
;
272 auto res
= bufs
.ptr
[bufnum
++][];
273 bufnum
%= bufs
.length
;
274 uint pos
= cast(uint)res
.length
;
277 if (digsleft
== 0) { res
.ptr
[--pos
] = ','; digsleft
= 3; }
278 res
.ptr
[--pos
] = cast(char)(l
%10+'0');
286 // ////////////////////////////////////////////////////////////////////////// //
288 import core
.sys
.posix
.unistd
: getpid
;
289 import std
.file
: exists
;
290 import std
.format
: format
;
291 __gshared
uint counter
= 0;
292 if (codeInjected
) return;
294 auto soname
= getSOName();
295 if (!soname
.exists
) {
296 stderr
.writeln("ERROR: '", soname
, "' not found!");
299 string newSOName
= "/tmp/_injed_%s_%s_%s.so\0".format(getpid(), preypid
, counter
++);
301 import core
.sys
.posix
.unistd
: unlink
;
302 unlink(newSOName
.ptr
);
305 auto fi
= VFile(soname
, "r");
306 auto fo
= VFile(newSOName
[0..$-1], "w");
307 char[1024] buf
= void;
308 while (fi
.tell
< fi
.size
) {
309 auto rd
= fi
.size
-fi
.tell
;
310 if (rd
> buf
.length
) rd
= buf
.length
;
311 fi
.rawReadExact(buf
[0..cast(uint)rd
]);
312 fo
.rawWrite(buf
[0..cast(uint)rd
]);
317 import core
.sys
.posix
.sys
.stat
: chmod
;
318 chmod(newSOName
.ptr
, 0o755
);
320 //writeln("'", soname, "' -> '", newSOName[0..$-1], "'");
321 } catch (Exception
) {
322 stderr
.writeln("ERROR: .so copying error!");
326 if (!injectSO(preypid
, newSOName
[0..$-1], nyaSockName
)) {
327 writeln("injecting failed");
329 writeln("injecting succeded, waiting for pingback");
330 injSock
= nyaSock
.accept();
331 writeln("pingback received, injection complete (fd=", injSock
.fd
, ")");
332 injSock
.ncReceivePingback(injInfo
);
333 writefln("pray has %sWINE, pray has %smono", (injInfo
.hasWine ?
"" : "no "), (injInfo
.hasMono ?
"" : "no "));
335 if (injInfo
.hasMono
) monoScan();
340 // ////////////////////////////////////////////////////////////////////////// //
341 void reloadRegions () {
342 import std
.algorithm
: max
, min
;
343 memregs
= readMemoryRegions(preypid
, true);
344 uint total
= 0, minsz
= uint.max
, maxsz
= 0;
345 foreach (const ref reg
; memregs
) {
346 total
+= reg
.end
-reg
.start
;
347 minsz
= min(minsz
, reg
.end
-reg
.start
);
348 maxsz
= max(maxsz
, reg
.end
-reg
.start
);
350 uint mbs
= (total
/1024)*10/1024;
351 writefln("%s.%s megabytes, %s regons (min:%s; max:%s)", mbs
/10, mbs
%10, memregs
.length
, commaed(minsz
), commaed(maxsz
));
356 enum IsValidScanType(T
) = (is(T
== float) ||
is(T
== double) ||
(__traits(isIntegral
, T
) && T
.sizeof
<= 8));
358 template ScanType2VT(T
) {
359 static if (is(T
== float)) enum ScanType2VT
= VType
.Float
;
360 else static if (is(T
== double)) enum ScanType2VT
= VType
.Double
;
361 else static if (is(T
== byte) ||
is(T
== ubyte)) enum ScanType2VT
= VType
.Byte
;
362 else static if (is(T
== short) ||
is(T
== ushort)) enum ScanType2VT
= VType
.Word
;
363 else static if (is(T
== int) ||
is(T
== uint)) enum ScanType2VT
= VType
.Int
;
364 else static if (is(T
== long) ||
is(T
== ulong)) enum ScanType2VT
= VType
.Long
;
365 else static assert(0, "invalid scan type");
369 // this can't be used in multiple threads anyway
370 void scanRegion(T
) (AttachedProc atp
, T val
, uint start
, uint end
, scope void delegate (uint addr
, ubyte vt
) savea
) if (IsValidScanType
!T
) {
371 enum BufSize
= 4*1024*1024;
372 import core
.stdc
.stdlib
: malloc
, free
;
373 __gshared
ubyte* buf
;
375 buf
= cast(ubyte*)malloc(BufSize
+8);
376 if (buf
is null) assert(0, "out of memory");
378 ubyte vt
= ScanType2VT
!T
;
379 while (start
< end
&& end
-start
>= T
.sizeof
) {
380 uint len
= end
-start
;
384 next
+= BufSize
-T
.sizeof
;
388 auto rd
= atp
.readBytes(buf
[0..len
], start
);
389 if (rd
.length
!= len
) assert(0, "wtf?!");
391 while (len
>= T
.sizeof
) {
392 import core
.stdc
.string
: memchr
, memcmp
;
393 auto np
= cast(ubyte*)memchr(dp
, *cast(ubyte*)&val
, len
);
394 if (np
is null) break;
395 uint ofs
= cast(uint)(np
-dp
);
399 if (memcmp(dp
, &val
, T
.sizeof
) == 0) savea(start
, vt
);
409 // ////////////////////////////////////////////////////////////////////////// //
410 void doNewScanBytes(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
411 foundAddrs
[vidx
].clear
;
413 auto atp
= AttachedProc(preypid
);
414 foreach (const ref reg
; memregs
) {
415 scanRegion(atp
, val
, reg
.start
, reg
.end
, (uint addr
, ubyte vt
) { foundAddrs
.ptr
[vidx
].addAddress(addr
, vt
); });
417 writeln(foundAddrs
[vidx
].count
, " matches found");
422 void doFilter(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
423 auto atp
= AttachedProc(preypid
);
424 auto reg
= ASRangeD(foundAddrs
[vidx
]); // this clears fa
425 ubyte vt
= ScanType2VT
!T
;
430 if ((va
.vt
&vt
) != 0) {
431 if (atp
.readBytes((&mv
)[0..1], va
.addr
).length
== 1) {
432 if (mv
== val
) foundAddrs
.ptr
[vidx
].addAddress(va
.addr
, vt
);
439 // ////////////////////////////////////////////////////////////////////////// //
440 void listMatches (uint vidx
) {
441 if (foundAddrs
[vidx
].count
== 0) return;
442 auto atp
= AttachedProc(preypid
);
444 foreach (auto va
; ASRange(foundAddrs
[vidx
])) {
446 writef("[%3s] [%s] 0x%08x ", idx
, vtName(va
.vt
), va
.addr
);
447 if (va
.vt
&VType
.IMask
) {
449 if (atp
.readBytes((cast(ubyte*)&v
)[0..maxVTSize(va
.vt
)], va
.addr
).length
== maxVTSize(va
.vt
)) writeln(v
); else writeln("ERROR");
450 } else if (va
.vt
== VType
.Float
) {
452 if (atp
.readBytes((&v
)[0..1], va
.addr
).length
== 1) writeln(v
); else writeln("ERROR");
453 } else if (va
.vt
== VType
.Double
) {
455 if (atp
.readBytes((&v
)[0..1], va
.addr
).length
== 1) writeln(v
); else writeln("ERROR");
463 // ////////////////////////////////////////////////////////////////////////// //
464 void xfilter(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
465 auto olen
= foundAddrs
[vidx
].count
;
467 auto xo
= foundAddrs
[vidx
].count
;
469 if (foundAddrs
[vidx
].count
== 0 || foundAddrs
[vidx
].count
== xo
) break;
471 writeln("reduced from ", olen
, " to ", foundAddrs
[vidx
].count
, " matches");
475 // ////////////////////////////////////////////////////////////////////////// //
476 void setVal(T
) (AttachedProc atp
, uint addr
, T val
) if (IsValidScanType
!T
) {
477 atp
.writeBytes((&val
)[0..1], addr
);
481 // ////////////////////////////////////////////////////////////////////////// //
482 T
xto(T
) (const(char)[] s
) if (__traits(isIntegral
, T
) && T
.sizeof
<= 8) {
483 import std
.conv
: to
;
485 if (s
.length
> 0 && s
[0] == '-') { neg = true; s
= s
[1..$]; }
486 else if (s
.length
> 0 && s
[0] == '+') { s
= s
[1..$]; }
487 if (s
.length
== 0) throw new Exception("invalid value");
489 if (s
.length
> 2 && s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X')) {
490 v
= to
!ulong(s
[2..$], 16);
496 if (nv
< 0) throw new Exception("invalid value");
498 if (nv
> 0) throw new Exception("invalid value");
499 static if (T
.sizeof
== 1) { if (nv
< byte.min || nv
> byte.max
) throw new Exception("invalid value"); }
500 else static if (T
.sizeof
== 2) { if (nv
< short.min || nv
> short.max
) throw new Exception("invalid value"); }
501 else static if (T
.sizeof
== 4) { if (nv
< int.min || nv
> int.max
) throw new Exception("invalid value"); }
504 static if (T
.sizeof
== 1) { if (v
> ubyte.max
) throw new Exception("invalid value"); }
505 else static if (T
.sizeof
== 2) { if (v
> ushort.max
) throw new Exception("invalid value"); }
506 else static if (T
.sizeof
== 4) { if (v
> uint.max
) throw new Exception("invalid value"); }
512 // ////////////////////////////////////////////////////////////////////////// //
513 void main (string
[] args
) {
514 rpcRegisterModuleEndpoints
!monox
;
517 if (args
.length
> 1) {
518 import std
.conv
: to
;
520 pid
= to
!uint(args
[1]);
521 } catch (Exception e
) {
522 int p
= findProcessByName(args
[1]);
524 writeln("process '", args
[1], "' not found!");
528 writeln("process '", args
[1], "' has pid ", pid
);
533 uint vidx
= 0; // current workset
534 // last search values and types for all worksets
535 ulong[MaxVals
] lastU64
= 0;
536 float[MaxVals
] lastF32
= 0;
537 double[MaxVals
] lastF64
= 0;
538 ubyte[MaxVals
] lastType
= VType
.Int
;
539 bool[MaxVals
] wasSearched
= false;
541 //TODO: convert this to CTFE generator
542 void doTypedAction(string fnname
) () {
543 if (lastType
[vidx
] == VType
.Float
) mixin(fnname
~"(vidx, lastF32[vidx]);");
544 else if (lastType
[vidx
] == VType
.Double
) mixin(fnname
~"(vidx, lastF64[vidx]);");
545 else if (lastType
[vidx
] == VType
.Byte
) mixin(fnname
~"(vidx, cast(ubyte)lastU64[vidx]);");
546 else if (lastType
[vidx
] == VType
.Word
) mixin(fnname
~"(vidx, cast(ushort)lastU64[vidx]);");
547 else if (lastType
[vidx
] == VType
.Int
) mixin(fnname
~"(vidx, cast(uint)lastU64[vidx]);");
548 else if (lastType
[vidx
] == VType
.Long
) mixin(fnname
~"(vidx, cast(ulong)lastU64[vidx]);");
549 else assert(0, "invalid type!");
552 //TODO: convert this to CTFE generator
554 void doTypedSet (uint addr
, const(char)[] val
) {
555 import std
.conv
: to
;
557 if (val
.length
== 0) throw new Exception("invalid value");
558 if (lastType
[vidx
] == VType
.Float
) {
559 float v
= to
!float(val
);
560 auto atp
= AttachedProc(preypid
);
562 } else if (lastType
[vidx
] == VType
.Double
) {
563 double v
= to
!double(val
);
564 auto atp
= AttachedProc(preypid
);
567 auto atp
= AttachedProc(preypid
);
568 if (lastType
[vidx
] == VType
.Byte
) atp
.setVal
!ubyte(addr
, val
.xto
!ubyte);
569 else if (lastType
[vidx
] == VType
.Word
) atp
.setVal
!ushort(addr
, val
.xto
!ushort);
570 else if (lastType
[vidx
] == VType
.Int
) atp
.setVal
!uint(addr
, val
.xto
!uint);
571 else if (lastType
[vidx
] == VType
.Long
) atp
.setVal
!ulong(addr
, val
.xto
!ulong);
572 else assert(0, "wtf?!");
577 auto res
= readLine(vidx
);
578 if (res
!= EditLine
.Result
.Normal
) break;
579 if (lineedit
.line
.wordCount
== 0) continue;
580 auto cmd
= lineedit
.line
.word(0); // command
581 if (cmd
.length
== 0) continue;
582 if (cmd
== "!!") { injectCode(); continue; }
584 if (cmd
.length
== 1) { lineedit
.beep
; continue; }
585 lineedit
.pushCurrentToHistory();
586 if (!codeInjected
) { writeln("prey is not hunted"); continue; }
588 string
[] list
= injSock
.rpcallany
!(string
[])("injed.listEndpoints");
589 writeln(list
.length
, " endpoints found");
590 foreach (string s
; list
) writeln(" ", s
);
593 if (cmd
== "@asms") {
594 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
595 writeln("=== assemblies ===");
596 foreach (const ref ass
; monoAsms
) {
597 writefln("#%s: %s", ass
.id
, ass
.name
);
602 auto wc
= lineedit
.line
.wordCount
;
603 if (cmd
== "@writeclasses") {
604 if (wc
!= 2) { writeln("filename?"); continue; }
605 auto arg
= lineedit
.line
.word(1);
607 auto fo
= VFile(arg
, "w");
608 foreach (const ref cls
; monoClasses
) fo
.writefln("0x%08x %s", cls
.id
, cls
.fullname
);
609 } catch (Exception
) {
610 writeln("write error with file '", arg
, "'");
614 if (cmd
== "@asmname") {
615 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
616 import core
.stdc
.string
: memcpy
;
617 if (wc
!= 2) { writeln("assembly id only"); continue; }
618 auto arg
= lineedit
.line
.word(1);
619 uint asmid
= uint.max
;
620 foreach (immutable idx
, const ref ass
; monoAsms
) if (ass
.name
== arg
) { asmid
= cast(uint)idx
; break; }
621 if (asmid
== uint.max
) {
622 try asmid
= arg
.xto
!uint; catch (Exception
) { writeln("invalid assembly id"); continue; }
624 if (asmid
< monoAsms
.length
) {
625 writefln("assembly #%s has id 0x%08x and name '%s'", asmid
, monoAsms
[asmid
].id
, monoAsms
[asmid
].name
);
628 foreach (immutable idx
, const ref ass
; monoAsms
) {
629 if (ass
.id
== asmid
) {
631 writefln("assembly #%s has id 0x%08x and name '%s'", idx
, ass
.id
, ass
.name
);
635 if (!found
) writeln("assembly not found");
639 if (cmd
== "@classes") {
640 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
641 import core
.stdc
.string
: memcpy
;
642 if (wc
> 2) { writeln("assembly id only"); continue; }
643 uint asmid
= uint.max
;
645 auto arg
= lineedit
.line
.word(1);
646 foreach (immutable idx
, const ref ass
; monoAsms
) if (ass
.name
== arg
) { asmid
= cast(uint)idx
; break; }
647 if (asmid
== uint.max
) {
648 try asmid
= arg
.xto
!uint; catch (Exception
) { writeln("invalid assembly id"); continue; }
650 if (asmid
< monoAsms
.length
) {
653 foreach (immutable idx
, const ref ass
; monoAsms
) {
654 if (ass
.id
== asmid
) {
656 asmid
= cast(uint)idx
;
660 if (!found
) { writeln("assembly not found"); continue; }
663 foreach (const ref cls
; monoClasses
) {
664 if (asmid
== uint.max || cls
.asmid
== asmid
) {
665 stderr
.writefln("0x%08x %s", cls
.id
, cls
.fullname
);
670 if (cmd
== "@fields") {
671 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
672 if (wc
!= 2) { writeln("class id only"); continue; }
673 auto arg
= lineedit
.line
.word(1);
674 int cid
= findClassByName(arg
);
675 if (cid
< 0) { writeln("class '", arg
, "' not found"); continue; }
676 //injSock.ncSendCommand(ReqCommand.MonoGetClassFields, monoClasses[cid].id);
678 scope(exit
) list
= null;
680 list
= injSock
.rpcall
!getClassFields(monoClasses
[cid
].id
);
681 writeln("=== ", list
, " fields ===");
682 foreach (const ref fld; list
) {
683 writefln("%s %s at 0x%08x (flags=0x%08x)", fld.typename
, fld.name
, fld.ofs
, fld.flags
);
685 } catch (Exception e
) {
686 writeln("ERROR: ", e
.msg
);
691 writeln("invalid remote command");
694 if (cmd
== "!1") { vidx
= 0; continue; }
695 if (cmd
== "!2") { vidx
= 1; continue; }
696 if (cmd
== "!3") { vidx
= 2; continue; }
697 if (cmd
== "!4") { vidx
= 3; continue; }
698 if (cmd
== "!5") { vidx
= 4; continue; }
699 if (cmd
== "!6") { vidx
= 5; continue; }
700 if (cmd
== "!7") { vidx
= 6; continue; }
701 if (cmd
== "!8") { vidx
= 7; continue; }
702 if (cmd
== "!9") { vidx
= 8; continue; }
703 if (cmd
== "q" || cmd
== "quit") break;
705 if (!wasSearched
[vidx
]) {
706 writeln("no previous value to filter; type is ", vtName(lastType
[vidx
]));
708 write("last search(", vtName(lastType
[vidx
]), "): ");
709 if (lastType
[vidx
] == VType
.Float
) writeln(lastF32
[vidx
]);
710 else if (lastType
[vidx
] == VType
.Double
) writeln(lastF64
[vidx
]);
711 else writeln(lastU64
[vidx
]);
716 if (!wasSearched
[vidx
]) {
717 writeln("no previous value to filter");
719 doTypedAction
!"xfilter"();
723 lineedit
.pushCurrentToHistory();
724 if (cmd
== "asbyte") { if (lastType
[vidx
] != VType
.Byte
) { lastType
[vidx
] = VType
.Byte
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
725 if (cmd
== "asshort") { if (lastType
[vidx
] != VType
.Word
) { lastType
[vidx
] = VType
.Word
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
726 if (cmd
== "asint") { if (lastType
[vidx
] != VType
.Int
) { lastType
[vidx
] = VType
.Int
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
727 if (cmd
== "aslong") { if (lastType
[vidx
] != VType
.Long
) { lastType
[vidx
] = VType
.Long
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
728 if (cmd
== "asfloat") { if (lastType
[vidx
] != VType
.Float
) { lastType
[vidx
] = VType
.Float
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
729 if (cmd
== "asdouble") { if (lastType
[vidx
] != VType
.Double
) { lastType
[vidx
] = VType
.Double
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
730 if (cmd
== "new") { taskNames
[vidx
] = null; foundAddrs
[vidx
].clear
; wasSearched
[vidx
] = false; continue; }
731 if (cmd
== "killall") {
733 foreach (ref fv
; foundAddrs
) fv
.clear();
734 wasSearched
[] = false;
735 lastType
[] = VType
.Int
;
740 if (cmd
== "list" || cmd
== "l" || cmd
== "ls") { listMatches(vidx
); continue; }
742 import std
.conv
: to
;
743 if (lastType
[vidx
] == VType
.Float
) lastF32
[vidx
] = to
!float(cmd
);
744 else if (lastType
[vidx
] == VType
.Double
) lastF64
[vidx
] = to
!double(cmd
);
745 else if (lastType
[vidx
] == VType
.Byte
) lastU64
[vidx
] = xto
!ubyte(cmd
);
746 else if (lastType
[vidx
] == VType
.Word
) lastU64
[vidx
] = xto
!ushort(cmd
);
747 else if (lastType
[vidx
] == VType
.Int
) lastU64
[vidx
] = xto
!uint(cmd
);
748 else if (lastType
[vidx
] == VType
.Long
) lastU64
[vidx
] = xto
!ulong(cmd
);
749 if (wasSearched
[vidx
]) doTypedAction
!"xfilter"(); else doTypedAction
!"doNewScanBytes"();
750 wasSearched
[vidx
] = true;
752 } catch (Exception e
) {}
754 auto wc
= lineedit
.line
.wordCount
;
756 auto arg
= lineedit
.line
.word(1);
758 if (wc
!= 2) throw new Exception("name?");
759 taskNames
[vidx
] = arg
.idup
;
763 import std
.conv
: to
;
764 const(char)[] saddr
, sval
;
765 if (wc
== 2 && foundAddrs
[vidx
].count
== 1) {
769 if (wc
!= 3) throw new Exception("set args fucked");
771 sval
= lineedit
.line
.word(2);
773 if (saddr
.length
> 2 && saddr
[0..2] == "0x") {
774 auto addr
= to
!uint(saddr
[2..$], 16);
775 doTypedSet(addr
, sval
);
776 } else if (saddr
== "*") {
777 auto atp
= AttachedProc(preypid
);
778 foreach (auto va
; ASRange(foundAddrs
[vidx
])) {
779 //FIXME: different types
780 doTypedSet(va
.addr
, sval
);
783 auto idx
= to
!uint(saddr
);
784 if (idx
>= foundAddrs
[vidx
].count
) throw new Exception("invalid value index");
785 auto va
= foundAddrs
[vidx
].at(idx
);
786 auto atp
= AttachedProc(preypid
);
787 doTypedSet(va
.addr
, sval
);
792 import std
.conv
: to
;
793 if (wc
!= 2) throw new Exception("set args fucked");
795 if (saddr
.length
> 2 && saddr
[0..2] == "0x") {
796 auto addr
= to
!uint(saddr
[2..$], 16);
797 foundAddrs
[vidx
].clear
;
798 foundAddrs
[vidx
].addAddress(addr
, VType
.Int
);
800 auto idx
= to
!uint(saddr
);
801 if (idx
>= foundAddrs
[vidx
].count
) throw new Exception("invalid value index");
802 auto va
= foundAddrs
[vidx
].at(idx
);
803 foundAddrs
[vidx
].clear
;
804 foundAddrs
[vidx
].addAddress(va
.addr
, va
.vt
);
809 } catch (Exception e
) {
810 writeln("ERROR: ", e
.msg
);
813 writeln("invalid command");