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 procutil
is aliced
;
19 import iv
.olly
.assembler
;
24 static assert((void*).sizeof
== 4);
27 // ////////////////////////////////////////////////////////////////////////// //
29 int findProcessByName (const(char)[] procname
) {
30 import core
.sys
.posix
.dirent
;
31 static assert(dirent
.d_name
.offsetof
== 19);
32 import core
.sys
.posix
.unistd
: readlink
;
33 import core
.stdc
.stdio
: snprintf
;
35 import std
.string
: fromStringz
;
37 if (procname
.length
== 0) return -1;
39 char[128] tbuf
= void;
40 char[1024] ebuf
= void;
42 auto dir
= opendir("/proc/");
43 if (dir
is null) return -1;
44 scope(exit
) closedir(dir
);
47 auto de = readdir(dir
);
48 if (de is null) break;
49 if (de.d_type
!= DT_DIR
) continue;
54 pid
= de.d_name
.ptr
.fromStringz
.to
!int;
55 } catch (Exception
) { pid
= 0; }
57 if (pid
> 1 && pid
<= int.max
) {
58 snprintf(tbuf
.ptr
, tbuf
.length
, "/proc/%s/exe", de.d_name
.ptr
);
60 auto len
= readlink(tbuf
.ptr
, ebuf
.ptr
, ebuf
.length
);
61 if (len
> 0 && len
< ebuf
.length
) {
62 //{ import core.stdc.stdio : printf; ebuf[len] = 0; printf("pid: %u; exe: [%s]\n", pid, ebuf.ptr); }
63 if (ebuf
[0..len
] == procname
) return pid
;
64 int pos
= cast(int)len
;
65 while (pos
> 0 && ebuf
[pos
-1] != '/') --pos
;
66 if (ebuf
[pos
..len
] == procname
) return pid
;
67 if (ebuf
[pos
..len
] == "wine-preloader") {
68 // oops, wine app; get name from command line
69 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, fread
;
70 snprintf(tbuf
.ptr
, tbuf
.length
, "/proc/%s/cmdline", de.d_name
.ptr
);
72 auto fl
= fopen(tbuf
.ptr
, "r");
74 scope(exit
) fclose(fl
);
76 len
= fread(line
.ptr
, 1, line
.length
-1, fl
);
79 //{ import core.stdc.stdio; printf("%u: lx=[%s]\n", pid, line.ptr); }
81 while (pos
< len
&& line
.ptr
[pos
]) ++pos
;
82 auto lx
= line
[0..pos
];
83 //{ import std.stdio; writeln("<", lx, ">"); }
84 if (lx
== procname
) return pid
;
85 pos
= cast(int)lx
.length
;
86 while (pos
> 0 && lx
[pos
-1] != '/' && lx
[pos
-1] != '\\') --pos
;
87 if (lx
[pos
..$] == procname
) return pid
;
99 // ////////////////////////////////////////////////////////////////////////// //
100 public align(1) struct MemoryRegionInfo
{
107 // ////////////////////////////////////////////////////////////////////////// //
108 public MemoryRegionInfo
[] readMemoryRegions (uint pid
, bool includeStack
=false) {
109 import std
.format
: format
;
111 static byte hexDigit (char ch
) pure nothrow @safe @nogc {
112 pragma(inline
, true);
114 ch
>= '0' && ch
<= '9' ?
cast(byte)(ch
-'0') :
115 ch
>= 'A' && ch
<= 'F' ?
cast(byte)(ch
-'A'+10) :
116 ch
>= 'a' && ch
<= 'f' ?
cast(byte)(ch
-'a'+10) :
120 static uint parseHex(T
: const(char)[]) (ref T s
) {
121 while (s
.length
> 0 && s
.ptr
[0] <= ' ') s
= s
[1..$];
122 if (s
.length
== 0 ||
hexDigit(s
.ptr
[0]) < 0) throw new Exception("hex number expected");
125 auto d
= hexDigit(s
.ptr
[0]);
128 if (nr
< res
) throw new Exception("hex overflow");
135 static void consume(T
: const(char)[]) (ref T s
, char ch
) {
136 if (s
.length
== 0 && s
.ptr
[0] != ch
) throw new Exception("'"~ch
~"' expected");
140 static void skipSpaces(T
: const(char)[]) (ref T s
) {
141 if (s
.length
== 0 && s
.ptr
[0] > ' ') throw new Exception("space expected");
142 while (s
.length
> 0 && s
.ptr
[0] <= ' ') s
= s
[1..$];
145 MemoryRegionInfo
[] res
;
146 foreach (char[] ln
; VFile("/proc/%s/maps".format(pid
)).byLine
) {
150 //stderr.writeln("*** [", ln, "]");
151 start
= parseHex(ln
);
155 if (start
>= end
) continue;
156 // skip regions with less than 16 bytes of data
157 if (end
-start
< 16) continue;
158 if (ln
.length
< 5) throw new Exception("invalid region description");
159 // skip non-writeable, {executable} and shared regions
160 if (ln
.ptr
[0] != 'r' || ln
.ptr
[1] != 'w' /*|| ln.ptr[2] == 'x'*/ || ln
.ptr
[3] != 'p') continue;
163 // if offset is not 0, it is mmaped region, skip it
164 if (parseHex(ln
) != 0) continue;
165 // if devicehi or devilelo is not 0, it is mmaped region, skip it
167 if (parseHex(ln
) != 0) continue;
169 if (parseHex(ln
) != 0) continue;
170 // if inode is not 0, it is mmaped region, skip it
172 if (parseHex(ln
) != 0) continue;
177 while (name
.length
&& name
[$-1] <= ' ') name
= name
[0..$-1];
179 } catch (Exception
) {
180 // can't parse this region, skip it
183 if (name
.length
> 0 && name
.ptr
[0] == '[') {
184 // specials; allow only "heap"
185 //writefln("**** %08X:%08X <%s>", start, end, name);
186 if (name
.length
< 6 || name
[0..5] != "[heap") {
187 if (!includeStack
) continue;
188 if (name
.length
< 6 || name
[0..6] != "[stack") continue;
191 //stderr.writefln("%08X:%08X <%s>", start, end, name);
192 res
~= MemoryRegionInfo(start
, end
, name
);
198 // ////////////////////////////////////////////////////////////////////////// //
200 * search the target process' /proc/pid/maps entry and find an executable
201 * region of memory that we can use to run code in.
202 * returns zero if nothing was found.
204 public uint findXSpaceInPrey (int pid
) {
205 // try to get "ld-" first
206 if (auto lda
= findLibInPrey(pid
, "ld-")) return lda
;
207 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, snprintf
, fgets
, sscanf
;
208 import core
.stdc
.string
: strstr
;
215 snprintf(filename
.ptr
, filename
.length
, "/proc/%u/maps", cast(uint)pid
);
216 fp
= fopen(filename
.ptr
, "r");
217 if (fp
is null) return 0;
218 while (fgets(line
.ptr
, 850, fp
) !is null) {
219 sscanf(line
.ptr
, "%lx-%lx %s %*s %*s %*d", &addr
, &eaddr
, perms
.ptr
);
220 if (strstr(perms
.ptr
, "x") !is null && eaddr
> addr
&& eaddr
-addr
>= 512) { found
= true; break; }
223 return (found ? addr
: 0);
228 * gets the base address of libc.so inside a process by reading /proc/pid/maps.
229 * returns 0 if nothing was found.
230 * basename is name without version and .so, i.e. "libc-" for libc, for example
232 private uint findLibInPrey (int pid
, const(char)[] basename
) {
233 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, snprintf
, fgets
, sscanf
;
234 import core
.stdc
.string
: strstr
;
241 snprintf(filename
.ptr
, filename
.length
, "/proc/%d/maps", pid
);
242 fp
= fopen(filename
.ptr
, "r");
243 if (fp
is null) return 0;
244 while (fgets(line
.ptr
, 850, fp
) !is null) {
245 import std
.string
: fromStringz
;
246 auto ln
= line
.ptr
.fromStringz
;
247 while (ln
.length
&& ln
[$-1] <= ' ') ln
= ln
[0..$-1];
248 if (ln
.length
== 0 || ln
.ptr
[0] <= ' ') continue;
250 foreach (immutable _
; 0..5) {
251 while (ln
.length
> 0 && ln
.ptr
[0] > ' ') ln
= ln
[1..$];
252 while (ln
.length
> 0 && ln
.ptr
[0] <= ' ') ln
= ln
[1..$];
254 if (ln
.length
== 0) continue;
255 int pos
= cast(int)ln
.length
;
256 while (pos
> 0 && ln
.ptr
[pos
-1] != '/') --pos
;
258 if (ln
.length
< basename
.length
+3 || ln
[0..basename
.length
] != basename
) continue;
259 ln
= ln
[basename
.length
..$];
262 while (pos
< ln
.length
) {
263 if (ln
.ptr
[pos
] >= '0' && ln
.ptr
[pos
] <= '9') { ++pos
; continue; }
264 if (ln
.ptr
[pos
] != '.') break;
265 if (ln
.length
-pos
> 1 && ln
.ptr
[pos
+1] >= '0' && ln
.ptr
[pos
+1] <= '9') { pos
+= 2; continue; }
268 //{ import std.stdio; stderr.writeln("[", ln, "] - [", ln[pos..$], "]"); }
269 if (pos
>= ln
.length || ln
.ptr
[pos
] != '.' || ln
.length
-pos
< 3 || ln
[pos
..pos
+3] != ".so") continue;
271 //{ import std.stdio; stderr.writeln("*[", ln, "]"); }
272 if (ln
.length
!= 0) {
273 // it should be dots and digits
274 if (ln
.ptr
[0] != '.') continue;
276 while (pos
< ln
.length
) {
277 if (ln
.ptr
[pos
] >= '0' && ln
.ptr
[pos
] <= '9') { ++pos
; continue; }
278 if (ln
.ptr
[pos
] != '.') break;
279 if (ln
.length
-pos
> 1 && ln
.ptr
[pos
+1] >= '0' && ln
.ptr
[pos
+1] <= '9') { pos
+= 2; continue; }
282 if (pos
< ln
.length
) continue;
285 sscanf(line
.ptr
, "%lx-%*lx %s %*s %*s %*d", &addr
, perms
.ptr
);
286 if (strstr(perms
.ptr
, "x") !is null) { found
= true; break; }
289 //{ import core.stdc.stdio : printf; printf("%u: 0x%08x: %d\n", pid, addr, (found ? 1 : 0)); }
290 return (found ? addr
: 0);
294 // ////////////////////////////////////////////////////////////////////////// //
295 struct AttachedProc
{
302 @disable this (this);
308 void decRef () nothrow @nogc {
310 auto n
= cast(Nfo
*)nfop
;
312 import core
.stdc
.stdlib
: free
;
314 import core
.sys
.posix
.unistd
: close
;
317 // addr is ignored under Linux, but should be 1 under FreeBSD in order to let the child process continue at what it had been interrupted
318 ptrace(PtraceRequest
.DETACH
, n
.pid
, 1, 0); // == 0
326 this (uint apid
) { attachTo(apid
); }
327 this (this) nothrow @trusted @nogc { pragma(inline
, true); if (nfop
) (cast(Nfo
*)nfop
).rc
+= 1; }
328 ~this () nothrow @nogc { pragma(inline
, true); if (nfop
) decRef(); }
330 void close () nothrow @nogc { pragma(inline
, true); if (nfop
) decRef(); }
332 void attachTo (uint apid
) {
333 import core
.stdc
.stdlib
: malloc
, free
;
334 import core
.stdc
.string
: memset
;
335 import core
.sys
.posix
.sys
.wait : waitpid
, WIFSTOPPED
;
338 if (apid
== 0 || apid
== 1) throw new Exception("attach failed");
339 // attach, to the target application, which should cause a SIGSTOP
340 if (ptrace(PtraceRequest
.ATTACH
, apid
, null, null) == -1) throw new Exception("attach failed");
341 // detach on failure, just in case
342 scope(failure
) ptrace(PtraceRequest
.DETACH
, apid
, 1, 0); // == 0
343 // wait for the SIGSTOP to take place
344 if (waitpid(apid
, &status
, 0) == -1 ||
!WIFSTOPPED(status
)) throw new Exception("error waiting target to stop");
345 auto n
= cast(Nfo
*)malloc(Nfo
.sizeof
);
346 if (n
is null) throw new Exception("out of memory"); // hm...
347 memset(n
, 0, Nfo
.sizeof
);
354 void opAssign() (in auto ref AttachedProc o
) nothrow @nogc {
355 // increase other's rc, and then decrease ours; doing in in another order is unsafe
356 if (o
.nfop
) (cast(Nfo
*)o
.nfop
).rc
+= 1;
361 @property bool valid () const nothrow @safe @nogc { pragma(inline
, true); return (nfop
!= 0); }
363 private bool openFD () nothrow @nogc {
364 if (!nfop
) return false;
365 auto n
= cast(Nfo
*)nfop
;
367 if (n
.memfd
== -666) {
368 import core
.stdc
.stdio
: snprintf
;
369 import core
.sys
.posix
.fcntl
: open
, O_RDONLY
, O_RDWR
;
371 snprintf(name
.ptr
, name
.length
-1, "/proc/%d/mem", pid
);
372 n
.memfd
= open(name
.ptr
, O_RDWR
);
375 n
.memfd
= open(name
.ptr
, O_RDONLY
);
376 if (n
.memfd
< 0) n
.memfd
= -1;
381 return (n
.memfd
>= 0);
384 T
[] readBytes(T
) (T
[] buf
, uint addr
) {
385 import core
.sys
.posix
.unistd
: pread
;
386 if (!nfop
) throw new Exception("can't read from unintialized attach");
387 if (buf
.length
> int.max
/2) throw new Exception("too many elements in read buffer");
388 if (cast(long)buf
.length
*T
.sizeof
> int.max
/2) throw new Exception("too many elements in read buffer");
389 if (buf
.length
== 0) return buf
;
390 auto rd
= (0x1_0000_0000L-addr
);
391 if (rd
< T
.sizeof
) throw new Exception("read error");
392 if (rd
> buf
.length
*T
.sizeof
) rd
= buf
.length
*T
.sizeof
;
393 auto n
= cast(Nfo
*)nfop
;
395 version(pt_use_mem
) {
396 if (!openFD
) throw new Exception("can't open process memory");
398 auto len
= pread(n
.memfd
, buf
.ptr
, cast(uint)(rd
*T
.sizeof
), addr
);
399 if (len
< T
.sizeof
) throw new Exception("read error");
400 return buf
[0..len
/T
.sizeof
];
402 import core
.stdc
.errno
;
403 import core
.stdc
.string
: memcpy
;
404 auto dp
= cast(uint*)buf
.ptr
;
407 foreach (immutable _
; 0..rd
/4) {
408 uint res
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
409 if (res
== -1 && errno
!= 0) throw new Exception("read error");
413 //if (ptrace(PtraceRequest.POKEDATA, pid, addr, res) != 0) throw new Exception("writing error");
416 uint res
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
417 if (res
== -1 && errno
!= 0) throw new Exception("read error");
418 memcpy(dp
, &res
, cast(uint)(rd
%4));
421 if (len
< T
.sizeof
) throw new Exception("read error");
422 return buf
[0..len
/T
.sizeof
];
426 void writeBytes (const(void)[] buf
, uint addr
) {
427 import core
.sys
.posix
.unistd
: pwrite
;
428 if (!nfop
) throw new Exception("can't write to unintialized attach");
429 if (buf
.length
== 0) return;
430 auto n
= cast(Nfo
*)nfop
;
432 if (!openFD
) throw new Exception("can't open process memory");
433 if (!n
.fdwr
) throw new Exception("can't open process memory for writing");
434 long wr
= 0x1_0000_0000L-addr
;
435 if (wr
< buf
.length
) throw new Exception("write error");
436 auto len
= pwrite(n
.memfd
, buf
.ptr
, cast(uint)buf
.length
, addr
);
437 if (len
!= buf
.length
) throw new Exception("write error");
442 // ////////////////////////////////////////////////////////////////////////// //
443 public T
[] procReadMem(T
) (uint pid
, T
[] buf
, uint addr
) {
444 if (pid
<= 2) return null; //throw new Exception("read error");
445 if (buf
.length
== 0) return buf
[];
448 local.iov_base = cast(void*)buf.ptr;
449 local.iov_len = buf.length;
450 remote.iov_base = cast(void*)addr;
451 remote.iov_len = buf.length;
452 auto len = process_vm_readv(pid, &local, 1, &remote, 1, 0);
453 if (len < T.sizeof) return null; //throw new Exception("read error");
454 return buf[0..len/T.sizeof];
456 import core
.stdc
.errno
;
457 auto bp
= cast(ubyte*)buf
.ptr
;
458 auto left
= buf
.length
*T
.sizeof
;
462 import core
.stdc
.string
: memcpy
;
463 auto ov
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
464 if (ov
== -1 && errno
!= 0) {
465 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "reading failed at 0x%08x (%u bytes left)\n", addr, cast(uint)left); }
468 if (left
>= 4) memcpy(bp
, &ov
, 4); else memcpy(bp
, &ov
, left
);
469 //{ import core.stdc.stdio : printf; printf("writing 0x%08x at 0x%08x (%u bytes left)\n", v, addr, cast(uint)left); }
470 if (left
<= 4) { total
+= left
; left
= 0; break; }
476 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "procReadMem complete (%u bytes)\n", total); }
477 return buf
[0..total
/T
.sizeof
];
481 public bool procWriteMem (uint pid
, const(void)[] buf
, uint addr
) {
482 if (pid
<= 2) return false; //throw new Exception("write error");
483 if (buf
.length
== 0) return true;
486 local.iov_base = cast(void*)buf.ptr;
487 local.iov_len = buf.length;
488 remote.iov_base = cast(void*)addr;
489 remote.iov_len = buf.length;
490 auto len = process_vm_writev(pid, &local, 1, &remote, 1, 0);
491 if (len != buf.length) return false; //throw new Exception("write error");
494 auto bp
= cast(const(ubyte)*)buf
.ptr
;
495 auto left
= buf
.length
;
497 import core
.stdc
.string
: memcpy
;
500 import core
.stdc
.errno
;
502 auto ov
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
503 if (ov
== -1 && errno
!= 0) return false;
507 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "writing 0x%08x at 0x%08x (%u bytes left)\n", v, addr, cast(uint)left); }
508 if (ptrace(PtraceRequest
.POKEDATA
, pid
, addr
, v
) != 0) return false;
509 if (left
<= 4) break;
514 //{ import core.stdc.stdio : stderr, fprintf; fprintf("procWriteMem complete\n"); }
519 // ////////////////////////////////////////////////////////////////////////// //
521 // some code was taken from "linux-inject" project: https://github.com/gaffe23/linux-inject
524 import iv
.olly
.disasm2
;
527 extern(C
) nothrow @nogc {
528 enum RTLD_LAZY
= 0x00001; // POSIX
529 void* __libc_dlopen_mode (const(char)* name
, int mode
);
530 void* __libc_dlsym (void* soh
, const(char)* name
);
531 int __libc_dlclose (void* soh
);
535 private uint getFunctionAddress (const(char)[] funcname
) {
536 //import core.sys.linux.dlfcn;
537 if (funcname
.length
== 0 || funcname
.length
> 127) return false;
539 buf
[0..funcname
.length
] = funcname
[];
540 void* self
= __libc_dlopen_mode("libc.so.6", RTLD_LAZY
);
541 void* funcAddr
= __libc_dlsym(self
, buf
.ptr
);
542 return cast(uint)funcAddr
;
546 // ////////////////////////////////////////////////////////////////////////// //
547 public bool injectSO (int pid
, const(char)[] libname
, const(char)[] initarg
) {
548 static immutable string injectCode
= q
{
549 ; call __libc_dlopen_mode() to load the
shared library
550 push 1 ; 2nd argument to
__libc_dlopen_mode(): flag
= RTLD_LAZY
551 push libnameofs
; 1st argument to
__libc_dlopen_mode(): filename
= the buffer we allocated earlier
552 call dlopen
; call __libc_dlopen_mode()
553 add esp
,4*2 ; as it
is cdecl
, we need to
pop arguments
554 ; exit
if library
is not loaded
558 ; call __libc_dlsym() to find the symbol
559 push initnameofs
; 2nd argument to
__libc_dlsym() -- function name
560 push eax
; 1st argument to
__libc_dlsym() -- library handle
561 call dlsym
; call __libc_dlsym()
562 add esp
,4*2 ; as it
is cdecl
, we need to
pop arguments
564 ; call the init
function if it
is there
568 push iniargofs
; argument
569 call eax
; call init
function
570 pop edx
; as it
is cdecl
, we need to
pop arguments (this is smaller than fixing esp
)
574 mov eax
,1 ; success flag
580 static bool attachTo (uint apid
) {
581 import core
.stdc
.stdlib
: malloc
, free
;
582 import core
.stdc
.string
: memset
;
583 import core
.sys
.posix
.sys
.wait : waitpid
, WIFSTOPPED
;
585 // attach, to the target application, which should cause a SIGSTOP
586 if (ptrace(PtraceRequest
.ATTACH
, apid
, null, null) == -1) return false;
587 // detach on failure, just in case
588 // wait for the SIGSTOP to take place
589 if (waitpid(apid
, &status
, 0) == -1 ||
!WIFSTOPPED(status
)) {
590 ptrace(PtraceRequest
.DETACH
, apid
, 1, 0);
596 static bool singleStep (uint apid
) {
597 import core
.stdc
.stdlib
: malloc
, free
;
598 import core
.stdc
.string
: memset
;
599 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
601 if (ptrace(PtraceRequest
.SINGLESTEP
, apid
, null, null) == -1) return false;
602 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) return false;
606 static bool singleStepUntilInt3 (uint apid
, uint saddr
, uint len
, user_regs_struct
* regsp
) {
607 import core
.stdc
.stdlib
: malloc
, free
;
608 import core
.stdc
.string
: memset
;
609 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
610 user_regs_struct regs
;
611 if (regsp
is null) regsp
= ®s
;
615 if (ptrace(PtraceRequest
.SINGLESTEP
, apid
, null, null) == -1) return false;
616 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) return false;
617 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) return false;
618 ubyte[MAXCMDSIZE
] ib
;
619 procReadMem(apid
, ib
[], regsp
.eip
);
621 import core
.stdc
.stdio
: stderr
, fprintf
;
622 //fprintf(stderr, "EIP=0x%08x (%02X)\n", regsp.eip, ib);
625 cfg
.tabarguments
= true;
626 auto dclen
= disasm(ib
[], regsp
.eip
, &da, DA_DUMP|DA_TEXT|DA_HILITE
, &cfg
);
628 fprintf(stderr
, "EIP=0x%08x FUCK!\n", regsp
.eip
);
630 import std
.stdio
: stderr
;
631 //stderr.writefln("0x%08x: %-16s %s", da.ip, da.dumpstr, da.resstr);
633 if (oeip
== regsp
.eip
) {
635 import std
.stdio
: stderr
;
636 stderr
.writefln("0x%08x: %-16s %s", da.ip
, da.dumpstr
, da.resstr
);
642 if (ib
[0] == 0xcc) break;
647 static bool int3Step (string msg
, uint apid
, uint saddr
, uint len
, user_regs_struct
* regsp
) {
649 import core
.stdc
.stdlib
: malloc
, free
;
650 import core
.stdc
.string
: memset
;
651 import core
.sys
.posix
.signal
: siginfo_t
, SIGTRAP
;
652 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
653 user_regs_struct regsxx
;
654 if (regsp
is null) regsp
= ®sxx
;
655 //{ import std.stdio : stderr; stderr.writeln("STEPPING: ", msg); }
658 if (ptrace(PtraceRequest
.CONT
, apid
, null, null) == -1) return false;
659 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) {
660 { import core
.stdc
.stdio
: printf
; printf("XFUCK0\n"); }
663 // check what signal we received
665 if (ptrace(PtraceRequest
.GETSIGINFO
, apid
, null, &tsig
) == -1) {
666 { import core
.stdc
.stdio
: printf
; printf("XFUCK1\n"); }
669 if (tsig
.si_signo
== SIGTRAP
) {
670 memset(regsp
, 0, user_regs_struct
.sizeof
);
671 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) {
672 { import core
.stdc
.stdio
: printf
; printf("XFUCK2\n"); }
675 if (regsp
.eip
>= saddr
&& regsp
.eip
<= saddr
+len
) {
677 if (procReadMem(apid
, (&bt)[0..1], regsp
.eip
).length
== 1 && bt == 0x90) {
678 import core
.stdc
.stdio
: printf
;
679 printf("eip=0x%08x eax=0x%08x ebx=0x%08x ecx=0x%08x edx=0x%08x esi=0x%08x edi=0x%08x\n", regsp
.eip
, regsp
.eax
, regsp
.ebx
, regsp
.ecx
, regsp
.edx
, regsp
.esi
, regsp
.edi
);
682 //{ import core.stdc.stdio : printf; printf("*** %02X 0x%08x\n", bt, regsp.eip); }
683 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", regsp.eip); }
686 { import core
.stdc
.stdio
: printf
; printf("trap signal at 0x%08x\n", regsp
.eip
); }
687 // not in our function, continue
689 // some other signal, skip it
690 { import core
.stdc
.stdio
: printf
; printf("non-trap signal at 0x%08x (%u)\n", regsp
.eip
, cast(uint)tsig
.si_signo
); }
697 Thread
.sleep(500.msecs
);
701 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "========================\n"); }
702 user_regs_struct regs
;
703 if (regsp
is null) regsp
= ®s
;
704 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) return false;
705 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "ecx=0x%08x; edi=0x%08x\n", regsp
.ecx
, regsp
.edi
); }
706 auto res
= singleStepUntilInt3(apid
, saddr
, len
, regsp
);
710 //if (ptrace(PtraceRequest.SETREGS, apid, null, regsp) == -1) return false;
712 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "res:%u; eax=0x%08x\n", (res ?
1 : 0), regsp
.eax
); }
717 import core
.sys
.posix
.unistd
: getpid
;
719 if (pid
<= 1 || libname
.length
== 0 || libname
.length
> 1023) return false;
720 if (initarg
.length
> 1023) return false; // alas
722 auto mypid
= getpid();
723 auto mylibcaddr
= findLibInPrey(mypid
, "libc-");
724 if (mylibcaddr
== 0) {
725 { import core
.stdc
.stdio
: printf
; printf("no libc found in our process (WTF?!)\n"); }
729 // find a good address to copy code to
730 uint ijAddr
= findXSpaceInPrey(pid
);
732 { import core
.stdc
.stdio
: printf
; printf("can't find free space to paste our code\n"); }
735 //{ import core.stdc.stdio : printf; printf("[0x%08x]\n", addr); }
737 // find the addresses of the syscalls that we'd like to use inside the
738 // target, as loaded inside THIS process (i.e. NOT the target process)
739 auto mallocAddr
= getFunctionAddress("malloc");
740 if (mallocAddr
== 0) {
741 { import core
.stdc
.stdio
: printf
; printf("no malloc found in our process\n"); }
744 auto freeAddr
= getFunctionAddress("free");
746 { import core
.stdc
.stdio
: printf
; printf("no free found in our process\n"); }
749 auto dlopenAddr
= getFunctionAddress("__libc_dlopen_mode");
750 if (dlopenAddr
== 0) {
751 { import core
.stdc
.stdio
: printf
; printf("no dlopen found in our process\n"); }
754 auto dlsymAddr
= getFunctionAddress("__libc_dlsym");
755 if (dlopenAddr
== 0) {
756 { import core
.stdc
.stdio
: printf
; printf("no dlsym found in our process\n"); }
760 // use the base address of libc to calculate offsets for the syscalls we want to use
761 uint mallocOffset
= mallocAddr
-mylibcaddr
;
762 uint freeOffset
= freeAddr
-mylibcaddr
;
763 uint dlopenOffset
= dlopenAddr
-mylibcaddr
;
764 uint dlsymOffset
= dlsymAddr
-mylibcaddr
;
766 // get the target process' libc address and use it to find the
767 // addresses of the syscalls we want to use inside the target process
768 uint targetLibcAddr
= findLibInPrey(pid
, "libc-");
769 if (targetLibcAddr
== 0) return false;
774 // build injection code
776 auto ass
= new Assembler(ijAddr
);
777 ass
.addLabel("malloc", targetLibcAddr
+mallocOffset
);
778 ass
.addLabel("free", targetLibcAddr
+freeOffset
);
779 ass
.addLabel("dlopen", targetLibcAddr
+dlopenOffset
);
780 ass
.addLabel("dlsym", targetLibcAddr
+dlsymOffset
);
781 ass
.addLines(injectCode
);
783 void putAsmStr (const(void)[] s
) {
784 import std
.format
: format
;
785 foreach (ubyte b
; cast(const(ubyte[]))s
) {
786 string ln
= " db 0x%02x".format(b
);
787 //{ import iv.vfs.io; writeln("[", ln, "]"); }
790 ass
.addLines(" db 0x00");
794 ass
.addLabelHere("libnameofs");
796 ass
.addLabelHere("initnameofs");
798 ass
.addLabelHere("iniargofs");
801 ijcode
= ass
.getCode();
802 //{ import std.stdio; writeln("assembled to ", ijcode.length, " bytes"); }
803 //ass.disasmCode(ijcode, ass.orgpc);
804 ijcodeUsed
= cast(uint)ijcode
.length
;
806 scope(exit
) ijcode
.destroy
;
808 user_regs_struct oldregs
, regs
;
810 if (!attachTo(pid
)) {
811 { import core
.stdc
.stdio
: printf
; printf("can't attach to target process\n"); }
815 if (ptrace(PtraceRequest
.GETREGS
, pid
, null, &oldregs
) == -1) {
816 ptrace(PtraceRequest
.DETACH
, pid
, 1, 0);
817 { import core
.stdc
.stdio
: printf
; printf("can't get registers of target process\n"); }
821 ubyte[] bkdata
= new ubyte[](ijcode
.length
);
822 bool bkdataUsed
= false;
825 // restore data and registers, and detach
826 if (bkdataUsed
) procWriteMem(pid
, bkdata
[], ijAddr
);
828 ptrace(PtraceRequest
.SETREGS
, pid
, null, &oldregs
);
829 ptrace(PtraceRequest
.DETACH
, pid
, 1, 0);
830 //{ import core.stdc.stdio : printf; printf("prey process restored\n"); }
833 // if prey is somewhere below libc, trace it until it returns
834 // this should prevent deadlocks on multithreaded apps (i hope)
835 while (oldregs
.eip
>= targetLibcAddr
) {
836 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", oldregs.eip); }
837 if (!singleStep(pid
)) {
838 { import core
.stdc
.stdio
: printf
; printf("singlestepping failed\n"); }
841 if (ptrace(PtraceRequest
.GETREGS
, pid
, null, &oldregs
) == -1) {
842 { import core
.stdc
.stdio
: printf
; printf("can't get registers of target process\n"); }
847 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", regs.eip); }
849 // back up whatever data used to be at the address we want to modify.
850 if (procReadMem(pid
, bkdata
[], ijAddr
).length
!= bkdata
.length
) {
851 { import core
.stdc
.stdio
: printf
; printf("can't read process memory\n"); }
856 if (!procWriteMem(pid
, ijcode
[], ijAddr
)) {
857 { import core
.stdc
.stdio
: printf
; printf("can't write process memory\n"); }
861 // execute our injected code
863 if (ptrace(PtraceRequest
.SETREGS
, pid
, null, ®s
) == -1) {
864 { import core
.stdc
.stdio
: printf
; printf("can't set registers of target process\n"); }
867 if (!int3Step("boo", pid
, ijAddr
, ijcodeUsed
, ®s
)) {
868 { import core
.stdc
.stdio
: printf
; printf("int3 stepping failed\n"); }
871 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", regs.eip); }
873 // at this point, the target should has success in eax (!=0)
874 uint targetBuf
= regs
.eax
;
876 { import core
.stdc
.stdio
: printf
; printf("target process failed to initialize\n"); }
884 // ////////////////////////////////////////////////////////////////////////// //
886 import core
.sys
.posix
.sys
.uio
: iovec
;
887 import core
.sys
.posix
.sys
.types
: pid_t
, ssize_t
;
888 import core
.stdc
.config
: c_ulong
;
889 extern(C
) nothrow @nogc:
891 ssize_t
process_vm_readv (pid_t pid
, const(iovec
)* local_iov
, c_ulong liovcnt
, const(iovec
)* remote_iov
, c_ulong riovcnt
, c_ulong flags
);
892 ssize_t
process_vm_writev (pid_t pid
, const(iovec
)* local_iov
, c_ulong liovcnt
, const(iovec
)* remote_iov
, c_ulong riovcnt
, c_ulong flags
);
895 // Type of the REQUEST argument to `ptrace()`
896 enum PtraceRequest
: int {
897 TRACEME
= 0, // indicate that the process making this request should be traced; all signals received by this process can be intercepted by its parent, and its parent can use the other `ptrace' requests
898 PEEKTEXT
= 1, // return the word in the process's text space at address ADDR
899 PEEKDATA
= 2, // return the word in the process's data space at address ADDR
900 PEEKUSER
= 3, // return the word in the process's user area at offset ADDR
901 POKETEXT
= 4, // write the word DATA into the process's text space at address ADDR
902 POKEDATA
= 5, // write the word DATA into the process's data space at address ADDR
903 POKEUSER
= 6, // write the word DATA into the process's user area at offset ADDR
904 CONT
= 7, // continue the process
905 KILL
= 8, // kill the process
906 SINGLESTEP
= 9, // single step the process. This is not supported on all machines
907 GETREGS
= 12, // get all general purpose registers used by a processes. This is not supported on all machines
908 SETREGS
= 13, // set all general purpose registers used by a processes. This is not supported on all machines
909 GETFPREGS
= 14, // get all floating point registers used by a processes. This is not supported on all machines
910 SETFPREGS
= 15, // set all floating point registers used by a processes. This is not supported on all machines
911 ATTACH
= 16, // attach to a process that is already running
912 DETACH
= 17, // detach from a process attached to with ATTACH
913 GETFPXREGS
= 18, // get all extended floating point registers used by a processes. This is not supported on all machines
914 SETFPXREGS
= 19, // set all extended floating point registers used by a processes. This is not supported on all machines
915 SYSCALL
= 24, // continue and stop at the next (return from) syscall
916 SETOPTIONS
= 0x4200, // set ptrace filter options
917 GETEVENTMSG
= 0x4201, // get last ptrace message
918 GETSIGINFO
= 0x4202, // get siginfo for process
919 SETSIGINFO
= 0x4203, // set new siginfo for process
920 GETREGSET
= 0x4204, // get register content
921 SETREGSET
= 0x4205, // set register content
922 SEIZE
= 0x4206, // like ATTACH, but do not force tracee to trap and do not affect signal or group stop state
923 INTERRUPT
= 0x4207, // trap seized tracee
924 LISTEN
= 0x4208, // wait for next group event
925 PEEKSIGINFO
= 0x4209,
928 SECCOMP_GET_FILTER
= 0x420c,
933 enum uint PTRACE_SEIZE_DEVEL
= 0x80000000u
;
935 // options set using SETOPTIONS
936 alias PtraceSetOptions
= uint;
937 enum /*PtraceSetOptions*/ : uint {
938 PTRACE_O_TRACESYSGOOD
= 0x00000001u
,
939 PTRACE_O_TRACEFORK
= 0x00000002u
,
940 PTRACE_O_TRACEVFORK
= 0x00000004u
,
941 PTRACE_O_TRACECLONE
= 0x00000008u
,
942 PTRACE_O_TRACEEXEC
= 0x00000010u
,
943 PTRACE_O_TRACEVFORKDONE
= 0x00000020u
,
944 PTRACE_O_TRACEEXIT
= 0x00000040u
,
945 PTRACE_O_TRACESECCOMP
= 0x00000080u
,
946 PTRACE_O_EXITKILL
= 0x00100000u
,
947 PTRACE_O_SUSPEND_SECCOMP
= 0x00200000u
,
948 PTRACE_O_MASK
= 0x003000ffu
,
951 // wait extended result codes for the above trace options
952 enum PtraceEventCodes
{
962 // arguments for PEEKSIGINFO
963 struct PtracePeekSigInfoArgs
{
964 ulong off
; // From which siginfo to start
965 uint flags
; // Flags for peeksiginfo
966 int nr
; // How many siginfos to take
969 enum uint PTRACE_PEEKSIGINFO_SHARED
= 1u<<0; // read signals from a shared (process wide) queue
971 /* Perform process tracing functions. REQUEST is one of the values above, and determines the action to be taken.
972 * For all requests except TRACEME, PID specifies the process to be traced.
974 * PID and the other arguments described above for the various requests should
975 * appear (those that are used for the particular request) as:
976 * pid_t PID, void *ADDR, int DATA, void *ADDR2
978 int ptrace (PtraceRequest request
, ...);
981 /* These are the 32-bit x86 structures. */
982 struct user_fpregs_struct
{
993 struct user_fpxregs_struct
{
1004 uint[32] st_space
; // 8*16 bytes for each FP-reg = 128 bytes
1005 uint[32] xmm_space
; // 8*16 bytes for each XMM-reg = 128 bytes
1009 struct user_regs_struct
{
1030 user_regs_struct regs
;
1032 user_fpregs_struct i387
;
1040 user_regs_struct
* u_ar0
;
1041 user_fpregs_struct
* u_fpstate
;