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
,
114 } else if (acnum
== 1) {
115 // autocomplete argument
116 if (auto cb
= line
.word(0) in knownACB
) {
117 //clearOutput(); wrt("FOUND ACCB! ["); wrt(line[acwp]); wrt("]\n");
118 lst
= (*cb
)(line
[acwp
]);
119 if (lst
.length
== 0) { beep
; return; }
120 lst
= acbuild(line
[acwp
].idup
, lst
);
130 if (lst
.length
== 0) { beep
; return; }
131 if (lst
.length
== 1) {
132 if (!line
.acWordReplaceSpaced(lst
[0])) { beep
; return; }
133 } else if (lst
.length
> 1) {
134 if (!line
.acWordReplace(lst
[0])) { beep
; return; }
136 auto len
= (lst
.length
> 24 ?
24 : lst
.length
);
137 wrt("====================\n");
138 foreach (auto s
; lst
[1..len
]) { wrt(s
); wrt("\r\n"); }
139 if (len
< lst
.length
) { wrt("..."); wrtuint(cast(int)lst
.length
-len
); wrt(" more...\n"); }
145 // ////////////////////////////////////////////////////////////////////////// //
146 __gshared EditLineAC lineedit
;
149 // ////////////////////////////////////////////////////////////////////////// //
150 void cleanupMemory () {
151 { import core
.memory
: GC
; GC
.collect
; GC
.minimize
; }
155 // ////////////////////////////////////////////////////////////////////////// //
156 EditLine
.Result
readLine (uint vidx
) {
157 import std
.format
: format
;
158 if (lineedit
is null) lineedit
= new EditLineAC();
159 string tn
= taskNames
[vidx
];
161 lineedit
.prompt
= "(%s)[%s] %s>".format(vidx
+1, foundAddrs
[vidx
].count
, tn
);
163 lineedit
.prompt
= "(%s)[%s]>".format(vidx
+1, foundAddrs
[vidx
].count
);
165 auto res
= lineedit
.readline();
171 // ////////////////////////////////////////////////////////////////////////// //
172 __gshared AssemblyInfo
[] monoAsms
;
173 __gshared NCClassInfo
[] monoClasses
;
182 // return index or -1
183 int findClassByName (const(char)[] name
) {
186 uint cid
= name
.xto
!uint;
187 foreach (immutable idx
, const ref cls
; monoClasses
) if (cls
.id
== cid
) return cast(int)idx
;
188 if (cid
< monoClasses
.length
) return cid
;
189 } catch (Exception
) {}
191 foreach (immutable idx
, const ref cls
; monoClasses
) {
192 if (cls
.fullname
== name
) return cast(int)idx
; // exact match
195 foreach (immutable idx
, const ref cls
; monoClasses
) {
196 if (cls
.isNameEqu(name
)) {
197 if (res
!= -1) return -1;
207 monoAsms
= injSock
.rpcall
!enumAssemblies();
208 writeln(monoAsms
.length
, " mono assemblies found");
211 monoClasses
= injSock
.rpcall
!getClassList();
212 writeln(injSock
.bytesReceived
, " bytes received");
213 foreach (ref cls
; monoClasses
) {
214 foreach (const ref ass
; monoAsms
) {
215 if (cls
.asmid
== ass
.id
) {
216 cls
.asmname
= ass
.name
;
221 cls
.fullname
= cls
.asmname
;
222 if (cls
.nspace
.length
) { if (cls
.fullname
.length
) cls
.fullname
~= '.'; cls
.fullname
~= cls
.nspace
; }
223 if (cls
.name
.length
) { if (cls
.fullname
.length
) cls
.fullname
~= '.'; cls
.fullname
~= cls
.name
; }
224 // build full name w/o assembly
225 cls
.fullnamenoasm
= cls
.nspace
;
226 if (cls
.name
.length
) { if (cls
.fullnamenoasm
.length
) cls
.fullnamenoasm
~= '.'; cls
.fullnamenoasm
~= cls
.name
; }
228 writeln(monoClasses
.length
, " mono classes found");
233 // ////////////////////////////////////////////////////////////////////////// //
234 __gshared
uint preypid
;
235 __gshared MemoryRegionInfo
[] memregs
;
236 __gshared string nyaSockName
;
237 __gshared UDSocket nyaSock
, injSock
;
238 __gshared
bool codeInjected
= false;
240 __gshared InjCodeInfo injInfo
;
243 void restart (uint newpid
) {
244 import core
.sys
.posix
.unistd
: getpid
;
245 import std
.string
: format
;
246 __gshared
uint counter
= 0;
250 if (newpid
< 1) throw new Exception("invalid pid");
253 nyaSockName
= "/nya-tools/injected/%s_%s_%s".format(getpid(), newpid
, counter
++);
254 nyaSock
.create(nyaSockName
);
255 codeInjected
= false;
259 // ////////////////////////////////////////////////////////////////////////// //
262 __gshared AddrStore
[MaxVals
] foundAddrs
;
263 __gshared string
[MaxVals
] taskNames
;
266 // ////////////////////////////////////////////////////////////////////////// //
268 const(char)[] commaed (ulong l
) {
269 __gshared
uint bufnum
;
270 __gshared
char[256][32] bufs
;
271 auto res
= bufs
.ptr
[bufnum
++][];
272 bufnum
%= bufs
.length
;
273 uint pos
= cast(uint)res
.length
;
276 if (digsleft
== 0) { res
.ptr
[--pos
] = ','; digsleft
= 3; }
277 res
.ptr
[--pos
] = cast(char)(l
%10+'0');
285 // ////////////////////////////////////////////////////////////////////////// //
287 import core
.sys
.posix
.unistd
: getpid
;
288 import std
.file
: exists
;
289 import std
.format
: format
;
290 __gshared
uint counter
= 0;
291 if (codeInjected
) return;
293 auto soname
= getSOName();
294 if (!soname
.exists
) {
295 stderr
.writeln("ERROR: '", soname
, "' not found!");
298 string newSOName
= "/tmp/_injed_%s_%s_%s.so\0".format(getpid(), preypid
, counter
++);
300 import core
.sys
.posix
.unistd
: unlink
;
301 unlink(newSOName
.ptr
);
304 auto fi
= VFile(soname
, "r");
305 auto fo
= VFile(newSOName
[0..$-1], "w");
306 char[1024] buf
= void;
307 while (fi
.tell
< fi
.size
) {
308 auto rd
= fi
.size
-fi
.tell
;
309 if (rd
> buf
.length
) rd
= buf
.length
;
310 fi
.rawReadExact(buf
[0..cast(uint)rd
]);
311 fo
.rawWrite(buf
[0..cast(uint)rd
]);
316 import core
.sys
.posix
.sys
.stat
: chmod
;
317 chmod(newSOName
.ptr
, 0o755
);
319 //writeln("'", soname, "' -> '", newSOName[0..$-1], "'");
320 } catch (Exception
) {
321 stderr
.writeln("ERROR: .so copying error!");
325 if (!injectSO(preypid
, newSOName
[0..$-1], nyaSockName
)) {
326 writeln("injecting failed");
328 writeln("injecting succeded, waiting for pingback");
329 injSock
= nyaSock
.accept();
330 writeln("pingback received, injection complete (fd=", injSock
.fd
, ")");
331 injSock
.ncReceivePingback(injInfo
);
332 writefln("pray has %sWINE, pray has %smono", (injInfo
.hasWine ?
"" : "no "), (injInfo
.hasMono ?
"" : "no "));
334 if (injInfo
.hasMono
) monoScan();
339 // ////////////////////////////////////////////////////////////////////////// //
340 void reloadRegions () {
341 import std
.algorithm
: max
, min
;
342 memregs
= readMemoryRegions(preypid
, true);
343 uint total
= 0, minsz
= uint.max
, maxsz
= 0;
344 foreach (const ref reg
; memregs
) {
345 total
+= reg
.end
-reg
.start
;
346 minsz
= min(minsz
, reg
.end
-reg
.start
);
347 maxsz
= max(maxsz
, reg
.end
-reg
.start
);
349 uint mbs
= (total
/1024)*10/1024;
350 writefln("%s.%s megabytes, %s regons (min:%s; max:%s)", mbs
/10, mbs
%10, memregs
.length
, commaed(minsz
), commaed(maxsz
));
355 enum IsValidScanType(T
) = (is(T
== float) ||
is(T
== double) ||
(__traits(isIntegral
, T
) && T
.sizeof
<= 8));
357 template ScanType2VT(T
) {
358 static if (is(T
== float)) enum ScanType2VT
= VType
.Float
;
359 else static if (is(T
== double)) enum ScanType2VT
= VType
.Double
;
360 else static if (is(T
== byte) ||
is(T
== ubyte)) enum ScanType2VT
= VType
.Byte
;
361 else static if (is(T
== short) ||
is(T
== ushort)) enum ScanType2VT
= VType
.Word
;
362 else static if (is(T
== int) ||
is(T
== uint)) enum ScanType2VT
= VType
.Int
;
363 else static if (is(T
== long) ||
is(T
== ulong)) enum ScanType2VT
= VType
.Long
;
364 else static assert(0, "invalid scan type");
368 // this can't be used in multiple threads anyway
369 void scanRegion(T
) (AttachedProc atp
, T val
, uint start
, uint end
, scope void delegate (uint addr
, ubyte vt
) savea
) if (IsValidScanType
!T
) {
370 enum BufSize
= 4*1024*1024;
371 import core
.stdc
.stdlib
: malloc
, free
;
372 __gshared
ubyte* buf
;
374 buf
= cast(ubyte*)malloc(BufSize
+8);
375 if (buf
is null) assert(0, "out of memory");
377 ubyte vt
= ScanType2VT
!T
;
378 while (start
< end
&& end
-start
>= T
.sizeof
) {
379 uint len
= end
-start
;
383 next
+= BufSize
-T
.sizeof
;
387 auto rd
= atp
.readBytes(buf
[0..len
], start
);
388 if (rd
.length
!= len
) assert(0, "wtf?!");
390 while (len
>= T
.sizeof
) {
391 import core
.stdc
.string
: memchr
, memcmp
;
392 auto np
= cast(ubyte*)memchr(dp
, *cast(ubyte*)&val
, len
);
393 if (np
is null) break;
394 uint ofs
= cast(uint)(np
-dp
);
398 if (memcmp(dp
, &val
, T
.sizeof
) == 0) savea(start
, vt
);
408 // ////////////////////////////////////////////////////////////////////////// //
409 void doNewScanBytes(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
410 foundAddrs
[vidx
].clear
;
412 auto atp
= AttachedProc(preypid
);
413 foreach (const ref reg
; memregs
) {
414 scanRegion(atp
, val
, reg
.start
, reg
.end
, (uint addr
, ubyte vt
) { foundAddrs
.ptr
[vidx
].addAddress(addr
, vt
); });
416 writeln(foundAddrs
[vidx
].count
, " matches found");
421 void doFilter(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
422 auto atp
= AttachedProc(preypid
);
423 auto reg
= ASRangeD(foundAddrs
[vidx
]); // this clears fa
424 ubyte vt
= ScanType2VT
!T
;
429 if ((va
.vt
&vt
) != 0) {
430 if (atp
.readBytes((&mv
)[0..1], va
.addr
).length
== 1) {
431 if (mv
== val
) foundAddrs
.ptr
[vidx
].addAddress(va
.addr
, vt
);
438 // ////////////////////////////////////////////////////////////////////////// //
439 void listMatches (uint vidx
) {
440 if (foundAddrs
[vidx
].count
== 0) return;
441 auto atp
= AttachedProc(preypid
);
443 foreach (auto va
; ASRange(foundAddrs
[vidx
])) {
445 writef("[%3s] [%s] 0x%08x ", idx
, vtName(va
.vt
), va
.addr
);
446 if (va
.vt
&VType
.IMask
) {
448 if (atp
.readBytes((cast(ubyte*)&v
)[0..maxVTSize(va
.vt
)], va
.addr
).length
== maxVTSize(va
.vt
)) writeln(v
); else writeln("ERROR");
449 } else if (va
.vt
== VType
.Float
) {
451 if (atp
.readBytes((&v
)[0..1], va
.addr
).length
== 1) writeln(v
); else writeln("ERROR");
452 } else if (va
.vt
== VType
.Double
) {
454 if (atp
.readBytes((&v
)[0..1], va
.addr
).length
== 1) writeln(v
); else writeln("ERROR");
462 // ////////////////////////////////////////////////////////////////////////// //
463 void xfilter(T
) (uint vidx
, T val
) if (IsValidScanType
!T
) {
464 auto olen
= foundAddrs
[vidx
].count
;
466 auto xo
= foundAddrs
[vidx
].count
;
468 if (foundAddrs
[vidx
].count
== 0 || foundAddrs
[vidx
].count
== xo
) break;
470 writeln("reduced from ", olen
, " to ", foundAddrs
[vidx
].count
, " matches");
474 // ////////////////////////////////////////////////////////////////////////// //
475 void setVal(T
) (AttachedProc atp
, uint addr
, T val
) if (IsValidScanType
!T
) {
476 atp
.writeBytes((&val
)[0..1], addr
);
480 // ////////////////////////////////////////////////////////////////////////// //
481 T
xto(T
) (const(char)[] s
) if (__traits(isIntegral
, T
) && T
.sizeof
<= 8) {
482 import std
.conv
: to
;
484 if (s
.length
> 0 && s
[0] == '-') { neg = true; s
= s
[1..$]; }
485 else if (s
.length
> 0 && s
[0] == '+') { s
= s
[1..$]; }
486 if (s
.length
== 0) throw new Exception("invalid value");
488 if (s
.length
> 2 && s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X')) {
489 v
= to
!ulong(s
[2..$], 16);
495 if (nv
< 0) throw new Exception("invalid value");
497 if (nv
> 0) throw new Exception("invalid value");
498 static if (T
.sizeof
== 1) { if (nv
< byte.min || nv
> byte.max
) throw new Exception("invalid value"); }
499 else static if (T
.sizeof
== 2) { if (nv
< short.min || nv
> short.max
) throw new Exception("invalid value"); }
500 else static if (T
.sizeof
== 4) { if (nv
< int.min || nv
> int.max
) throw new Exception("invalid value"); }
503 static if (T
.sizeof
== 1) { if (v
> ubyte.max
) throw new Exception("invalid value"); }
504 else static if (T
.sizeof
== 2) { if (v
> ushort.max
) throw new Exception("invalid value"); }
505 else static if (T
.sizeof
== 4) { if (v
> uint.max
) throw new Exception("invalid value"); }
511 // ////////////////////////////////////////////////////////////////////////// //
512 void main (string
[] args
) {
513 rpcRegisterModuleEndpoints
!monox
;
516 if (args
.length
> 1) {
517 import std
.conv
: to
;
519 pid
= to
!uint(args
[1]);
520 } catch (Exception e
) {
521 int p
= findProcessByName(args
[1]);
523 writeln("process '", args
[1], "' not found!");
527 writeln("process '", args
[1], "' has pid ", pid
);
532 uint vidx
= 0; // current workset
533 // last search values and types for all worksets
534 ulong[MaxVals
] lastU64
= 0;
535 float[MaxVals
] lastF32
= 0;
536 double[MaxVals
] lastF64
= 0;
537 ubyte[MaxVals
] lastType
= VType
.Int
;
538 bool[MaxVals
] wasSearched
= false;
540 //TODO: convert this to CTFE generator
541 void doTypedAction(string fnname
) () {
542 if (lastType
[vidx
] == VType
.Float
) mixin(fnname
~"(vidx, lastF32[vidx]);");
543 else if (lastType
[vidx
] == VType
.Double
) mixin(fnname
~"(vidx, lastF64[vidx]);");
544 else if (lastType
[vidx
] == VType
.Byte
) mixin(fnname
~"(vidx, cast(ubyte)lastU64[vidx]);");
545 else if (lastType
[vidx
] == VType
.Word
) mixin(fnname
~"(vidx, cast(ushort)lastU64[vidx]);");
546 else if (lastType
[vidx
] == VType
.Int
) mixin(fnname
~"(vidx, cast(uint)lastU64[vidx]);");
547 else if (lastType
[vidx
] == VType
.Long
) mixin(fnname
~"(vidx, cast(ulong)lastU64[vidx]);");
548 else assert(0, "invalid type!");
551 //TODO: convert this to CTFE generator
553 void doTypedSet (uint addr
, const(char)[] val
) {
554 import std
.conv
: to
;
556 if (val
.length
== 0) throw new Exception("invalid value");
557 if (lastType
[vidx
] == VType
.Float
) {
558 float v
= to
!float(val
);
559 auto atp
= AttachedProc(preypid
);
561 } else if (lastType
[vidx
] == VType
.Double
) {
562 double v
= to
!double(val
);
563 auto atp
= AttachedProc(preypid
);
566 auto atp
= AttachedProc(preypid
);
567 if (lastType
[vidx
] == VType
.Byte
) atp
.setVal
!ubyte(addr
, val
.xto
!ubyte);
568 else if (lastType
[vidx
] == VType
.Word
) atp
.setVal
!ushort(addr
, val
.xto
!ushort);
569 else if (lastType
[vidx
] == VType
.Int
) atp
.setVal
!uint(addr
, val
.xto
!uint);
570 else if (lastType
[vidx
] == VType
.Long
) atp
.setVal
!ulong(addr
, val
.xto
!ulong);
571 else assert(0, "wtf?!");
576 auto res
= readLine(vidx
);
577 if (res
!= EditLine
.Result
.Normal
) break;
578 if (lineedit
.line
.wordCount
== 0) continue;
579 auto cmd
= lineedit
.line
.word(0); // command
580 if (cmd
.length
== 0) continue;
581 if (cmd
== "!!") { injectCode(); continue; }
583 if (cmd
.length
== 1) { lineedit
.beep
; continue; }
584 lineedit
.pushCurrentToHistory();
585 if (!codeInjected
) { writeln("prey is not hunted"); continue; }
587 string
[] list
= injSock
.rpcallany
!(string
[])("injed.listEndpoints");
588 writeln(list
.length
, " endpoints found");
589 foreach (string s
; list
) writeln(" ", s
);
592 if (cmd
== "@asms") {
593 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
594 writeln("=== assemblies ===");
595 foreach (const ref ass
; monoAsms
) {
596 writefln("#%s: %s", ass
.id
, ass
.name
);
601 auto wc
= lineedit
.line
.wordCount
;
602 if (cmd
== "@asmname") {
603 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
604 import core
.stdc
.string
: memcpy
;
605 if (wc
!= 2) { writeln("assembly id only"); continue; }
606 auto arg
= lineedit
.line
.word(1);
607 uint asmid
= uint.max
;
608 foreach (immutable idx
, const ref ass
; monoAsms
) if (ass
.name
== arg
) { asmid
= cast(uint)idx
; break; }
609 if (asmid
== uint.max
) {
610 try asmid
= arg
.xto
!uint; catch (Exception
) { writeln("invalid assembly id"); continue; }
612 if (asmid
< monoAsms
.length
) {
613 writefln("assembly #%s has id 0x%08x and name '%s'", asmid
, monoAsms
[asmid
].id
, monoAsms
[asmid
].name
);
616 foreach (immutable idx
, const ref ass
; monoAsms
) {
617 if (ass
.id
== asmid
) {
619 writefln("assembly #%s has id 0x%08x and name '%s'", idx
, ass
.id
, ass
.name
);
623 if (!found
) writeln("assembly not found");
627 if (cmd
== "@classes") {
628 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
629 import core
.stdc
.string
: memcpy
;
630 if (wc
> 2) { writeln("assembly id only"); continue; }
631 uint asmid
= uint.max
;
633 auto arg
= lineedit
.line
.word(1);
634 foreach (immutable idx
, const ref ass
; monoAsms
) if (ass
.name
== arg
) { asmid
= cast(uint)idx
; break; }
635 if (asmid
== uint.max
) {
636 try asmid
= arg
.xto
!uint; catch (Exception
) { writeln("invalid assembly id"); continue; }
638 if (asmid
< monoAsms
.length
) {
641 foreach (immutable idx
, const ref ass
; monoAsms
) {
642 if (ass
.id
== asmid
) {
644 asmid
= cast(uint)idx
;
648 if (!found
) { writeln("assembly not found"); continue; }
651 foreach (const ref cls
; monoClasses
) {
652 if (asmid
== uint.max || cls
.asmid
== asmid
) {
653 stderr
.writef("0x%08x ", cls
.id
);
654 if (asmid
== uint.max
) {
655 stderr
.writeln("[", cls
.asmname
, "] ", cls
.nspace
, ".", cls
.name
);
657 stdout
.writeln("[", cls
.asmname
, "] ", cls
.nspace
, ".", cls
.name
);
663 if (cmd
== "@fields") {
664 if (!injInfo
.hasMono
) { writeln("prey is colorful"); continue; }
665 if (wc
!= 2) { writeln("class id only"); continue; }
666 auto arg
= lineedit
.line
.word(1);
667 int cid
= findClassByName(arg
);
668 if (cid
< 0) { writeln("class '", arg
, "' not found"); continue; }
669 //injSock.ncSendCommand(ReqCommand.MonoGetClassFields, monoClasses[cid].id);
671 scope(exit
) list
= null;
673 list
= injSock
.rpcall
!getClassFields(monoClasses
[cid
].id
);
674 writeln("=== ", list
, " fields ===");
675 foreach (const ref fld; list
) {
676 writefln("%s %s at 0x%08x (flags=0x%08x)", fld.typename
, fld.name
, fld.ofs
, fld.flags
);
678 } catch (Exception e
) {
679 writeln("ERROR: ", e
.msg
);
684 writeln("invalid remote command");
687 if (cmd
== "!1") { vidx
= 0; continue; }
688 if (cmd
== "!2") { vidx
= 1; continue; }
689 if (cmd
== "!3") { vidx
= 2; continue; }
690 if (cmd
== "!4") { vidx
= 3; continue; }
691 if (cmd
== "!5") { vidx
= 4; continue; }
692 if (cmd
== "!6") { vidx
= 5; continue; }
693 if (cmd
== "!7") { vidx
= 6; continue; }
694 if (cmd
== "!8") { vidx
= 7; continue; }
695 if (cmd
== "!9") { vidx
= 8; continue; }
696 if (cmd
== "q" || cmd
== "quit") break;
698 if (!wasSearched
[vidx
]) {
699 writeln("no previous value to filter; type is ", vtName(lastType
[vidx
]));
701 write("last search(", vtName(lastType
[vidx
]), "): ");
702 if (lastType
[vidx
] == VType
.Float
) writeln(lastF32
[vidx
]);
703 else if (lastType
[vidx
] == VType
.Double
) writeln(lastF64
[vidx
]);
704 else writeln(lastU64
[vidx
]);
709 if (!wasSearched
[vidx
]) {
710 writeln("no previous value to filter");
712 doTypedAction
!"xfilter"();
716 lineedit
.pushCurrentToHistory();
717 if (cmd
== "asbyte") { if (lastType
[vidx
] != VType
.Byte
) { lastType
[vidx
] = VType
.Byte
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
718 if (cmd
== "asshort") { if (lastType
[vidx
] != VType
.Word
) { lastType
[vidx
] = VType
.Word
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
719 if (cmd
== "asint") { if (lastType
[vidx
] != VType
.Int
) { lastType
[vidx
] = VType
.Int
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
720 if (cmd
== "aslong") { if (lastType
[vidx
] != VType
.Long
) { lastType
[vidx
] = VType
.Long
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
721 if (cmd
== "asfloat") { if (lastType
[vidx
] != VType
.Float
) { lastType
[vidx
] = VType
.Float
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
722 if (cmd
== "asdouble") { if (lastType
[vidx
] != VType
.Double
) { lastType
[vidx
] = VType
.Double
; wasSearched
[vidx
] = false; foundAddrs
[vidx
].clear
; } continue; }
723 if (cmd
== "new") { taskNames
[vidx
] = null; foundAddrs
[vidx
].clear
; wasSearched
[vidx
] = false; continue; }
724 if (cmd
== "killall") {
726 foreach (ref fv
; foundAddrs
) fv
.clear();
727 wasSearched
[] = false;
728 lastType
[] = VType
.Int
;
733 if (cmd
== "list" || cmd
== "l" || cmd
== "ls") { listMatches(vidx
); continue; }
735 import std
.conv
: to
;
736 if (lastType
[vidx
] == VType
.Float
) lastF32
[vidx
] = to
!float(cmd
);
737 else if (lastType
[vidx
] == VType
.Double
) lastF64
[vidx
] = to
!double(cmd
);
738 else if (lastType
[vidx
] == VType
.Byte
) lastU64
[vidx
] = xto
!ubyte(cmd
);
739 else if (lastType
[vidx
] == VType
.Word
) lastU64
[vidx
] = xto
!ushort(cmd
);
740 else if (lastType
[vidx
] == VType
.Int
) lastU64
[vidx
] = xto
!uint(cmd
);
741 else if (lastType
[vidx
] == VType
.Long
) lastU64
[vidx
] = xto
!ulong(cmd
);
742 if (wasSearched
[vidx
]) doTypedAction
!"xfilter"(); else doTypedAction
!"doNewScanBytes"();
743 wasSearched
[vidx
] = true;
745 } catch (Exception e
) {}
747 auto wc
= lineedit
.line
.wordCount
;
749 auto arg
= lineedit
.line
.word(1);
751 if (wc
!= 2) throw new Exception("name?");
752 taskNames
[vidx
] = arg
.idup
;
756 import std
.conv
: to
;
757 const(char)[] saddr
, sval
;
758 if (wc
== 2 && foundAddrs
[vidx
].count
== 1) {
762 if (wc
!= 3) throw new Exception("set args fucked");
764 sval
= lineedit
.line
.word(2);
766 if (saddr
.length
> 2 && saddr
[0..2] == "0x") {
767 auto addr
= to
!uint(saddr
[2..$], 16);
768 doTypedSet(addr
, sval
);
769 } else if (saddr
== "*") {
770 auto atp
= AttachedProc(preypid
);
771 foreach (auto va
; ASRange(foundAddrs
[vidx
])) {
772 //FIXME: different types
773 doTypedSet(va
.addr
, sval
);
776 auto idx
= to
!uint(saddr
);
777 if (idx
>= foundAddrs
[vidx
].count
) throw new Exception("invalid value index");
778 auto va
= foundAddrs
[vidx
].at(idx
);
779 auto atp
= AttachedProc(preypid
);
780 doTypedSet(va
.addr
, sval
);
785 import std
.conv
: to
;
786 if (wc
!= 2) throw new Exception("set args fucked");
788 if (saddr
.length
> 2 && saddr
[0..2] == "0x") {
789 auto addr
= to
!uint(saddr
[2..$], 16);
790 foundAddrs
[vidx
].clear
;
791 foundAddrs
[vidx
].addAddress(addr
, VType
.Int
);
793 auto idx
= to
!uint(saddr
);
794 if (idx
>= foundAddrs
[vidx
].count
) throw new Exception("invalid value index");
795 auto va
= foundAddrs
[vidx
].at(idx
);
796 foundAddrs
[vidx
].clear
;
797 foundAddrs
[vidx
].addAddress(va
.addr
, va
.vt
);
802 } catch (Exception e
) {
803 writeln("ERROR: ", e
.msg
);
806 writeln("invalid command");