more API
[nyatools.git] / nya.d
blob640629a4f05d78f4bc9591dc411d4b0db3ee86fe
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/>.
17 module nya is aliced;
19 import iv.autocomplete : acbuild = autocomplete;
20 import iv.editline;
21 import iv.strex;
22 import iv.vfs;
23 import iv.vfs.io;
24 import iv.ncserial;
26 import addrstore;
27 import procutil;
28 import sockchan;
29 import ncproto;
31 import monox;
34 // ////////////////////////////////////////////////////////////////////////// //
35 string getSOName () {
36 version(rdmd) {
37 return "/home/ketmar/back/D/prj/nyatools/mono/injed.so";
38 } else {
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, "]");
57 string[] lst;
58 if (pfx.length == 0) {
59 foreach (const ref cls; monoClasses) lst ~= cls.fullname;
60 } else {
61 // class names
62 foreach (const ref cls; monoClasses) if (cls.name.startsWith(pfx)) lst ~= cls.name;
63 // namespaces
64 foreach (const ref cls; monoClasses) if (cls.fullnamenoasm.startsWith(pfx)) lst ~= cls.fullnamenoasm;
65 // assemblies
66 foreach (const ref cls; monoClasses) if (cls.fullname != cls.fullnamenoasm && cls.fullname.startsWith(pfx)) lst ~= cls.fullname;
68 // done
69 return lst;
73 // ////////////////////////////////////////////////////////////////////////// //
74 shared static this () {
75 import std.functional : toDelegate;
76 knownACB["@fields"] = toDelegate(&acAtFieldsCB);
80 // ////////////////////////////////////////////////////////////////////////// //
81 class EditLineAC : EditLine {
82 this () { super(); }
84 override void autocomplete () {
85 if (!line.canAutocomplete) { beep; return; }
86 int wc = line.wordCount;
87 if (wc > 2) return;
88 auto acwp = line.acWordPos;
89 auto acnum = line.acWordNum;
90 string[] lst;
91 if (acnum == 0) {
92 // we want autocomplete command
93 lst = acbuild(line.word(0).idup,
94 // mono dissector
95 "@asms",
96 "@asmname",
97 "@classes",
98 "@fields",
100 "asbyte",
101 "asdouble",
102 "asfloat",
103 "asint",
104 "aslong",
105 "asshort",
106 "killall",
107 "list",
108 "name",
109 "new",
110 "only",
111 "quit",
112 "set",
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);
121 } else {
122 beep;
123 return;
125 } else {
126 // unknown (TODO)
127 beep;
128 return;
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; }
135 clearOutput();
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];
160 if (tn.length) {
161 lineedit.prompt = "(%s)[%s] %s>".format(vidx+1, foundAddrs[vidx].count, tn);
162 } else {
163 lineedit.prompt = "(%s)[%s]>".format(vidx+1, foundAddrs[vidx].count);
165 auto res = lineedit.readline();
166 cleanupMemory();
167 return res;
171 // ////////////////////////////////////////////////////////////////////////// //
172 __gshared AssemblyInfo[] monoAsms;
173 __gshared NCClassInfo[] monoClasses;
176 void monoClear () {
177 monoAsms = null;
178 monoClasses = null;
182 // return index or -1
183 int findClassByName (const(char)[] name) {
184 // by id or index
185 try {
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) {}
190 // by name
191 foreach (immutable idx, const ref cls; monoClasses) {
192 if (cls.fullname == name) return cast(int)idx; // exact match
194 int res = -1;
195 foreach (immutable idx, const ref cls; monoClasses) {
196 if (cls.isNameEqu(name)) {
197 if (res != -1) return -1;
198 res = cast(int)idx;
201 return res;
205 void monoScan () {
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;
217 break;
220 // build full 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;
247 injSock.close();
248 nyaSock.close();
249 monoClear();
250 if (newpid < 1) throw new Exception("invalid pid");
251 preypid = newpid;
252 reloadRegions();
253 nyaSockName = "/nya-tools/injected/%s_%s_%s".format(getpid(), newpid, counter++);
254 nyaSock.create(nyaSockName);
255 codeInjected = false;
259 // ////////////////////////////////////////////////////////////////////////// //
260 enum MaxVals = 10;
261 __gshared uint nums;
262 __gshared AddrStore[MaxVals] foundAddrs;
263 __gshared string[MaxVals] taskNames;
266 // ////////////////////////////////////////////////////////////////////////// //
267 // terrible hack!
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;
274 ubyte digsleft = 3;
275 do {
276 if (digsleft == 0) { res.ptr[--pos] = ','; digsleft = 3; }
277 res.ptr[--pos] = cast(char)(l%10+'0');
278 l /= 10;
279 --digsleft;
280 } while (l != 0);
281 return res[pos..$];
285 // ////////////////////////////////////////////////////////////////////////// //
286 void injectCode () {
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;
292 // copy so
293 auto soname = getSOName();
294 if (!soname.exists) {
295 stderr.writeln("ERROR: '", soname, "' not found!");
296 return;
298 string newSOName = "/tmp/_injed_%s_%s_%s.so\0".format(getpid(), preypid, counter++);
299 scope(exit) {
300 import core.sys.posix.unistd : unlink;
301 unlink(newSOName.ptr);
303 try {
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]);
313 fi.close();
314 fo.close();
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!");
322 return;
324 nyaSock.listen();
325 if (!injectSO(preypid, newSOName[0..$-1], nyaSockName)) {
326 writeln("injecting failed");
327 } else {
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 "));
333 codeInjected = true;
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));
351 cleanupMemory();
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;
373 if (buf is null) {
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;
380 uint next = start;
381 if (len > BufSize) {
382 len = BufSize;
383 next += BufSize-T.sizeof;
384 } else {
385 next += len;
387 auto rd = atp.readBytes(buf[0..len], start);
388 if (rd.length != len) assert(0, "wtf?!");
389 auto dp = buf;
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);
395 len -= ofs;
396 start += ofs;
397 dp += ofs;
398 if (memcmp(dp, &val, T.sizeof) == 0) savea(start, vt);
399 --len;
400 ++start;
401 ++dp;
403 start = next;
408 // ////////////////////////////////////////////////////////////////////////// //
409 void doNewScanBytes(T) (uint vidx, T val) if (IsValidScanType!T) {
410 foundAddrs[vidx].clear;
411 reloadRegions();
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");
417 cleanupMemory();
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;
425 T mv = void;
426 while (!reg.empty) {
427 auto va = reg.front;
428 reg.popFront;
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);
442 uint idx = -1;
443 foreach (auto va; ASRange(foundAddrs[vidx])) {
444 ++idx;
445 writef("[%3s] [%s] 0x%08x ", idx, vtName(va.vt), va.addr);
446 if (va.vt&VType.IMask) {
447 ulong v = 0;
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) {
450 float v = 0;
451 if (atp.readBytes((&v)[0..1], va.addr).length == 1) writeln(v); else writeln("ERROR");
452 } else if (va.vt == VType.Double) {
453 double v = 0;
454 if (atp.readBytes((&v)[0..1], va.addr).length == 1) writeln(v); else writeln("ERROR");
455 } else {
456 writeln("ERROR");
462 // ////////////////////////////////////////////////////////////////////////// //
463 void xfilter(T) (uint vidx, T val) if (IsValidScanType!T) {
464 auto olen = foundAddrs[vidx].count;
465 for (;;) {
466 auto xo = foundAddrs[vidx].count;
467 doFilter(vidx, val);
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;
483 bool neg = false;
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");
487 ulong v;
488 if (s.length > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
489 v = to!ulong(s[2..$], 16);
490 } else {
491 v = to!ulong(s, 10);
493 if (neg) {
494 long nv = v;
495 if (nv < 0) throw new Exception("invalid value");
496 nv = -nv;
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"); }
501 return cast(T)nv;
502 } else {
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"); }
506 return cast(T)v;
511 // ////////////////////////////////////////////////////////////////////////// //
512 void main (string[] args) {
513 rpcRegisterModuleEndpoints!monox;
515 uint pid = 0;
516 if (args.length > 1) {
517 import std.conv : to;
518 try {
519 pid = to!uint(args[1]);
520 } catch (Exception e) {
521 int p = findProcessByName(args[1]);
522 if (p < 0) {
523 writeln("process '", args[1], "' not found!");
524 return;
526 pid = p;
527 writeln("process '", args[1], "' has pid ", pid);
530 restart(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
552 // this throws
553 void doTypedSet (uint addr, const(char)[] val) {
554 import std.conv : to;
555 val = val.xstrip;
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);
560 atp.setVal(addr, v);
561 } else if (lastType[vidx] == VType.Double) {
562 double v = to!double(val);
563 auto atp = AttachedProc(preypid);
564 atp.setVal(addr, v);
565 } else {
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?!");
575 for (;;) {
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; }
582 if (cmd[0] == '@') {
583 if (cmd.length == 1) { lineedit.beep; continue; }
584 lineedit.pushCurrentToHistory();
585 if (!codeInjected) { writeln("prey is not hunted"); continue; }
586 if (cmd == "@@") {
587 string[] list = injSock.rpcallany!(string[])("injed.listEndpoints");
588 writeln(list.length, " endpoints found");
589 foreach (string s; list) writeln(" ", s);
590 continue;
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);
598 continue;
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);
614 } else {
615 bool found = false;
616 foreach (immutable idx, const ref ass; monoAsms) {
617 if (ass.id == asmid) {
618 found = true;
619 writefln("assembly #%s has id 0x%08x and name '%s'", idx, ass.id, ass.name);
620 break;
623 if (!found) writeln("assembly not found");
625 continue;
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;
632 if (wc == 2) {
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) {
639 } else {
640 bool found = false;
641 foreach (immutable idx, const ref ass; monoAsms) {
642 if (ass.id == asmid) {
643 found = true;
644 asmid = cast(uint)idx;
645 break;
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);
656 } else {
657 stdout.writeln("[", cls.asmname, "] ", cls.nspace, ".", cls.name);
661 continue;
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);
670 FieldInfo[] list;
671 scope(exit) list = null;
672 try {
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);
681 continue;
684 writeln("invalid remote command");
685 continue;
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;
697 if (cmd == "?") {
698 if (!wasSearched[vidx]) {
699 writeln("no previous value to filter; type is ", vtName(lastType[vidx]));
700 } else {
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]);
706 continue;
708 if (cmd == ".") {
709 if (!wasSearched[vidx]) {
710 writeln("no previous value to filter");
711 } else {
712 doTypedAction!"xfilter"();
714 continue;
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") {
725 taskNames[] = null;
726 foreach (ref fv; foundAddrs) fv.clear();
727 wasSearched[] = false;
728 lastType[] = VType.Int;
729 vidx = 0;
730 restart(pid);
731 continue;
733 if (cmd == "list" || cmd == "l" || cmd == "ls") { listMatches(vidx); continue; }
734 try {
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;
744 continue;
745 } catch (Exception e) {}
746 try {
747 auto wc = lineedit.line.wordCount;
748 if (wc > 1) {
749 auto arg = lineedit.line.word(1);
750 if (cmd == "name") {
751 if (wc != 2) throw new Exception("name?");
752 taskNames[vidx] = arg.idup;
753 continue;
755 if (cmd == "set") {
756 import std.conv : to;
757 const(char)[] saddr, sval;
758 if (wc == 2 && foundAddrs[vidx].count == 1) {
759 saddr = "0";
760 sval = arg;
761 } else {
762 if (wc != 3) throw new Exception("set args fucked");
763 saddr = arg;
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);
775 } else {
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);
782 continue;
784 if (cmd == "only") {
785 import std.conv : to;
786 if (wc != 2) throw new Exception("set args fucked");
787 auto saddr = arg;
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);
792 } else {
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);
799 continue;
802 } catch (Exception e) {
803 writeln("ERROR: ", e.msg);
804 continue;
806 writeln("invalid command");