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
;
23 static assert((void*).sizeof
== 4);
26 // ////////////////////////////////////////////////////////////////////////// //
28 int findProcessByName (const(char)[] procname
) {
29 import core
.sys
.posix
.dirent
;
30 static assert(dirent
.d_name
.offsetof
== 19);
31 import core
.sys
.posix
.unistd
: readlink
;
32 import core
.stdc
.stdio
: snprintf
;
34 import std
.string
: fromStringz
;
36 if (procname
.length
== 0) return -1;
38 char[128] tbuf
= void;
39 char[1024] ebuf
= void;
41 auto dir
= opendir("/proc/");
42 if (dir
is null) return -1;
43 scope(exit
) closedir(dir
);
46 auto de = readdir(dir
);
47 if (de is null) break;
48 if (de.d_type
!= DT_DIR
) continue;
53 pid
= de.d_name
.ptr
.fromStringz
.to
!int;
54 } catch (Exception
) { pid
= 0; }
56 if (pid
> 1 && pid
<= int.max
) {
57 snprintf(tbuf
.ptr
, tbuf
.length
, "/proc/%s/exe", de.d_name
.ptr
);
59 auto len
= readlink(tbuf
.ptr
, ebuf
.ptr
, ebuf
.length
);
60 if (len
> 0 && len
< ebuf
.length
) {
61 //{ import core.stdc.stdio : printf; ebuf[len] = 0; printf("pid: %u; exe: [%s]\n", pid, ebuf.ptr); }
62 if (ebuf
[0..len
] == procname
) return pid
;
63 int pos
= cast(int)len
;
64 while (pos
> 0 && ebuf
[pos
-1] != '/') --pos
;
65 if (ebuf
[pos
..len
] == procname
) return pid
;
74 // ////////////////////////////////////////////////////////////////////////// //
75 public align(1) struct MemoryRegionInfo
{
82 // ////////////////////////////////////////////////////////////////////////// //
83 public MemoryRegionInfo
[] readMemoryRegions (uint pid
, bool includeStack
=false) {
84 import std
.format
: format
;
86 static byte hexDigit (char ch
) pure nothrow @safe @nogc {
89 ch
>= '0' && ch
<= '9' ?
cast(byte)(ch
-'0') :
90 ch
>= 'A' && ch
<= 'F' ?
cast(byte)(ch
-'A'+10) :
91 ch
>= 'a' && ch
<= 'f' ?
cast(byte)(ch
-'a'+10) :
95 static uint parseHex(T
: const(char)[]) (ref T s
) {
96 while (s
.length
> 0 && s
.ptr
[0] <= ' ') s
= s
[1..$];
97 if (s
.length
== 0 ||
hexDigit(s
.ptr
[0]) < 0) throw new Exception("hex number expected");
100 auto d
= hexDigit(s
.ptr
[0]);
103 if (nr
< res
) throw new Exception("hex overflow");
110 static void consume(T
: const(char)[]) (ref T s
, char ch
) {
111 if (s
.length
== 0 && s
.ptr
[0] != ch
) throw new Exception("'"~ch
~"' expected");
115 static void skipSpaces(T
: const(char)[]) (ref T s
) {
116 if (s
.length
== 0 && s
.ptr
[0] > ' ') throw new Exception("space expected");
117 while (s
.length
> 0 && s
.ptr
[0] <= ' ') s
= s
[1..$];
120 MemoryRegionInfo
[] res
;
121 foreach (char[] ln
; VFile("/proc/%s/maps".format(pid
)).byLine
) {
125 //stderr.writeln("*** [", ln, "]");
126 start
= parseHex(ln
);
130 if (start
>= end
) continue;
131 // skip regions with less than 16 bytes of data
132 if (end
-start
< 16) continue;
133 if (ln
.length
< 5) throw new Exception("invalid region description");
134 // skip non-writeable, {executable} and shared regions
135 if (ln
.ptr
[0] != 'r' || ln
.ptr
[1] != 'w' /*|| ln.ptr[2] == 'x'*/ || ln
.ptr
[3] != 'p') continue;
138 // if offset is not 0, it is mmaped region, skip it
139 if (parseHex(ln
) != 0) continue;
140 // if devicehi or devilelo is not 0, it is mmaped region, skip it
142 if (parseHex(ln
) != 0) continue;
144 if (parseHex(ln
) != 0) continue;
145 // if inode is not 0, it is mmaped region, skip it
147 if (parseHex(ln
) != 0) continue;
152 while (name
.length
&& name
[$-1] <= ' ') name
= name
[0..$-1];
154 } catch (Exception
) {
155 // can't parse this region, skip it
158 if (name
.length
> 0 && name
.ptr
[0] == '[') {
159 // specials; allow only "heap"
160 //writefln("**** %08X:%08X <%s>", start, end, name);
161 if (name
.length
< 6 || name
[0..5] != "[heap") {
162 if (!includeStack
) continue;
163 if (name
.length
< 6 || name
[0..6] != "[stack") continue;
166 //stderr.writefln("%08X:%08X <%s>", start, end, name);
167 res
~= MemoryRegionInfo(start
, end
, name
);
173 // ////////////////////////////////////////////////////////////////////////// //
175 * search the target process' /proc/pid/maps entry and find an executable
176 * region of memory that we can use to run code in.
177 * returns zero if nothing was found.
179 public uint findXSpaceInPrey (int pid
) {
180 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, snprintf
, fgets
, sscanf
;
181 import core
.stdc
.string
: strstr
;
188 snprintf(filename
.ptr
, filename
.length
, "/proc/%u/maps", cast(uint)pid
);
189 fp
= fopen(filename
.ptr
, "r");
190 if (fp
is null) return 0;
191 while (fgets(line
.ptr
, 850, fp
) !is null) {
192 sscanf(line
.ptr
, "%lx-%lx %s %*s %*s %*d", &addr
, &eaddr
, perms
.ptr
);
193 if (strstr(perms
.ptr
, "x") !is null && eaddr
> addr
&& eaddr
-addr
>= 128) { found
= true; break; }
196 return (found ? addr
: 0);
201 * gets the base address of libc.so inside a process by reading /proc/pid/maps.
202 * returns 0 if nothing was found.
204 private uint findLibcInPrey (int pid
) {
205 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, snprintf
, fgets
, sscanf
;
206 import core
.stdc
.string
: strstr
;
214 snprintf(filename
.ptr
, filename
.length
, "/proc/%d/maps", pid
);
215 fp
= fopen(filename
.ptr
, "r");
216 if (fp
is null) return 0;
217 while (fgets(line
.ptr
, 850, fp
) !is null) {
218 import std
.string
: fromStringz
;
219 auto ln
= line
.ptr
.fromStringz
;
220 while (ln
.length
&& ln
[$-1] <= ' ') ln
= ln
[0..$-1];
221 if (ln
.length
== 0 || ln
.ptr
[0] <= ' ') continue;
223 foreach (immutable _
; 0..5) {
224 while (ln
.length
> 0 && ln
.ptr
[0] > ' ') ln
= ln
[1..$];
225 while (ln
.length
> 0 && ln
.ptr
[0] <= ' ') ln
= ln
[1..$];
227 if (ln
.length
== 0) continue;
228 int pos
= cast(int)ln
.length
;
229 while (pos
> 0 && ln
.ptr
[pos
-1] != '/') --pos
;
231 if (ln
.length
< 8 || ln
[0..5] != "libc-") continue;
235 while (pos
< ln
.length
) {
236 if (ln
.ptr
[pos
] >= '0' && ln
.ptr
[pos
] <= '9') { ++pos
; continue; }
237 if (ln
.ptr
[pos
] != '.') break;
238 if (ln
.length
-pos
> 1 && ln
.ptr
[pos
+1] >= '0' && ln
.ptr
[pos
+1] <= '9') { pos
+= 2; continue; }
241 //{ import std.stdio; stderr.writeln("[", ln, "] - [", ln[pos..$], "]"); }
242 if (pos
>= ln
.length || ln
.ptr
[pos
] != '.' || ln
.length
-pos
< 3 || ln
[pos
..pos
+3] != ".so") continue;
244 //{ import std.stdio; stderr.writeln("*[", ln, "]"); }
245 if (ln
.length
!= 0) {
246 // it should be dots and digits
247 if (ln
.ptr
[0] != '.') continue;
249 while (pos
< ln
.length
) {
250 if (ln
.ptr
[pos
] >= '0' && ln
.ptr
[pos
] <= '9') { ++pos
; continue; }
251 if (ln
.ptr
[pos
] != '.') break;
252 if (ln
.length
-pos
> 1 && ln
.ptr
[pos
+1] >= '0' && ln
.ptr
[pos
+1] <= '9') { pos
+= 2; continue; }
255 if (pos
< ln
.length
) continue;
258 sscanf(line
.ptr
, "%lx-%*lx %s %*s %*s %*d", &addr
, perms
.ptr
);
259 if (strstr(perms
.ptr
, "x") !is null) { found
= true; break; }
262 //{ import core.stdc.stdio : printf; printf("%u: 0x%08x: %d\n", pid, addr, (found ? 1 : 0)); }
263 return (found ? addr
: 0);
267 // ////////////////////////////////////////////////////////////////////////// //
268 struct AttachedProc
{
275 @disable this (this);
281 void decRef () nothrow @nogc {
283 auto n
= cast(Nfo
*)nfop
;
285 import core
.stdc
.stdlib
: free
;
287 import core
.sys
.posix
.unistd
: close
;
290 // 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
291 ptrace(PtraceRequest
.DETACH
, n
.pid
, 1, 0); // == 0
299 this (uint apid
) { attachTo(apid
); }
300 this (this) nothrow @trusted @nogc { pragma(inline
, true); if (nfop
) (cast(Nfo
*)nfop
).rc
+= 1; }
301 ~this () nothrow @nogc { pragma(inline
, true); if (nfop
) decRef(); }
303 void close () nothrow @nogc { pragma(inline
, true); if (nfop
) decRef(); }
305 void attachTo (uint apid
) {
306 import core
.stdc
.stdlib
: malloc
, free
;
307 import core
.stdc
.string
: memset
;
308 import core
.sys
.posix
.sys
.wait : waitpid
, WIFSTOPPED
;
311 if (apid
== 0 || apid
== 1) throw new Exception("attach failed");
312 // attach, to the target application, which should cause a SIGSTOP
313 if (ptrace(PtraceRequest
.ATTACH
, apid
, null, null) == -1) throw new Exception("attach failed");
314 // detach on failure, just in case
315 scope(failure
) ptrace(PtraceRequest
.DETACH
, apid
, 1, 0); // == 0
316 // wait for the SIGSTOP to take place
317 if (waitpid(apid
, &status
, 0) == -1 ||
!WIFSTOPPED(status
)) throw new Exception("error waiting target to stop");
318 auto n
= cast(Nfo
*)malloc(Nfo
.sizeof
);
319 if (n
is null) throw new Exception("out of memory"); // hm...
320 memset(n
, 0, Nfo
.sizeof
);
327 void opAssign() (in auto ref AttachedProc o
) nothrow @nogc {
328 // increase other's rc, and then decrease ours; doing in in another order is unsafe
329 if (o
.nfop
) (cast(Nfo
*)o
.nfop
).rc
+= 1;
334 @property bool valid () const nothrow @safe @nogc { pragma(inline
, true); return (nfop
!= 0); }
336 private bool openFD () nothrow @nogc {
337 if (!nfop
) return false;
338 auto n
= cast(Nfo
*)nfop
;
340 if (n
.memfd
== -666) {
341 import core
.stdc
.stdio
: snprintf
;
342 import core
.sys
.posix
.fcntl
: open
, O_RDONLY
, O_RDWR
;
344 snprintf(name
.ptr
, name
.length
-1, "/proc/%d/mem", pid
);
345 n
.memfd
= open(name
.ptr
, O_RDWR
);
348 n
.memfd
= open(name
.ptr
, O_RDONLY
);
349 if (n
.memfd
< 0) n
.memfd
= -1;
354 return (n
.memfd
>= 0);
357 T
[] readBytes(T
) (T
[] buf
, uint addr
) {
358 import core
.sys
.posix
.unistd
: pread
;
359 if (!nfop
) throw new Exception("can't read from unintialized attach");
360 if (buf
.length
> int.max
/2) throw new Exception("too many elements in read buffer");
361 if (cast(long)buf
.length
*T
.sizeof
> int.max
/2) throw new Exception("too many elements in read buffer");
362 if (buf
.length
== 0) return buf
;
363 auto rd
= (0x1_0000_0000L-addr
);
364 if (rd
< T
.sizeof
) throw new Exception("read error");
365 if (rd
> buf
.length
*T
.sizeof
) rd
= buf
.length
*T
.sizeof
;
366 auto n
= cast(Nfo
*)nfop
;
368 version(pt_use_mem
) {
369 if (!openFD
) throw new Exception("can't open process memory");
371 auto len
= pread(n
.memfd
, buf
.ptr
, cast(uint)(rd
*T
.sizeof
), addr
);
372 if (len
< T
.sizeof
) throw new Exception("read error");
373 return buf
[0..len
/T
.sizeof
];
375 import core
.stdc
.errno
;
376 import core
.stdc
.string
: memcpy
;
377 auto dp
= cast(uint*)buf
.ptr
;
380 foreach (immutable _
; 0..rd
/4) {
381 uint res
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
382 if (res
== -1 && errno
!= 0) throw new Exception("read error");
386 //if (ptrace(PtraceRequest.POKEDATA, pid, addr, res) != 0) throw new Exception("writing error");
389 uint res
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
390 if (res
== -1 && errno
!= 0) throw new Exception("read error");
391 memcpy(dp
, &res
, cast(uint)(rd
%4));
394 if (len
< T
.sizeof
) throw new Exception("read error");
395 return buf
[0..len
/T
.sizeof
];
399 void writeBytes (const(void)[] buf
, uint addr
) {
400 import core
.sys
.posix
.unistd
: pwrite
;
401 if (!nfop
) throw new Exception("can't write to unintialized attach");
402 if (buf
.length
== 0) return;
403 auto n
= cast(Nfo
*)nfop
;
405 if (!openFD
) throw new Exception("can't open process memory");
406 if (!n
.fdwr
) throw new Exception("can't open process memory for writing");
407 long wr
= 0x1_0000_0000L-addr
;
408 if (wr
< buf
.length
) throw new Exception("write error");
409 auto len
= pwrite(n
.memfd
, buf
.ptr
, cast(uint)buf
.length
, addr
);
410 if (len
!= buf
.length
) throw new Exception("write error");
415 // ////////////////////////////////////////////////////////////////////////// //
416 public T
[] procReadMem(T
) (uint pid
, T
[] buf
, uint addr
) {
417 if (pid
<= 2) return null; //throw new Exception("read error");
418 if (buf
.length
== 0) return buf
[];
421 local.iov_base = cast(void*)buf.ptr;
422 local.iov_len = buf.length;
423 remote.iov_base = cast(void*)addr;
424 remote.iov_len = buf.length;
425 auto len = process_vm_readv(pid, &local, 1, &remote, 1, 0);
426 if (len < T.sizeof) return null; //throw new Exception("read error");
427 return buf[0..len/T.sizeof];
429 import core
.stdc
.errno
;
430 auto bp
= cast(ubyte*)buf
.ptr
;
431 auto left
= buf
.length
*T
.sizeof
;
435 import core
.stdc
.string
: memcpy
;
436 auto ov
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
437 if (ov
== -1 && errno
!= 0) {
438 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "reading failed at 0x%08x (%u bytes left)\n", addr, cast(uint)left); }
441 if (left
>= 4) memcpy(bp
, &ov
, 4); else memcpy(bp
, &ov
, left
);
442 //{ import core.stdc.stdio : printf; printf("writing 0x%08x at 0x%08x (%u bytes left)\n", v, addr, cast(uint)left); }
443 if (left
<= 4) { total
+= left
; left
= 0; break; }
449 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "procReadMem complete (%u bytes)\n", total); }
450 return buf
[0..total
/T
.sizeof
];
454 public bool procWriteMem (uint pid
, const(void)[] buf
, uint addr
) {
455 if (pid
<= 2) return false; //throw new Exception("write error");
456 if (buf
.length
== 0) return true;
459 local.iov_base = cast(void*)buf.ptr;
460 local.iov_len = buf.length;
461 remote.iov_base = cast(void*)addr;
462 remote.iov_len = buf.length;
463 auto len = process_vm_writev(pid, &local, 1, &remote, 1, 0);
464 if (len != buf.length) return false; //throw new Exception("write error");
467 auto bp
= cast(const(ubyte)*)buf
.ptr
;
468 auto left
= buf
.length
;
470 import core
.stdc
.string
: memcpy
;
473 import core
.stdc
.errno
;
475 auto ov
= ptrace(PtraceRequest
.PEEKDATA
, pid
, addr
, null);
476 if (ov
== -1 && errno
!= 0) return false;
480 //{ import core.stdc.stdio : stderr, fprintf; fprintf(stderr, "writing 0x%08x at 0x%08x (%u bytes left)\n", v, addr, cast(uint)left); }
481 if (ptrace(PtraceRequest
.POKEDATA
, pid
, addr
, v
) != 0) return false;
482 if (left
<= 4) break;
487 //{ import core.stdc.stdio : stderr, fprintf; fprintf("procWriteMem complete\n"); }
492 // ////////////////////////////////////////////////////////////////////////// //
494 // some code was taken from "linux-inject" project: https://github.com/gaffe23/linux-inject
496 import iv
.olly
.disasm2
;
499 extern(C
) nothrow @nogc {
500 enum RTLD_LAZY
= 0x00001; // POSIX
501 void* __libc_dlopen_mode (const(char)* name
, int mode
);
502 void* __libc_dlsym (void* soh
, const(char)* name
);
503 int __libc_dlclose (void* soh
);
508 * injectSharedLibrary()
510 * This is the code that will actually be injected into the target process.
511 * This code is responsible for loading the shared library into the target
512 * process' address space. First, it calls malloc() to allocate a buffer to
513 * hold the filename of the library to be loaded. Then, it calls
514 * __libc_dlopen_mode(), libc's implementation of dlopen(), to load the desired
515 * shared library. Finally, it calls free() to free the buffer containing the
516 * library name. Each time it needs to give control back to the injector
517 * process, it breaks back in by executing an "int $3" instruction. See the
518 * comments below for more details on how this works.
521 private void injectSharedLibrary (/*uint mallocaddr, uint freeaddr, uint dlopenaddr*/) nothrow @trusted @nogc {
522 // here are the assumptions I'm making about what data will be located
523 // where at the time the target executes this code:
524 asm nothrow @trusted @nogc {
526 // choose the amount of memory to allocate with malloc() based on the size
527 // of the path to the shared library passed via ecx
531 // break back in so that the injector can get the return value
534 // call __libc_dlopen_mode() to load the shared library
535 // 2nd argument to __libc_dlopen_mode(): flag = RTLD_LAZY
537 // 1st argument to __libc_dlopen_mode(): filename = the buffer we allocated earlier
539 // call __libc_dlopen_mode()
541 // break back in so that the injector can check the return value
544 // call free() on the previously malloc'd buffer
545 // 1st argument to free(): ptr = the buffer we allocated earlier
551 ret; // end marker; do not remove, do not add another "ret"
556 private uint islLength () {
558 auto code
= cast(const(ubyte)*)&injectSharedLibrary
;
559 uint addr
= cast(uint)&injectSharedLibrary
;
562 auto len
= disasm(code
[0..64], addr
, &da, /*DA_DUMP|DA_TEXT|DA_HILITE*/0, null);
563 if (len
== 0) return 0;
564 if (da.size
== 1 && code
[0] == 0xc3) return da.ip
+1-staddr
;
574 * Given a process ID and the name of a shared library, check whether that
575 * process has loaded the shared library by reading entries in its
576 * /proc/[pid]/maps file.
579 * - pid_t pid: the pid of the process to check
580 * - char* libname: the library to search /proc/[pid]/maps for
583 * - an int indicating whether or not the library has been loaded into the
584 * process (1 = yes, 0 = no)
587 private bool checkloaded (int pid
, const(char)[] libname
) {
588 import core
.stdc
.stdio
: FILE
, fopen
, fclose
, snprintf
, fgets
, sscanf
;
589 import core
.stdc
.string
: strstr
;
592 char[850] line
, ln2
= 0;
595 if (libname
.length
== 0 || libname
.length
> ln2
.length
-1) return false;
596 ln2
[0..libname
.length
] = libname
[];
597 snprintf(filename
.ptr
, filename
.length
, "/proc/%d/maps", pid
);
598 fp
= fopen(filename
.ptr
, "r");
599 if (fp
is null) return 0;
600 while (fgets(line
.ptr
, 850, fp
) !is null) {
601 if (strstr(line
.ptr
, ln2
.ptr
) !is null) {
612 * getFunctionAddress()
614 * Find the address of a function within our own loaded copy of libc.so.
617 * - char* funcName: name of the function whose address we want to find
620 * - a long containing the address of that function
623 private uint getFunctionAddress (const(char)[] funcname
) {
624 //import core.sys.linux.dlfcn;
625 if (funcname
.length
== 0 || funcname
.length
> 127) return false;
627 buf
[0..funcname
.length
] = funcname
[];
628 void* self
= __libc_dlopen_mode("libc.so.6", RTLD_LAZY
);
629 void* funcAddr
= __libc_dlsym(self
, buf
.ptr
);
630 return cast(uint)funcAddr
;
634 // ////////////////////////////////////////////////////////////////////////// //
635 public bool injectSO (int pid
, const(char)[] libname
) {
636 import core
.sys
.posix
.unistd
: getpid
;
637 import core
.stdc
.string
: memset
;
639 if (pid
<= 1 || libname
.length
== 0 || libname
.length
> 1023) return false;
641 auto mypid
= getpid();
642 auto mylibcaddr
= findLibcInPrey(mypid
);
643 if (mylibcaddr
== 0) {
644 { import core
.stdc
.stdio
: printf
; printf("no libc found in our process\n"); }
648 // find the addresses of the syscalls that we'd like to use inside the
649 // target, as loaded inside THIS process (i.e. NOT the target process)
650 auto mallocAddr
= getFunctionAddress("malloc");
651 if (mallocAddr
== 0) {
652 { import core
.stdc
.stdio
: printf
; printf("no malloc found in our process\n"); }
655 auto freeAddr
= getFunctionAddress("free");
657 { import core
.stdc
.stdio
: printf
; printf("no free found in our process\n"); }
660 auto dlopenAddr
= getFunctionAddress("__libc_dlopen_mode");
661 if (dlopenAddr
== 0) {
662 { import core
.stdc
.stdio
: printf
; printf("no dlopen found in our process\n"); }
666 // use the base address of libc to calculate offsets for the syscalls we want to use
667 uint mallocOffset
= mallocAddr
-mylibcaddr
;
668 uint freeOffset
= freeAddr
-mylibcaddr
;
669 uint dlopenOffset
= dlopenAddr
-mylibcaddr
;
671 // get the target process' libc address and use it to find the
672 // addresses of the syscalls we want to use inside the target process
673 uint targetLibcAddr
= findLibcInPrey(pid
);
674 if (targetLibcAddr
== 0) return false;
675 uint targetMallocAddr
= targetLibcAddr
+mallocOffset
;
676 uint targetFreeAddr
= targetLibcAddr
+freeOffset
;
677 uint targetDlopenAddr
= targetLibcAddr
+dlopenOffset
;
679 // find a good address to copy code to
680 uint ijAddr
= findXSpaceInPrey(pid
);
682 { import core
.stdc
.stdio
: printf
; printf("can't find free space to paste our code\n"); }
685 ijAddr
+= uint.sizeof
;
686 //{ import core.stdc.stdio : printf; printf("[0x%08x]\n", addr); }
688 ubyte[128] bkdata
= 0;
691 uint ijcSize
= islLength();
692 if (ijcSize
== 0 || ijcSize
>= bkdata
.length
-1) {
693 { import core
.stdc
.stdio
: printf
; printf("disasm fucked\n"); }
697 user_regs_struct oldregs
, regs
;
698 memset(&oldregs
, 0, user_regs_struct
.sizeof
);
699 memset(®s
, 0, user_regs_struct
.sizeof
);
701 static bool attachTo (uint apid
) {
702 import core
.stdc
.stdlib
: malloc
, free
;
703 import core
.stdc
.string
: memset
;
704 import core
.sys
.posix
.sys
.wait : waitpid
, WIFSTOPPED
;
706 // attach, to the target application, which should cause a SIGSTOP
707 if (ptrace(PtraceRequest
.ATTACH
, apid
, null, null) == -1) return false;
708 // detach on failure, just in case
709 // wait for the SIGSTOP to take place
710 if (waitpid(apid
, &status
, 0) == -1 ||
!WIFSTOPPED(status
)) {
711 ptrace(PtraceRequest
.DETACH
, apid
, 1, 0);
717 static bool singleStep (uint apid
) {
718 import core
.stdc
.stdlib
: malloc
, free
;
719 import core
.stdc
.string
: memset
;
720 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
722 if (ptrace(PtraceRequest
.SINGLESTEP
, apid
, null, null) == -1) return false;
723 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) return false;
727 static bool singleStepUntilInt3 (uint apid
, uint saddr
, uint len
, user_regs_struct
* regsp
) {
728 import core
.stdc
.stdlib
: malloc
, free
;
729 import core
.stdc
.string
: memset
;
730 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
731 user_regs_struct regs
;
732 if (regsp
is null) regsp
= ®s
;
735 if (ptrace(PtraceRequest
.SINGLESTEP
, apid
, null, null) == -1) return false;
736 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) return false;
737 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) return false;
738 ubyte[MAXCMDSIZE
] ib
;
739 procReadMem(apid
, ib
[], regsp
.eip
);
741 import core
.stdc
.stdio
: stderr
, fprintf
;
742 //fprintf(stderr, "EIP=0x%08x (%02X)\n", regsp.eip, ib);
745 cfg
.tabarguments
= true;
746 auto dclen
= disasm(ib
[], regsp
.eip
, &da, DA_DUMP|DA_TEXT|DA_HILITE
, &cfg
);
748 fprintf(stderr
, "EIP=0x%08x FUCK!\n", regsp
.eip
);
750 import std
.stdio
: stderr
;
751 stderr
.writefln("0x%08x: %-16s %s", da.ip
, da.dumpstr
, da.resstr
);
754 if (ib
[0] == 0xcc) break;
759 static bool int3Step (uint apid
, uint saddr
, uint len
, user_regs_struct
* regsp
) {
761 import core
.stdc
.stdlib
: malloc
, free
;
762 import core
.stdc
.string
: memset
;
763 import core
.sys
.posix
.signal
: siginfo_t
, SIGTRAP
;
764 import core
.sys
.posix
.sys
.wait : waitpid
, WIFEXITED
;
765 user_regs_struct regs
;
766 if (regsp
is null) regsp
= ®s
;
769 if (ptrace(PtraceRequest
.CONT
, apid
, null, null) == -1) return false;
770 if (waitpid(apid
, &status
, 0) == -1 ||
WIFEXITED(status
)) return false;
771 // check what signal we received
773 if (ptrace(PtraceRequest
.GETSIGINFO
, apid
, null, &tsig
) == -1) return false;
774 if (tsig
.si_signo
== SIGTRAP
) {
775 memset(regsp
, 0, user_regs_struct
.sizeof
);
776 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) return false;
777 if (regsp
.eip
>= saddr
&& regsp
.eip
<= saddr
+len
) return true;
778 { import core
.stdc
.stdio
: printf
; printf("trap signal at 0x%08x\n", regsp
.eip
); }
779 // not in our function, continue
781 // some other signal, skip it
782 { import core
.stdc
.stdio
: printf
; printf("non-trap signal at 0x%08x (%u)\n", regsp
.eip
, cast(uint)tsig
.si_signo
); }
788 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "========================\n"); }
789 user_regs_struct regs
;
790 if (regsp
is null) regsp
= ®s
;
791 if (ptrace(PtraceRequest
.GETREGS
, apid
, null, regsp
) == -1) return false;
792 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "ecx=0x%08x; edi=0x%08x\n", regsp
.ecx
, regsp
.edi
); }
793 auto res
= singleStepUntilInt3(apid
, saddr
, len
, regsp
);
797 //if (ptrace(PtraceRequest.SETREGS, apid, null, regsp) == -1) return false;
799 { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "res:%u; eax=0x%08x\n", (res ?
1 : 0), regsp
.eax
); }
804 if (!attachTo(pid
)) {
805 { import core
.stdc
.stdio
: printf
; printf("can't attach to target process\n"); }
809 // if prey is somewhere below libc, trace it until it returns
810 // this should prevent deadlocks on multithreaded apps (i hope)
811 while (regs
.eip
>= targetLibcAddr
) {
812 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", regs.eip); }
813 if (!singleStep(pid
)) {
814 { import core
.stdc
.stdio
: printf
; printf("singlestepping failed\n"); }
817 if (ptrace(PtraceRequest
.GETREGS
, pid
, null, ®s
) == -1) {
818 { import core
.stdc
.stdio
: printf
; printf("can't get registers of target process\n"); }
822 //{ import core.stdc.stdio : printf; printf("EIP=0x%08x\n", regs.eip); }
824 if (ptrace(PtraceRequest
.GETREGS
, pid
, null, &oldregs
) == -1) {
825 ptrace(PtraceRequest
.DETACH
, pid
, 1, 0);
826 { import core
.stdc
.stdio
: printf
; printf("can't get registers of target process\n"); }
832 // restore data and registers, and detach
833 if (bkdataUsed
) procWriteMem(pid
, bkdata
[0..bkdataUsed
], ijAddr
);
834 ptrace(PtraceRequest
.SETREGS
, pid
, null, &oldregs
);
835 ptrace(PtraceRequest
.DETACH
, pid
, 1, 0);
836 { import core
.stdc
.stdio
: printf
; printf("prey process restored\n"); }
839 // back up whatever data used to be at the address we want to modify.
840 if (procReadMem(pid
, bkdata
[0..ijcSize
], ijAddr
).length
!= ijcSize
) {
841 { import core
.stdc
.stdio
: printf
; printf("can't read process memory\n"); }
845 auto code
= cast(immutable(ubyte)*)&injectSharedLibrary
;
846 if (!procWriteMem(pid
, code
[0..ijcSize
], ijAddr
)) {
847 { import core
.stdc
.stdio
: printf
; printf("can't write process memory\n"); }
853 if (!procWriteMem(pid, (&int3)[0..1], ijAddr+ijcSize-1)) {
854 { import core.stdc.stdio : printf; printf("can't write process memory\n"); }
858 bkdataUsed
= ijcSize
;
860 user_regs_struct malloc_regs
;
862 // now that we have an address to copy code to, set the target's eip to it
865 // pass arguments to my function injectSharedLibrary() by loading them into the right registers
866 // see comments in injectSharedLibrary() for more details
867 regs
.ecx
= libname
.length
+1; // including zero
868 regs
.edi
= targetMallocAddr
;
869 if (ptrace(PtraceRequest
.SETREGS
, pid
, null, ®s
) == -1) {
870 { import core
.stdc
.stdio
: printf
; printf("can't set registers of target process\n"); }
874 //singleStepUntilInt3(pid, ijAddr, ijcSize, &malloc_regs);
877 // now that the new code is in place, let the target run our injected code
878 if (!int3Step(pid
, ijAddr
, ijcSize
, &malloc_regs
)) {
879 { import core
.stdc
.stdio
: printf
; printf("int3 stepping failed\n"); }
883 // at this point, the target should have run malloc()
884 // check its return value to see if it succeeded, and bail out cleanly if it didn't
885 uint targetBuf
= malloc_regs
.eax
;
886 if (targetBuf
== 0) {
887 { import core
.stdc
.stdio
: printf
; printf("target process failed to allocate memory\n"); }
891 // if we get here, then malloc likely succeeded, so now we need to copy
892 // the path to the shared library we want to inject into the buffer
893 // that the target process just malloc'd; this is needed so that it can
894 // be passed as an argument to __libc_dlopen_mode later on
895 if (!procWriteMem(pid
, libname
[], targetBuf
)) {
896 { import core
.stdc
.stdio
: printf
; printf("writing memory failed\n"); }
901 if (!procWriteMem(pid
, (&zeroByte
)[0..1], targetBuf
+cast(uint)libname
.length
)) {
902 { import core
.stdc
.stdio
: printf
; printf("writing memory failed\n"); }
906 // now call __libc_dlopen_mode() to attempt to load the shared library
908 // pass arguments to my function injectSharedLibrary() by loading them into the right registers
909 // see comments in injectSharedLibrary() for more details
910 malloc_regs
.ecx
= targetBuf
;
911 malloc_regs
.edi
= targetDlopenAddr
;
912 if (ptrace(PtraceRequest
.SETREGS
, pid
, null, &malloc_regs
) == -1) {
913 { import core
.stdc
.stdio
: printf
; printf("can't set registers of target process\n"); }
916 if (!int3Step(pid
, ijAddr
, ijcSize
, &malloc_regs
)) {
917 { import core
.stdc
.stdio
: printf
; printf("int3 stepping failed\n"); }
921 // check out what the registers look like after calling __libc_dlopen_mode
922 uint libAddr
= malloc_regs
.eax
;
924 // if eax is 0 here, then dlopen failed, and we should bail out cleanly
926 { import core
.stdc
.stdio
: printf
; printf("prey dlopen failed\n"); }
930 // now check /proc/pid/maps to see whether injection was successful.
932 if (checkloaded(pid
, libname
)) {
933 { import core
.stdc
.stdio
: printf
; printf("injected ok\n"); }
936 { import core
.stdc
.stdio
: printf
; printf("injecting failed\n"); }
939 // as a courtesy, free the buffer that we allocated inside the target
940 // process. we don't really care whether this succeeds, so don't
941 // bother checking the return value.
943 // pass arguments to my function injectSharedLibrary() by loading them into the right registers
944 // see comments in injectSharedLibrary() for more details
945 malloc_regs
.ecx
= targetBuf
;
946 malloc_regs
.edi
= targetFreeAddr
;
947 if (ptrace(PtraceRequest
.SETREGS
, pid
, null, &malloc_regs
) == -1) {
948 { import core
.stdc
.stdio
: printf
; printf("can't set registers of target process\n"); }
951 int3Step(pid
, ijAddr
, ijcSize
, &malloc_regs
);
952 { import core
.stdc
.stdio
: printf
; printf("injection complete\n"); }
958 // ////////////////////////////////////////////////////////////////////////// //
960 import core
.sys
.posix
.sys
.uio
: iovec
;
961 import core
.sys
.posix
.sys
.types
: pid_t
, ssize_t
;
962 import core
.stdc
.config
: c_ulong
;
963 extern(C
) nothrow @nogc:
965 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
);
966 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
);
969 // Type of the REQUEST argument to `ptrace()`
970 enum PtraceRequest
: int {
971 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
972 PEEKTEXT
= 1, // return the word in the process's text space at address ADDR
973 PEEKDATA
= 2, // return the word in the process's data space at address ADDR
974 PEEKUSER
= 3, // return the word in the process's user area at offset ADDR
975 POKETEXT
= 4, // write the word DATA into the process's text space at address ADDR
976 POKEDATA
= 5, // write the word DATA into the process's data space at address ADDR
977 POKEUSER
= 6, // write the word DATA into the process's user area at offset ADDR
978 CONT
= 7, // continue the process
979 KILL
= 8, // kill the process
980 SINGLESTEP
= 9, // single step the process. This is not supported on all machines
981 GETREGS
= 12, // get all general purpose registers used by a processes. This is not supported on all machines
982 SETREGS
= 13, // set all general purpose registers used by a processes. This is not supported on all machines
983 GETFPREGS
= 14, // get all floating point registers used by a processes. This is not supported on all machines
984 SETFPREGS
= 15, // set all floating point registers used by a processes. This is not supported on all machines
985 ATTACH
= 16, // attach to a process that is already running
986 DETACH
= 17, // detach from a process attached to with ATTACH
987 GETFPXREGS
= 18, // get all extended floating point registers used by a processes. This is not supported on all machines
988 SETFPXREGS
= 19, // set all extended floating point registers used by a processes. This is not supported on all machines
989 SYSCALL
= 24, // continue and stop at the next (return from) syscall
990 SETOPTIONS
= 0x4200, // set ptrace filter options
991 GETEVENTMSG
= 0x4201, // get last ptrace message
992 GETSIGINFO
= 0x4202, // get siginfo for process
993 SETSIGINFO
= 0x4203, // set new siginfo for process
994 GETREGSET
= 0x4204, // get register content
995 SETREGSET
= 0x4205, // set register content
996 SEIZE
= 0x4206, // like ATTACH, but do not force tracee to trap and do not affect signal or group stop state
997 INTERRUPT
= 0x4207, // trap seized tracee
998 LISTEN
= 0x4208, // wait for next group event
999 PEEKSIGINFO
= 0x4209,
1000 GETSIGMASK
= 0x420a,
1001 SETSIGMASK
= 0x420b,
1002 SECCOMP_GET_FILTER
= 0x420c,
1007 enum uint PTRACE_SEIZE_DEVEL
= 0x80000000u
;
1009 // options set using SETOPTIONS
1010 alias PtraceSetOptions
= uint;
1011 enum /*PtraceSetOptions*/ : uint {
1012 PTRACE_O_TRACESYSGOOD
= 0x00000001u
,
1013 PTRACE_O_TRACEFORK
= 0x00000002u
,
1014 PTRACE_O_TRACEVFORK
= 0x00000004u
,
1015 PTRACE_O_TRACECLONE
= 0x00000008u
,
1016 PTRACE_O_TRACEEXEC
= 0x00000010u
,
1017 PTRACE_O_TRACEVFORKDONE
= 0x00000020u
,
1018 PTRACE_O_TRACEEXIT
= 0x00000040u
,
1019 PTRACE_O_TRACESECCOMP
= 0x00000080u
,
1020 PTRACE_O_EXITKILL
= 0x00100000u
,
1021 PTRACE_O_SUSPEND_SECCOMP
= 0x00200000u
,
1022 PTRACE_O_MASK
= 0x003000ffu
,
1025 // wait extended result codes for the above trace options
1026 enum PtraceEventCodes
{
1036 // arguments for PEEKSIGINFO
1037 struct PtracePeekSigInfoArgs
{
1038 ulong off
; // From which siginfo to start
1039 uint flags
; // Flags for peeksiginfo
1040 int nr
; // How many siginfos to take
1043 enum uint PTRACE_PEEKSIGINFO_SHARED
= 1u<<0; // read signals from a shared (process wide) queue
1045 /* Perform process tracing functions. REQUEST is one of the values above, and determines the action to be taken.
1046 * For all requests except TRACEME, PID specifies the process to be traced.
1048 * PID and the other arguments described above for the various requests should
1049 * appear (those that are used for the particular request) as:
1050 * pid_t PID, void *ADDR, int DATA, void *ADDR2
1052 int ptrace (PtraceRequest request
, ...);
1055 /* These are the 32-bit x86 structures. */
1056 struct user_fpregs_struct
{
1067 struct user_fpxregs_struct
{
1078 uint[32] st_space
; // 8*16 bytes for each FP-reg = 128 bytes
1079 uint[32] xmm_space
; // 8*16 bytes for each XMM-reg = 128 bytes
1083 struct user_regs_struct
{
1104 user_regs_struct regs
;
1106 user_fpregs_struct i387
;
1114 user_regs_struct
* u_ar0
;
1115 user_fpregs_struct
* u_fpstate
;