Skip navigation.

taviso

linux, programming and security

Posts tagged with "gdb"

Executable Patching with GDB

, ,

Patching code using gdb is no fun at all, if you need to do anything more complicated than just nop'ing a few instructions the standard procedure is to write the code, assemble it, objdump it then use ht or biew or similar to do the patching. You can patch code within gdb, but it's so long winded I'm sure few people bother unless they just need to modify one or two opcodes.

Someone mentioned that gentoo's skel .gdbinit file contained a handy macro `assemble` that would print opcodes to stdout, I normally use gdb vanilla so hadnt tried it before, but after checking it out, it's a great idea.

gdb> nasm_assemble 
Hit Ctrl-D to start, type code to assemble, hit Ctrl-D when done.
It is recommended to start with
        BITS 32
Note that this command uses NASM (Intel syntax) to assemble.
BITS 32
xor eax, eax
rdtsc
xor eax, edx
rol eax, 0x4
 31 c0 0f 31 31 d0 c1 c0 04

Unfortunately it has a number of problems:

  • It's buggy, you have to hit ctrl-d before starting, then again to finish, this is just confusing.
  • You have to enter "BITS 32" before entering any code, if you forget, you have to start again.
  • You can't see which instructions correspond to which bytes in the output.
  • It's not very gdb like, which normally allows you to finish by entering 'end' and uses readline.
  • You still have to patch the code manually using the bytes it outputs.


I rewrote the macro to fix all these issues. Here's an example of it in use.

$ gdb -q
(gdb) file true
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) tb main
Breakpoint 1 at 0x80489b9: file true.c, line 62.
(gdb) r
main (argc=1, argv=0xbfffea64) at true.c:62
(gdb) x/6i $pc
0x80489b9 <main+21>:    mov    eax,DWORD PTR [ebx]
0x80489bb <main+23>:    mov    DWORD PTR [esp],0x6
0x80489c2 <main+30>:    mov    DWORD PTR [esp+4],0x8049e43
0x80489ca <main+38>:    mov    ds:0x804b884,eax
0x80489cf <main+43>:    call   0x80486e0 <setlocale@plt>
0x80489d4 <main+48>:    mov    DWORD PTR [esp],0x8048a79
(gdb) help assemble
Assemble instructions using nasm.
Type a line containing "end" to indicate the end.
If an address is specified, insert instructions at that address.
If no address is specified, assembled instructions are printed to stdout.
(gdb) assemble 
Instructions will be written to stdout.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>inc eax
>end
00000000  31C0              xor eax,eax
00000002  40                inc eax
(gdb) assemble $pc
Instructions will be written to 0x80489b9.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>inc eax
>mov ebx, 42
>int 0x80
>end
(gdb) x/6i $pc
0x80489b9 <main+21>:    xor    eax,eax
0x80489bb <main+23>:    inc    eax
0x80489bc <main+24>:    mov    ebx,0x2a
0x80489c1 <main+29>:    int    0x80
0x80489c3 <main+31>:    inc    esp
0x80489c4 <main+32>:    and    al,0x4
(gdb) c

Program exited with code 052.

This is really great for patching routines on the fly, if a hostile binary is using the classic ptrace(PTRACE_TRACEME, ...) trick to detect debuggers using ptrace(), patching it out is now as simple as:

$ cat test.c 
#include <unistd.h>
#include <sys/ptrace.h>
#include <stdio.h>

int main(int argc, char **argv)
{
   if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1) {
       fprintf(stdout, "debugger present.\n");
       return 1;
   }

   fprintf(stdout, "no debugger present.\n");

   return 0;
}
$ gdb -q
(gdb) file ptrace
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) r
debugger present.

Program exited with code 01.
(gdb) set write
(gdb) file ptrace
(gdb) info functions ptrace
0x080482f4  ptrace@plt
(gdb) assemble 0x080482f4
Instructions will be written to 0x80482f4.
Type instructions, one per line.
End with a line saying just "end".
>xor eax, eax
>ret
>end
(gdb) set write off
(gdb) file ptrace
(gdb) r
no debugger present.

Program exited normally.
(gdb) q

You can use convenience variables (eg registers), symbol names or actual addresses as an argument, and the address is checked for validity before prompting for input.

$ gdb -q
(gdb) assemble 0x12345
Cannot access memory at address 0x12345
(gdb) q


The prompt is supposed to be identical to the multiline prompt gdb uses, it also uses readline so you can use standard readline bindings, and you no longer have to use BITS 32. I'm really pleased with how it worked out, hopefuly somebody else will find it useful. Here's the full code of the macro:

define assemble
 # dont enter routine again if user hits enter
 dont-repeat
 if ($argc)
  if (*$arg0 = *$arg0)
    # check if we have a valid address by dereferencing it,
    # if we havnt, this will cause the routine to exit.
  end
  printf "Instructions will be written to %#x.\n", $arg0
 else
  printf "Instructions will be written to stdout.\n"
 end
 printf "Type instructions, one per line.\n"
 printf "End with a line saying just \"end\".\n"
 if ($argc)
  # argument specified, assemble instructions into memory
  # at address specified.
  shell nasm -f bin -o /dev/stdout /dev/stdin \
    <<< "$( echo "BITS 32"; while read -ep '>' r && test "$r" != end; \
                do echo -E "$r"; done )" | hexdump -ve \
        '1/1 "set *((unsigned char *) $arg0 + %#2_ax) = %#02x\n"' \
            > ~/.gdbassemble
  # load the file containing set instructions
  source ~/.gdbassemble
  # all done.
  shell rm -f ~/.gdbassemble
 else
  # no argument, assemble instructions to stdout
  shell nasm -f bin -o /dev/stdout /dev/stdin \
    <<< "$( echo "BITS 32"; while read -ep '>' r && test "$r" != end; \
                do echo -E "$r"; done )" | ndisasm -i -b32 /dev/stdin
 end
end
document assemble
Assemble instructions using nasm.
Type a line containing "end" to indicate the end.
If an address is specified, insert instructions at that address.
If no address is specified, assembled instructions are printed to stdout.
Use the pseudo instruction "org ADDR" to set the base address.
end

December 2009
S M T W T F S
November 2009January 2010
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31