Reverse Engineering Assembly Using GDB

Given an executable that has been compiled with debugging information included in the executable (e.g. gcc -g source.c) we can use the gdb debugger on it and view its assembly code in order to see how it works.

Below I show you how to debug a program named bomb1 which asks the user to enter a passcode and based on that value entered, prints either “Defused” or “Boom!”.

All of the commands shown below can be found in the comprehensive documentation for gdb found here.

To run gdb on an executable we pass gdb the name of an executable.  If you are running gdb on the command line you can also use the Text User Interface by supplying the -tui switch.  The Text User Interface uses the curses library to display a window in the terminal.

Below we run gdb with the -tui switch on the executable named bomb1.

$ gdb -tui bomb1

When gdb is running you will see the gdb command prompt from which you can, as the name suggests, issue gdb commands.

(gdb)

To view the assembly code in the window we issue the layout asm command.

(gdb) layout asm

If the executable expects input from stdin (i.e. the keyboard) we can place text in a file and redirect the contents of the file to stdin when we run the executable in gdb.  Similarly we can tell gdb to redirect output intended for stdout (i.e. the console) to a file.

Below I redirect text in the file named input1.txt to stdin (using < input.txt) and redirect output from stdout into a file named output1.txt (using > output.txt).

(gdb) run < input1.txt > output1.txt

If we inspect output.txt we will see all of the output that the executable typically prints to the screen.

Typically, we will want to step through the assembly instructions.  If we run the executable without setting breakpoints, like we did above, the executable will not stop.  If we want to step through the assembly instructions starting at the beginning of main, we can set a breakpoint at main and then issue the gdb run command.

(gdb) break main
(gdb) run < input1.txt > output1.txt

Throughout this debugging process, if you ever need to restart back up at main, simply issue the stop command and then the start command.  The start command will reissue the previous run command.

(gdb) stop
(gdb) start

Before we step around in the assembly code, let’s observer something.  We know the program asks us for input, reads in the input and then either prints “Defused” or “Boom!” If you look at the callq instructions in the assembly code, you can see the structure of the program.  The calls to puts print to stdout and scanf reads from stdin.

Before we continue, let’s look at a few commands that enable us to view the values that are stored in RAM and in registers.

To print the value stored in a register we use the p command followed by $ and the name of the register.  For example to print the contents of the register %eax we use the following command.

(gdb) p $eax
$1 = 4195741

Since we are in TUI mode we can view the values in all registers in a window using the following command:

(gdb) layout regs

To examine the value at a memory address we can use the x command.  By default, the value is displayed in hexadecimal.  To change the display format we include a forward slash / and a character.  For example, to display the value at memory address 0x400692 as a string we can use the following command.

(gdb) x/s 0x400692
0x400692:        "Defused"

Now we’re ready to walk through the assembly code.  To step to the next assembly instruction we can use the ni command.

(gdb) ni

After the call to scanf we see that a value is moved into %eax.  This is the value that is scanned in from the input file.  The lower byte of the %eax register, namely %al, is compared to 0x24 (decimal 36) and if they are not equal then a conditional jump to 0x4005d9 is performed.  Notice that 0x4005d9 is address of the mov instruction right before a puts call.  The mov instruction moves the address 0x40069a into %edi which is used by puts.  If you examine the value at 0x40069a and format it as a string you will see the address holds the string “Boom!”

(gdb) x/s 0x40069a
0x40069a:        "Boom!"

So if the value of the single byte in %a1 is not equal to decimal value 36 the program prints “Boom!”.  What type in the c programming language uses a single byte? A character.  So what is the character with the decimal value 36?  That’s the input that will diffuse the bomb!