cosmetix
[nyatools.git] / nya.d
blobd25af5d0dfb6b9b1386431848bb16706dc644fe6
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 "@asmname",
96 "@asms",
97 "@classes",
98 "@fields",
99 "@writeclasses",
101 "asbyte",
102 "asdouble",
103 "asfloat",
104 "asint",
105 "aslong",
106 "asshort",
107 "killall",
108 "list",
109 "name",
110 "new",
111 "only",
112 "quit",
113 "set",
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);
122 } else {
123 beep;
124 return;
126 } else {
127 // unknown (TODO)
128 beep;
129 return;
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; }
136 clearOutput();
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];
161 if (tn.length) {
162 lineedit.prompt = "(%s)[%s] %s>".format(vidx+1, foundAddrs[vidx].count, tn);
163 } else {
164 lineedit.prompt = "(%s)[%s]>".format(vidx+1, foundAddrs[vidx].count);
166 auto res = lineedit.readline();
167 cleanupMemory();
168 return res;
172 // ////////////////////////////////////////////////////////////////////////// //
173 __gshared AssemblyInfo[] monoAsms;
174 __gshared NCClassInfo[] monoClasses;
177 void monoClear () {
178 monoAsms = null;
179 monoClasses = null;
183 // return index or -1
184 int findClassByName (const(char)[] name) {
185 // by id or index
186 try {
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) {}
191 // by name
192 foreach (immutable idx, const ref cls; monoClasses) {
193 if (cls.fullname == name) return cast(int)idx; // exact match
195 int res = -1;
196 foreach (immutable idx, const ref cls; monoClasses) {
197 if (cls.isNameEqu(name)) {
198 if (res != -1) return -1;
199 res = cast(int)idx;
202 return res;
206 void monoScan () {
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;
218 break;
221 // build full 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;
248 injSock.close();
249 nyaSock.close();
250 monoClear();
251 if (newpid < 1) throw new Exception("invalid pid");
252 preypid = newpid;
253 reloadRegions();
254 nyaSockName = "/nya-tools/injected/%s_%s_%s".format(getpid(), newpid, counter++);
255 nyaSock.create(nyaSockName);
256 codeInjected = false;
260 // ////////////////////////////////////////////////////////////////////////// //
261 enum MaxVals = 10;
262 __gshared uint nums;
263 __gshared AddrStore[MaxVals] foundAddrs;
264 __gshared string[MaxVals] taskNames;
267 // ////////////////////////////////////////////////////////////////////////// //
268 // terrible hack!
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;
275 ubyte digsleft = 3;
276 do {
277 if (digsleft == 0) { res.ptr[--pos] = ','; digsleft = 3; }
278 res.ptr[--pos] = cast(char)(l%10+'0');
279 l /= 10;
280 --digsleft;
281 } while (l != 0);
282 return res[pos..$];
286 // ////////////////////////////////////////////////////////////////////////// //
287 void injectCode () {
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;
293 // copy so
294 auto soname = getSOName();
295 if (!soname.exists) {
296 stderr.writeln("ERROR: '", soname, "' not found!");
297 return;
299 string newSOName = "/tmp/_injed_%s_%s_%s.so\0".format(getpid(), preypid, counter++);
300 scope(exit) {
301 import core.sys.posix.unistd : unlink;
302 unlink(newSOName.ptr);
304 try {
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]);
314 fi.close();
315 fo.close();
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!");
323 return;
325 nyaSock.listen();
326 if (!injectSO(preypid, newSOName[0..$-1], nyaSockName)) {
327 writeln("injecting failed");
328 } else {
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 "));
334 codeInjected = true;
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));
352 cleanupMemory();
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;
374 if (buf is null) {
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;
381 uint next = start;
382 if (len > BufSize) {
383 len = BufSize;
384 next += BufSize-T.sizeof;
385 } else {
386 next += len;
388 auto rd = atp.readBytes(buf[0..len], start);
389 if (rd.length != len) assert(0, "wtf?!");
390 auto dp = buf;
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);
396 len -= ofs;
397 start += ofs;
398 dp += ofs;
399 if (memcmp(dp, &val, T.sizeof) == 0) savea(start, vt);
400 --len;
401 ++start;
402 ++dp;
404 start = next;
409 // ////////////////////////////////////////////////////////////////////////// //
410 void doNewScanBytes(T) (uint vidx, T val) if (IsValidScanType!T) {
411 foundAddrs[vidx].clear;
412 reloadRegions();
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");
418 cleanupMemory();
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;
426 T mv = void;
427 while (!reg.empty) {
428 auto va = reg.front;
429 reg.popFront;
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);
443 uint idx = -1;
444 foreach (auto va; ASRange(foundAddrs[vidx])) {
445 ++idx;
446 writef("[%3s] [%s] 0x%08x ", idx, vtName(va.vt), va.addr);
447 if (va.vt&VType.IMask) {
448 ulong v = 0;
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) {
451 float v = 0;
452 if (atp.readBytes((&v)[0..1], va.addr).length == 1) writeln(v); else writeln("ERROR");
453 } else if (va.vt == VType.Double) {
454 double v = 0;
455 if (atp.readBytes((&v)[0..1], va.addr).length == 1) writeln(v); else writeln("ERROR");
456 } else {
457 writeln("ERROR");
463 // ////////////////////////////////////////////////////////////////////////// //
464 void xfilter(T) (uint vidx, T val) if (IsValidScanType!T) {
465 auto olen = foundAddrs[vidx].count;
466 for (;;) {
467 auto xo = foundAddrs[vidx].count;
468 doFilter(vidx, val);
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;
484 bool neg = false;
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");
488 ulong v;
489 if (s.length > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
490 v = to!ulong(s[2..$], 16);
491 } else {
492 v = to!ulong(s, 10);
494 if (neg) {
495 long nv = v;
496 if (nv < 0) throw new Exception("invalid value");
497 nv = -nv;
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"); }
502 return cast(T)nv;
503 } else {
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"); }
507 return cast(T)v;
512 // ////////////////////////////////////////////////////////////////////////// //
513 void main (string[] args) {
514 rpcRegisterModuleEndpoints!monox;
516 uint pid = 0;
517 if (args.length > 1) {
518 import std.conv : to;
519 try {
520 pid = to!uint(args[1]);
521 } catch (Exception e) {
522 int p = findProcessByName(args[1]);
523 if (p < 0) {
524 writeln("process '", args[1], "' not found!");
525 return;
527 pid = p;
528 writeln("process '", args[1], "' has pid ", pid);
531 restart(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
553 // this throws
554 void doTypedSet (uint addr, const(char)[] val) {
555 import std.conv : to;
556 val = val.xstrip;
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);
561 atp.setVal(addr, v);
562 } else if (lastType[vidx] == VType.Double) {
563 double v = to!double(val);
564 auto atp = AttachedProc(preypid);
565 atp.setVal(addr, v);
566 } else {
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?!");
576 for (;;) {
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; }
583 if (cmd[0] == '@') {
584 if (cmd.length == 1) { lineedit.beep; continue; }
585 lineedit.pushCurrentToHistory();
586 if (!codeInjected) { writeln("prey is not hunted"); continue; }
587 if (cmd == "@@") {
588 string[] list = injSock.rpcallany!(string[])("injed.listEndpoints");
589 writeln(list.length, " endpoints found");
590 foreach (string s; list) writeln(" ", s);
591 continue;
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);
599 continue;
602 auto wc = lineedit.line.wordCount;
603 if (cmd == "@writeclasses") {
604 if (wc != 2) { writeln("filename?"); continue; }
605 auto arg = lineedit.line.word(1);
606 try {
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, "'");
612 continue;
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);
626 } else {
627 bool found = false;
628 foreach (immutable idx, const ref ass; monoAsms) {
629 if (ass.id == asmid) {
630 found = true;
631 writefln("assembly #%s has id 0x%08x and name '%s'", idx, ass.id, ass.name);
632 break;
635 if (!found) writeln("assembly not found");
637 continue;
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;
644 if (wc == 2) {
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) {
651 } else {
652 bool found = false;
653 foreach (immutable idx, const ref ass; monoAsms) {
654 if (ass.id == asmid) {
655 found = true;
656 asmid = cast(uint)idx;
657 break;
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);
668 continue;
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);
677 FieldInfo[] list;
678 scope(exit) list = null;
679 try {
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);
688 continue;
691 writeln("invalid remote command");
692 continue;
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;
704 if (cmd == "?") {
705 if (!wasSearched[vidx]) {
706 writeln("no previous value to filter; type is ", vtName(lastType[vidx]));
707 } else {
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]);
713 continue;
715 if (cmd == ".") {
716 if (!wasSearched[vidx]) {
717 writeln("no previous value to filter");
718 } else {
719 doTypedAction!"xfilter"();
721 continue;
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") {
732 taskNames[] = null;
733 foreach (ref fv; foundAddrs) fv.clear();
734 wasSearched[] = false;
735 lastType[] = VType.Int;
736 vidx = 0;
737 restart(pid);
738 continue;
740 if (cmd == "list" || cmd == "l" || cmd == "ls") { listMatches(vidx); continue; }
741 try {
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;
751 continue;
752 } catch (Exception e) {}
753 try {
754 auto wc = lineedit.line.wordCount;
755 if (wc > 1) {
756 auto arg = lineedit.line.word(1);
757 if (cmd == "name") {
758 if (wc != 2) throw new Exception("name?");
759 taskNames[vidx] = arg.idup;
760 continue;
762 if (cmd == "set") {
763 import std.conv : to;
764 const(char)[] saddr, sval;
765 if (wc == 2 && foundAddrs[vidx].count == 1) {
766 saddr = "0";
767 sval = arg;
768 } else {
769 if (wc != 3) throw new Exception("set args fucked");
770 saddr = arg;
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);
782 } else {
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);
789 continue;
791 if (cmd == "only") {
792 import std.conv : to;
793 if (wc != 2) throw new Exception("set args fucked");
794 auto saddr = arg;
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);
799 } else {
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);
806 continue;
809 } catch (Exception e) {
810 writeln("ERROR: ", e.msg);
811 continue;
813 writeln("invalid command");