Examining Data in GDB

The GNU Debugger (GDB) is a powerful tool to debug binary executables. It can be used to do reverse-engineering as well. Let's debug the following code written by LiveOverflow:

All it does is checking the key provided as an argument with the correct key embedded inside the code. We need to compile it with the debug flag:

Supplying our key "MYKEY" as an argument can be done in different ways. Directly:

Or after loading the program by starting it with an argument "MYKEY":

Similarly, but by using "set" operator

So let's print the argument count and the arguments themselves by dereferencing the pointers and specifying the number of characters to be printed by using artificial arrays (@ sign):

The first argument is the executable's file name with full path and the second one is key we supplied, so in total there are two argument and hence "argc" is equal to 2.

It's too easy. We can dig into more low-level. From the "System V Application Binary Interface: AMD64 Architecture Processor Supplement" (Linux AMD64 ABI) we know that the first argument to a C function (we are not talking about BASH) is passed via RDI register, the second is passed via RSI register. So in the main function

integer "argc" will be found in register RDI and array of pointers to characters "argv" will be found in register RSI. What "argv" really means will be clear in a minute.

If we look into the memory map, we will see

Let's examine registers' content using "print" command. Format identifers "d" stands for "decimal", "x" - for hexadecimal, "z" - for hexadecimal with zero extension.

GDB cannot guess type of register RSI, so dereferencing it using "*" gives only a 32-bit address, not 64-bit one:

That's why we need to cast it to pointer to a long integer, which is of 64-bit size:

Now the address is printed correctly. More proper way to do this is to cast RSI register to the native type of "argv":

We can also dump memory directly by giving the address to "examine" ("x") command:

This command examines content of a the memory to which the address supplied points to. So it answers the question "What is stored at address blah blah blah?".  It implies dereferencing of the pointer supplied. Command "print", on contrary, just prints the address and does nothing more. This is a very important difference.

The format specifier "2xg" in "examine" command stands for "repeat twice hexadecimal dumping of a giant word (which is of size 64 bits)". Strings can be displayed by submitting "s" format specifier.

However, completely variable/register name dependent examination of memory is possible:

Here casting new pointer types and dereferencing pointers is exactly the same as in C. Manual dereferencing of pointers without "examine" command and printing the obtained content allows to display only a single character, which is "/" of "/home/johndoe/license_1":

Actually, it can be overcome by using artificial arrays but it looks ugly:

Making it prettier requires submitting a string format specifier:

Instead of "print" we can use the system's "printf" function with well-known format specifiers just like in C:

Variables and memory can be changed while debugging:

Dumping the stack is easy:

Disassembling current function:

Disassembling any part of the code around RIP register using "examine" command:

Here format specifier "i" interprets binary data as an opcode and disassembles it. Overall, GDB is quite suitable for disassembling and examining executables while debugging.

Leave a Reply

Your email address will not be published. Required fields are marked *