use mem file where it is possible (at writing too)
[nyatools.git] / nya.d
blob7473b96511f869b179e78cfb1b5187d9e78e3f59
1 import std.concurrency;
3 import iv.strex;
4 import iv.vfs;
5 import iv.vfs.io;
7 import mregs;
8 import ptrace;
11 // ////////////////////////////////////////////////////////////////////////// //
12 string readLine () {
13 import std.stdio : stdin, stdout;
14 for (;;) {
15 stdout.write('>'); stdout.flush();
16 auto res = stdin.readln;
17 if (res.length == 0) return null;
18 if (res[$-1] != '\n') {
19 stdout.writeln("ERROR: command too long!");
20 do {
21 res = stdin.readln;
22 if (res.length == 0) return null;
23 } while (res[$-1] != '\n');
25 return res.xstrip;
30 // ////////////////////////////////////////////////////////////////////////// //
31 __gshared uint preypid;
32 __gshared MemoryRegionInfo[] memregs;
33 __gshared bool scanAlignedOnly = true;
34 __gshared ubyte scanTypes = VType.Int;
35 __gshared const(ubyte)* scanData;
38 void restart (uint newpid) {
39 if (newpid < 1) throw new Exception("invalid pid");
40 preypid = newpid;
41 clearScan();
45 // ////////////////////////////////////////////////////////////////////////// //
46 enum VType : ubyte {
47 Byte = 1<<0, // 8 bits
48 Word = 1<<1, // 16 bits
49 Int = 1<<2, // 32 bits
50 Long = 1<<3, // 64 bits
51 Float = 1<<4, // 32 bits
52 Double = 1<<5, // 64 bits
56 uint maxVTSize (ubyte vt) {
57 if (vt&VType.Double) return 8;
58 if (vt&VType.Long) return 8;
59 if (vt&VType.Float) return 4;
60 if (vt&VType.Int) return 4;
61 if (vt&VType.Word) return 2;
62 return 1;
66 // ////////////////////////////////////////////////////////////////////////// //
67 alias FVRange = FVRangeImpl!false;
68 alias FVRangeD = FVRangeImpl!true;
70 struct FVRangeImpl(bool destructive) {
71 public:
72 static struct FRes { uint addr; ubyte vt; }
74 private:
75 private import core.stdc.stdlib : free;
76 @disable this (this);
77 FoundValues.PoolHead* ph;
78 ubyte* dp;
79 uint addr;
80 ubyte vt;
81 uint len;
82 uint dataLeft;
84 private:
85 void finish () nothrow @nogc {
86 static if (destructive) {
87 import core.stdc.stdlib : free;
88 while (ph !is null) {
89 auto np = ph.next;
90 free(ph);
91 ph = np;
93 } else {
94 ph = null;
98 public:
99 this (ref FoundValues fv) nothrow @nogc { setup(fv); }
101 void setup (ref FoundValues fv) nothrow @nogc {
102 finish();
103 ph = fv.firstPool;
104 len = fv.addrCount;
105 static if (destructive) {
106 fv.firstPool = fv.lastPool = null;
107 fv.poolCount = 0;
108 fv.lastAddr = 0;
109 fv.addrCount = 0;
111 if (len) {
112 ++len; // as popFront will dec it
113 popFront();
117 public nothrow @nogc:
118 ~this () { finish(); }
120 @property bool empty () const pure { pragma(inline, true); return (len == 0); }
121 @property FRes front () const pure { pragma(inline, true); return FRes(addr, vt); }
123 void popFront () {
124 if (len <= 1) { len = 0; return; }
125 --len;
126 while (ph !is null) {
127 if (dp is null || dataLeft < 2) {
128 if (dp !is null) {
129 // free current block, get next one
130 static if (destructive) {
131 import core.stdc.stdlib : free;
132 auto np = ph.next;
133 free(ph);
134 ph = np;
135 } else {
136 ph = ph.next;
138 dp = null;
139 continue;
141 // first entry of new block
142 dataLeft = ph.used;
143 if (dataLeft < 5) { dataLeft = 0; continue; } // out of data
144 dp = ph.data.ptr;
145 addr = *cast(uint*)dp; dp += 4;
146 vt = *dp++;
147 dataLeft -= 5;
148 return;
150 uint ofs = *dp++;
151 if (ofs != 0 && ofs != 255) {
152 // short
153 addr += ofs;
154 vt = *dp++;
155 dataLeft -= 2;
156 } else if (ofs == 0) {
157 // long
158 if (dataLeft < 5) { dataLeft = 0; continue; } // out of data in this chunk
159 ofs = *dp++;
160 ofs |= (*dp++)<<8;
161 ofs |= (*dp++)<<16;
162 addr += ofs;
163 vt = *dp++;
164 dataLeft -= 5;
165 } else {
166 // new address
167 if (dataLeft < 6) { dataLeft = 0; continue; } // out of data in this chunk
168 addr = *dp++;
169 addr |= (*dp++)<<8;
170 addr |= (*dp++)<<16;
171 addr |= (*dp++)<<24;
172 vt = *dp++;
173 dataLeft -= 6;
175 return;
177 len = 0; // alas
182 // ////////////////////////////////////////////////////////////////////////// //
183 struct FoundValues {
184 private:
185 enum PoolSize = 32*1024;
187 align(1) struct PoolHead {
188 align(1):
189 PoolHead* next; // null for last one
190 uint size; // data size, excluding head
191 uint used; // data used
192 // data follows
193 ubyte[0] data;
194 nothrow @trusted @nogc:
195 void putByte (ubyte b) {
196 if (used >= size || size-used < 1) assert(0, "wtf?!");
197 *(data.ptr+(used++)) = b;
201 PoolHead* firstPool;
202 PoolHead* lastPool;
203 uint poolCount;
204 uint lastAddr;
205 uint addrCount;
207 void addPool () {
208 import core.stdc.stdlib : malloc;
209 PoolHead* ph = cast(PoolHead*)malloc(PoolSize);
210 if (ph is null) assert(0, "out of memory");
211 if (lastPool !is null) lastPool.next = ph; else firstPool = ph;
212 lastPool = ph;
213 ph.next = null;
214 ph.size = PoolSize-PoolHead.sizeof;
215 ph.used = 0;
216 ++poolCount;
219 // pool data format:
220 // first 4 bytes: base address
221 // next byte; value type
222 // data loop:
223 // next byte:
224 // !=0: this is byte offset to next address
225 // ==0: 3-byte offset to next address follows
226 // ==255: 4-byte next address follows
227 // next byte: value type
229 public:
230 @disable this (this);
231 ~this () { clear(); }
233 uint count () const pure nothrow @safe @nogc { pragma(inline, true); return addrCount; }
235 void clear () {
236 import core.stdc.stdlib : free;
237 while (firstPool !is null) {
238 auto cp = firstPool;
239 firstPool = cp.next;
240 free(cp);
242 lastPool = null;
243 poolCount = 0;
244 lastAddr = 0;
245 addrCount = 0;
248 void filter (scope bool delegate (uint addr, ubyte vt) nothrow check) {
249 // simply recreate it all; we will free pools as we go, so it should not use more than one additional pool in most cases
250 auto rng = FVRangeD(this);
251 while (!rng.empty) {
252 auto av = rng.front;
253 rng.popFront();
254 if (check(av.addr, av.vt)) addAddress(av.addr, av.vt);
258 void addAddress (uint addr, ubyte vt) {
259 // we can waste up to 4 bytes with this; idc
260 if (lastPool !is null && lastPool.size-lastPool.used >= 6) {
261 if (lastAddr <= addr && addr-lastAddr <= 0xffffff) {
262 uint diff = addr-lastAddr;
263 if (diff == 0 || diff > 0xfe) {
264 // long form
265 lastPool.putByte(0);
266 lastPool.putByte(diff&0xff);
267 lastPool.putByte((diff>>8)&0xff);
268 lastPool.putByte((diff>>16)&0xff);
269 } else {
270 // short form
271 lastPool.putByte(cast(ubyte)diff);
273 } else {
274 // new addr form
275 lastPool.putByte(255);
276 lastPool.putByte(addr&0xff);
277 lastPool.putByte((addr>>8)&0xff);
278 lastPool.putByte((addr>>16)&0xff);
279 lastPool.putByte((addr>>24)&0xff);
281 lastPool.putByte(vt);
282 lastAddr = addr;
283 ++addrCount;
284 return;
286 // new pool
287 addPool();
288 *cast(uint*)(lastPool.data.ptr) = addr;
289 *cast(ubyte*)(lastPool.data.ptr+4) = vt;
290 lastPool.used = 5;
291 lastAddr = addr;
292 ++addrCount;
296 __gshared FoundValues foundValues;
299 // ////////////////////////////////////////////////////////////////////////// //
300 void clearScan () {
301 memregs = null;
302 foundValues.clear;
303 { import core.memory : GC; GC.collect; GC.minimize; }
304 memregs = readMemoryRegions(preypid);
305 uint total = 0;
306 foreach (const ref reg; memregs) total += reg.end-reg.start;
307 writeln(cast(double)total/1024/1024, " megabytes, ", memregs.length, " regons.");
311 // this can't be used in multiple threads anyway
312 void scanRegion (AttachedProc atp, uint start, uint end) {
313 enum BufSize = 1024*1024;
314 __gshared ubyte* buf = null;
315 if (buf is null) {
316 import core.stdc.stdlib : malloc;
317 buf = cast(ubyte*)malloc(BufSize+64);
318 if (buf is null) assert(0, "out of memory");
319 buf[0..BufSize+64] = 0;
321 while (start < end && end-start >= 4) {
322 import core.stdc.string : memchr, memcmp;
323 // load
324 uint left = end-start;
325 if (left > BufSize) left = BufSize;
326 if (left/4 < 1) break;
327 uint bufUsed = (left/4)*4;
328 assert((bufUsed&0x03) == 0);
329 auto rd = atp.readBytes(buf[0..bufUsed], start);
330 // scan
331 left = bufUsed;
332 auto dp = buf;
333 auto curaddr = start;
334 while (left > 0) {
335 auto np = cast(ubyte*)memchr(dp, scanData[0], left);
336 if (np is null) break;
337 curaddr += cast(uint)(np-dp);
338 left -= cast(uint)(np-dp);
339 dp = np;
340 if (!scanAlignedOnly || (curaddr&0x03) == 0) {
341 import core.stdc.string : memcmp;
342 ubyte tfound = 0;
343 if ((scanTypes&VType.Byte) != 0 && memcmp(dp, scanData, 1) == 0) tfound |= VType.Byte;
344 if ((scanTypes&VType.Word) != 0 && left >= 2 && memcmp(dp, scanData, 2) == 0) tfound |= VType.Word;
345 if ((scanTypes&VType.Int) != 0 && left >= 4 && memcmp(dp, scanData, 4) == 0) tfound |= VType.Int;
346 if ((scanTypes&VType.Long) != 0 && left >= 8 && memcmp(dp, scanData, 8) == 0) tfound |= VType.Long;
347 if ((scanTypes&VType.Float) != 0 && left >= 4 && memcmp(dp, scanData, 4) == 0) tfound |= VType.Float;
348 if ((scanTypes&VType.Double) != 0 && left >= 8 && memcmp(dp, scanData, 8) == 0) tfound |= VType.Double;
349 if (tfound) foundValues.addAddress(curaddr, tfound);
351 ++dp;
352 ++curaddr;
353 --left;
355 // advance
356 if (bufUsed < BufSize) break;
357 start += bufUsed-8;
362 // ////////////////////////////////////////////////////////////////////////// //
363 // `data` should be at least 8 bytes
364 void doNewScanBytes (const(ubyte)* data) {
365 if ((scanTypes&0b111111) == 0) assert(0, "wtf?!");
366 scanData = data;
367 scanTypes = VType.Int;
368 scanAlignedOnly = true;
370 clearScan();
371 uint total = 0;
372 foreach (const ref reg; memregs) total += reg.end-reg.start;
373 uint scanned = 0;
375 auto atp = AttachedProc(preypid);
377 void showProgress (bool nl=false) {
378 import core.stdc.stdio : stdout, fprintf, fflush;
379 fprintf(stdout, "\r%3u%% done [%u/%u] (%u matches; %u regions used)", cast(uint)(cast(ulong)scanned*100/total), scanned/1024/1024, total/1024/1024, foundValues.count, foundValues.poolCount);
380 if (nl) fprintf(stdout, "\n"); else fflush(stdout);
383 foreach (const ref reg; memregs) {
384 showProgress();
385 scanRegion(atp, reg.start, reg.end);
386 scanned += reg.end-reg.start;
388 showProgress(true);
392 // ////////////////////////////////////////////////////////////////////////// //
393 void main (string[] args) {
394 uint pid = 0; //30329;
395 if (args.length > 1) {
396 import std.conv : to;
397 pid = to!uint(args[1]);
399 restart(pid);
400 for (;;) {
401 string cmd = readLine;
402 if (cmd is null) break;
403 if (cmd == "q" || cmd == "quit") break;
404 //writeln((cmd is null), ": ", cmd.quote);
405 if (cmd == "new") { clearScan(); continue; }
406 try {
407 import std.conv : to;
408 uint v = to!uint(cmd);
409 if (foundValues.count == 0) {
410 doNewScanBytes(cast(ubyte*)&v);
411 } else {
412 // filter
413 auto oldc = foundValues.count;
414 auto atp = AttachedProc(preypid);
415 foundValues.filter((uint addr, ubyte vt) {
416 try {
417 if (vt&VType.Int) {
418 uint mv = void;
419 if (atp.readBytes((&mv)[0..1], addr).length != 1) return false;
420 return (mv == v);
422 } catch (Exception) {}
423 return false;
425 { import core.stdc.stdio; printf("reduced from %u matches to %u matches\n", oldc, foundValues.count); }
427 continue;
428 } catch (Exception e) {}
429 writeln("invalid command");