title: ptrace fails when injecting code into old ELF binary (2005)
body: <p>I have a very old gameserver file for a video-game that became obsolete years ago (more than a decade and a half has passed since then). A lot of clones and reverse-engineered copycats of this engine exist, but nothing will ever be like the real thing, or at least without tons and tons of work (it's a MMORPG server). It's so old that it uses LinuxThreads instead of NPTL, and uses it for its thread-per-connection architecture (up to 1100 simmultaneos connections interacting with a huge 2D tilebased game world).</p>
  <p>I was trying to rewrite it from scratch, since it has ancient GCC debug info available, but had to drop it since the amount of work to do was insane. Now I'm trying to inject new functionality into it, but couldn't proggress any further since I don't really understand how I'm supposed to inject into it.</p>
  <p>I've tried coding a very simple injector using ptrace, but the server dies because of a segmentation fault (SIGSEGV) and since I can't attach GDB, I don't really know where the crash is happening.</p>
  <pre><code>#include &lt;sys/ptrace.h&gt;
  #include &lt;sys/user.h&gt;
  #include &lt;sys/types.h&gt;
  #include &lt;sys/wait.h&gt;
  #include &lt;sys/syscall.h&gt;
  #include &lt;string.h&gt;
  #include &lt;errno.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;stdint.h&gt;
  #include &lt;unistd.h&gt;
  
  #define EMPTY_ADDR 0x80EF000UL
  
  const char *SHELLCODE = &quot;\x90\x90\x90\x90\x90\x90\x90\x90&quot;;
  
  pid_t GamePID;
  
  void GetGamePID() {
      FILE *fp = fopen(&quot;/home/bob/lock.pid&quot;, &quot;rb&quot;);
      if (!fp) {
          printf(&quot;:: [Error] Game not running.\n&quot;);
          return;
      }
  
      fscanf(fp, &quot;%d&quot;, &amp;GamePID);
  }
  
  int main() {
      GetGamePID();
      if (!GamePID)
          return 0;
  
      if (ptrace(PTRACE_ATTACH, GamePID, NULL, NULL)) {
          printf(&quot;:: [Error] Cannot attach.\n&quot;);
          printf(&quot;:: # Fail %d: %s.\n&quot;, errno, strerror(errno));
          return 0;
      }
  
      siginfo_t info;
      waitid(P_PID, GamePID, &amp;info, WSTOPPED);
  
      printf(&quot;:: [Info] Attach on PID %d.\n&quot;, GamePID);
  
      struct user_regs_struct oldregs, regs;
      if (ptrace(PTRACE_GETREGS, GamePID, NULL, &amp;oldregs)) {
          printf(&quot;:: [Error] Cannot save registers (Fail %d).\n&quot;, errno);
          ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
          return 1;
      }
      
      unsigned long save[2];
      save[0] = ptrace(PTRACE_PEEKTEXT, GamePID, (void *)(EMPTY_ADDR), NULL);
      save[1] = ptrace(PTRACE_PEEKTEXT, GamePID, (void *)(EMPTY_ADDR + 8UL), NULL);
  
      size_t payload_size = strlen(SHELLCODE);
      uint64_t *payload = (uint64_t *)SHELLCODE;
  
      printf(&quot;[*] Injecting payload at address 0x%lx.\n&quot;, EMPTY_ADDR);
      for (size_t i = 0; i &lt; payload_size; i += 8, payload++) {
          if (ptrace(PTRACE_POKETEXT, GamePID, EMPTY_ADDR + i, *payload) &lt; 0) {
              printf(&quot;:: [Error] Failed to PTRACE_POKETEXT: %s\n&quot;, strerror(errno));
              return 1;
          }
      }
  
      regs = oldregs;
      regs.rip = EMPTY_ADDR;
      if (ptrace(PTRACE_SETREGS, GamePID, NULL, &amp;regs)) {
          printf(&quot;:: [Error] Cannot modify registers.\n&quot;);
          ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
          return 1;
      }
  
      if (ptrace(PTRACE_CONT, GamePID, NULL, NULL)) {
          printf(&quot;:: [Error] Cannot run shellcode.\n&quot;);
          ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
          return 1;
      }
  
      waitid(P_PID, GamePID, &amp;info, WSTOPPED);
  
      // I used blank r-xp space but just in case...
      if (ptrace(PTRACE_POKETEXT, GamePID, (void *)(EMPTY_ADDR), (void *)save[0]) ||
          ptrace(PTRACE_POKETEXT, GamePID, (void *)(EMPTY_ADDR + 8UL), (void *)save[1])) {
          printf(&quot;:: [Error] Cannot restore origcode.\n&quot;);
          ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
          return 1;
      }
  
      if (ptrace(PTRACE_SETREGS, GamePID, NULL, &amp;oldregs)) {
          printf(&quot;:: [Error] Cannot restore registers.\n&quot;);
          ptrace(PTRACE_DETACH, GamePID, NULL, NULL);
          return 1;
      }
  
      if (ptrace(PTRACE_DETACH, GamePID, NULL, NULL)) {
          printf(&quot;:: [Error] Cannot detach from process.\n&quot;);
          return 1;
      }
  
      puts(&quot;[+] Done.&quot;);
  }
  </code></pre>
  <p>That's how my injector looks like. Strings and whatnot is hardcoded because this is dummy code. Here is /proc/pid/maps output:</p>
  <pre><code>08047000-08048000 rwxp 00000000 08:01 549662                             /home/bob/bin/game.orig
  08048000-08125000 r-xp 00001000 08:01 549662                             /home/bob/bin/game.orig
  08125000-08129000 rwxp 000dd000 08:01 549662                             /home/bob/bin/game.orig
  08129000-0eb45000 rwxp 00000000 00:00 0
  0f3e2000-14f10000 rwxp 00000000 00:00 0                                  [heap]
  c573f000-f7b69000 rwxp 00000000 00:00 0
  f7b6a000-f7b71000 rwxs 00000000 00:01 32821                              /SYSV0000271b (deleted)
  f7b71000-f7b72000 rwxp 00000000 00:00 0
  f7bb5000-f7beb000 rwxp 00000000 00:00 0
  f7bec000-f7d3e000 rwxp 00000000 00:00 0
  f7d3e000-f7e57000 r-xp 00000000 08:01 1041510                            /home/bob/lib/libc.so.6
  f7e57000-f7e59000 r-xp 00119000 08:01 1041510                            /home/bob/lib/libc.so.6
  f7e59000-f7e5b000 rwxp 0011b000 08:01 1041510                            /home/bob/lib/libc.so.6
  f7e5b000-f7e5d000 rwxp 00000000 00:00 0
  f7e5d000-f7e66000 r-xp 00000000 08:01 1041511                            /home/bob/lib/libgcc_s.so.1
  f7e66000-f7e67000 rwxp 00009000 08:01 1041511                            /home/bob/lib/libgcc_s.so.1
  f7e67000-f7e68000 rwxp 00000000 00:00 0
  f7e68000-f7e8c000 r-xp 00000000 08:01 1041512                            /home/bob/lib/libm.so.6
  f7e8c000-f7e8d000 r-xp 00023000 08:01 1041512                            /home/bob/lib/libm.so.6
  f7e8d000-f7e8e000 rwxp 00024000 08:01 1041512                            /home/bob/lib/libm.so.6
  f7e8e000-f7f3d000 r-xp 00000000 08:01 1041514                            /home/bob/lib/libstdc++.so.5
  f7f3d000-f7f42000 rwxp 000ae000 08:01 1041514                            /home/bob/lib/libstdc++.so.5
  f7f42000-f7f47000 rwxp 00000000 00:00 0
  f7f47000-f7f56000 r-xp 00000000 08:01 1041513                            /home/bob/lib/libpthread.so.0
  f7f56000-f7f57000 r-xp 0000e000 08:01 1041513                            /home/bob/lib/libpthread.so.0
  f7f57000-f7f58000 rwxp 0000f000 08:01 1041513                            /home/bob/lib/libpthread.so.0
  f7f58000-f7f9b000 rwxp 00000000 00:00 0
  f7f9b000-f7f9e000 r--p 00000000 00:00 0                                  [vvar]
  f7f9e000-f7f9f000 r-xp 00000000 00:00 0                                  [vdso]
  f7f9f000-f7fb8000 r-xp 00000000 08:01 1041509                            /home/bob/lib/ld-linux.so.2
  f7fb8000-f7fb9000 r-xp 00018000 08:01 1041509                            /home/bob/lib/ld-linux.so.2
  f7fb9000-f7fba000 rwxp 00019000 08:01 1041509                            /home/bob/lib/ld-linux.so.2
  ff000000-ff001000 ---p 00000000 00:00 0
  ff001000-ff200000 rwxp 00000000 00:00 0
  ff200000-ff201000 ---p 00000000 00:00 0
  ff201000-ff400000 rwxp 00000000 00:00 0
  ff400000-ff401000 ---p 00000000 00:00 0
  ff401000-ff600000 rwxp 00000000 00:00 0
  ff600000-ff601000 ---p 00000000 00:00 0
  ff601000-ff800000 rwxp 00000000 00:00 0
  fff4b000-fffa2000 rwxp 00000000 00:00 0                                  [stack]
  </code></pre>
  <p>My server spawns each thread as an independent process, and as you can see I had to use my libraries (/home/bob/lib) copied from and ancient RedHat version which was compatible with the threading model (getpid() returns a different value for each thread).</p>
  <p>I would really like to hear some suggestions on how I can inject new code. Is it just as hacky as writing directly to /proc/pid/mem? How would I override a game function (say, HandlePlayerMove) with /proc/pid/mem? Is it possible to inject a shared object using LD_PRELOAD given that it was compiled against a newer version of libc, libm, etc? Is there a way of knowing what's triggering SIGSEGV without ptrace failing?</p>
  <p>edit: dummy code typo</p>