The Art of Exploitation book has a nice section on using GDB to inspect memory. We start by disassembling the simple Hello World post.
Start by compiling hello_world.c with the ‘-g’ option.
-g Produce debugging information in the operating system's native format (stabs, COFF , XCOFF , or DWARF 2). GDB can work with this debugging information.
By default, when you disassemble code in GDB, it uses the AT&T format for the assembly instructions. The Art of Exploitation book prefers to use the Intel format. I’m not familiar with either but in case you want to follow along with the book, I’ll use the Intel syntax in my examples.
Since the book was written, GDB has changed the way you set the syntax format so you’ll want to modify it. The book says to do
set dis intel
You’ll want to do
set disassembly-flavor intel
You can create a file in your home directory called .gdbinit so you don’t have to specify this every time you start GDB. You can do all of this in one command
nobody@nobody:~$ echo "set disassembly-flavor intel" > ~/.gdbinit
Now lets fire up GDB. The book uses the ‘-q’ option which is optional
-q ``Quiet''.  Do not print the  introductory  and  copyright  messages.  These messages are also suppressed in batch mode.
nobody@nobody:$ gcc -g hello_world.c
nobody@nobody:$ gdb -q ./a.out 
Reading symbols from a.out...done.
(gdb) 
Now that we have GDB up, lets use it to disassemble the x86_64 code. You can type the full version ‘disassemble main’ or the short version ‘disass main’
(gdb) disass main
Dump of assembler code for function main:
   0x0000000000400524 <+0>:	push   rbp
   0x0000000000400525 <+1>:	mov    rbp,rsp
   0x0000000000400528 <+4>:	mov    edi,0x40062c
   0x000000000040052d <+9>:	call   0x400418 <puts@plt>
   0x0000000000400532 <+14>:	mov    eax,0x0
   0x0000000000400537 <+19>:	leave  
   0x0000000000400538 <+20>:	ret    
End of assembler dump.
If you’re following along in the book, you’ll notice that the output is slightly different. The book uses the older x86 architecture, I’m using the x86_64 architecture. That means all of the 32-bit ‘E’ registers now have their equivalent 64-bit ‘R’ registers. For example, ‘esp’ is the 32-bit version of the “Extended Stack Pointer” and ‘rsp’ is the 64-bit version of the “Register Stack Pointer”.
I’ve highlighted the important part of the code, the body of main. Lets use GDB’s examine memory commands to see exactly what’s going on
GDB uses the ‘x’ command to look at the contents of memory. You can tell GDB what format you’d like to see the memory in and unit you’d like to see it in
Formats:
o - octal
d - decimal
x - hexadecimal
u - unsigned integer
s - string
t - binary
Units:
b - byte
h - half
w - word
g - double word
You can also tell GDB how many you’d like to see by prefixing it’s type with a number. Since we know that “Hello World” is a string, we can use the x/s command to view the contents of memory where we think that string is stored.
(gdb) x/s 0x40062c
0x40062c:	 "Hello World!"
We can also look at the equivalent ASCII characters of “Hello World” in memory
(gdb) x/12db 0x40062c
0x40062c:	72	101	108	108	111	32	87	111
0x400634:	114	108	100	33
An interesting thing about GCC is that it automatically optimizes printf() with 1 argument to use puts(). This confused me at first since the code used printf() but the disassembly shows a call to puts()
   0x000000000040052d <+9>:	call   0x400418 <puts@plt>
The puts() function automatically adds a new line, that’s why when we examine memory in GDB, we don’t see the ‘n’ character that we added when we called printf() in the code
More to come; we’re just getting started!