1 import std
.concurrency
;
11 // ////////////////////////////////////////////////////////////////////////// //
13 import std
.stdio
: stdin
, stdout
;
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!");
22 if (res
.length
== 0) return null;
23 } while (res
[$-1] != '\n');
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");
45 // ////////////////////////////////////////////////////////////////////////// //
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;
66 // ////////////////////////////////////////////////////////////////////////// //
67 alias FVRange
= FVRangeImpl
!false;
68 alias FVRangeD
= FVRangeImpl
!true;
70 struct FVRangeImpl(bool destructive
) {
72 static struct FRes
{ uint addr
; ubyte vt
; }
75 private import core
.stdc
.stdlib
: free
;
77 FoundValues
.PoolHead
* ph
;
85 void finish () nothrow @nogc {
86 static if (destructive
) {
87 import core
.stdc
.stdlib
: free
;
99 this (ref FoundValues fv
) nothrow @nogc { setup(fv
); }
101 void setup (ref FoundValues fv
) nothrow @nogc {
105 static if (destructive
) {
106 fv
.firstPool
= fv
.lastPool
= null;
112 ++len
; // as popFront will dec it
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
); }
124 if (len
<= 1) { len
= 0; return; }
126 while (ph
!is null) {
127 if (dp
is null || dataLeft
< 2) {
129 // free current block, get next one
130 static if (destructive
) {
131 import core
.stdc
.stdlib
: free
;
141 // first entry of new block
143 if (dataLeft
< 5) { dataLeft
= 0; continue; } // out of data
145 addr
= *cast(uint*)dp
; dp
+= 4;
151 if (ofs
!= 0 && ofs
!= 255) {
156 } else if (ofs
== 0) {
158 if (dataLeft
< 5) { dataLeft
= 0; continue; } // out of data in this chunk
167 if (dataLeft
< 6) { dataLeft
= 0; continue; } // out of data in this chunk
182 // ////////////////////////////////////////////////////////////////////////// //
185 enum PoolSize
= 32*1024;
187 align(1) struct PoolHead
{
189 PoolHead
* next
; // null for last one
190 uint size
; // data size, excluding head
191 uint used
; // data used
194 nothrow @trusted @nogc:
195 void putByte (ubyte b
) {
196 if (used
>= size || size
-used
< 1) assert(0, "wtf?!");
197 *(data
.ptr
+(used
++)) = b
;
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
;
214 ph
.size
= PoolSize
-PoolHead
.sizeof
;
220 // first 4 bytes: base address
221 // next byte; value type
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
230 @disable this (this);
231 ~this () { clear(); }
233 uint count () const pure nothrow @safe @nogc { pragma(inline
, true); return addrCount
; }
236 import core
.stdc
.stdlib
: free
;
237 while (firstPool
!is null) {
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);
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) {
266 lastPool
.putByte(diff
&0xff);
267 lastPool
.putByte((diff
>>8)&0xff);
268 lastPool
.putByte((diff
>>16)&0xff);
271 lastPool
.putByte(cast(ubyte)diff
);
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
);
288 *cast(uint*)(lastPool
.data
.ptr
) = addr
;
289 *cast(ubyte*)(lastPool
.data
.ptr
+4) = vt
;
296 __gshared FoundValues foundValues
;
299 // ////////////////////////////////////////////////////////////////////////// //
303 { import core
.memory
: GC
; GC
.collect
; GC
.minimize
; }
304 memregs
= readMemoryRegions(preypid
);
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;
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
;
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
);
333 auto curaddr
= start
;
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
);
340 if (!scanAlignedOnly ||
(curaddr
&0x03) == 0) {
341 import core
.stdc
.string
: memcmp
;
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
);
356 if (bufUsed
< BufSize
) break;
362 // ////////////////////////////////////////////////////////////////////////// //
363 // `data` should be at least 8 bytes
364 void doNewScanBytes (const(ubyte)* data
) {
365 if ((scanTypes
&0b111111) == 0) assert(0, "wtf?!");
367 scanTypes
= VType
.Int
;
368 scanAlignedOnly
= true;
372 foreach (const ref reg
; memregs
) total
+= reg
.end
-reg
.start
;
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
) {
385 scanRegion(atp
, reg
.start
, reg
.end
);
386 scanned
+= reg
.end
-reg
.start
;
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]);
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; }
407 import std
.conv
: to
;
408 uint v
= to
!uint(cmd
);
409 if (foundValues
.count
== 0) {
410 doNewScanBytes(cast(ubyte*)&v
);
413 auto oldc
= foundValues
.count
;
414 auto atp
= AttachedProc(preypid
);
415 foundValues
.filter((uint addr
, ubyte vt
) {
419 if (atp
.readBytes((&mv
)[0..1], addr
).length
!= 1) return false;
422 } catch (Exception
) {}
425 { import core
.stdc
.stdio
; printf("reduced from %u matches to %u matches\n", oldc
, foundValues
.count
); }
428 } catch (Exception e
) {}
429 writeln("invalid command");