Examples

1. Print "hello world!"

080483fb <main>:
 80483fb:       8d 4c 24 04             lea    ecx,[esp+0x4]
 80483ff:       83 e4 f0                and    esp,0xfffffff0
 8048402:       ff 71 fc                push   DWORD PTR [ecx-0x4]
 8048405:       55                      push   ebp
 8048406:       89 e5                   mov    ebp,esp
 8048408:       51                      push   ecx
 8048409:       83 ec 04                sub    esp,0x4
 804840c:       83 ec 0c                sub    esp,0xc
 804840f:       68 b0 84 04 08          push   0x80484b0
 8048414:       e8 b7 fe ff ff          call   80482d0 <puts@plt>
 8048419:       83 c4 10                add    esp,0x10
 804841c:       b8 00 00 00 00          mov    eax,0x0
 8048421:       8b 4d fc                mov    ecx,DWORD PTR [ebp-0x4]
 8048424:       c9                      leave  
 8048425:       8d 61 fc                lea    esp,[ecx-0x4]
 8048428:       c3                      ret    
 8048429:       66 90                   xchg   ax,ax
 804842b:       66 90                   xchg   ax,ax
 804842d:       66 90                   xchg   ax,ax
 804842f:       90                      nop

The important instructions

 804840f:       68 b0 84 04 08          push   0x80484b0
 8048414:       e8 b7 fe ff ff          call   80482d0 <puts@plt>

a. The first line simply pushes the pointer of the string onto the stack (to the puts() function)

b. The second line calls the puts() function

Rest of the instructions

  • The rest of the instructions deals with moving the ESP and EBP around for stack frame setup and restoration, preparing spaces for local variables, and stack alignment

Debugging with GDB

Refer to the following GDB notes for more information on the available commands

We can use gdb to better understand the binary (with the gef extension).

  1. Disassemble the main function:

$ gdb hello_world
gef> disass main
Dump of assembler code for function main:
   0x080483fb <+0>:     lea    ecx,[esp+0x4]
   0x080483ff <+4>:     and    esp,0xfffffff0
   0x08048402 <+7>:     push   DWORD PTR [ecx-0x4]
   0x08048405 <+10>:    push   ebp
   0x08048406 <+11>:    mov    ebp,esp
   0x08048408 <+13>:    push   ecx
   0x08048409 <+14>:    sub    esp,0x4
   0x0804840c <+17>:    sub    esp,0xc
   0x0804840f <+20>:    push   0x80484b0
   0x08048414 <+25>:    call   0x80482d0 <puts@plt>
   0x08048419 <+30>:    add    esp,0x10
   0x0804841c <+33>:    mov    eax,0x0
   0x08048421 <+38>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x08048424 <+41>:    leave
   0x08048425 <+42>:    lea    esp,[ecx-0x4]
   0x08048428 <+45>:    ret
End of assembler dump.
  1. Set a breakpoint on the main function

gef> break main
Note: breakpoint 1 also set at pc 0x8048409.
Breakpoint 2 at 0x8048409
  • We notice that the breakpoint is set at 0x8048409 , which corresponds with the following line (from the disass main command):

0x08048409 <+14>:    sub    esp,0x4

This is the instruction to be executed next, while the final instruction that was already executed right before the main function is called can be found on the previous line:

0x08048408 <+13>:    push   ecx
  1. Run

gef> run
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x080483fb  →  <main+0000> lea ecx, [esp+0x4]
$ebx   : 0xf7f97e14  →  0x00232d0c ("
                                     -#"?)
$ecx   : 0xffffcf00  →  0x00000001
$edx   : 0xffffcf20  →  0xf7f97e14  →  0x00232d0c ("
                                                    -#"?)
$esp   : 0xffffcee4  →  0xffffcf00  →  0x00000001
$ebp   : 0xffffcee8  →  0x00000000
$esi   : 0x08048430  →  <__libc_csu_init+0000> push ebp
$edi   : 0xf7ffcb60  →  0x00000000
$eip   : 0x08048409  →  <main+000e> sub esp, 0x4
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffcee4│+0x0000: 0xffffcf00  →  0x00000001    ← $esp
0xffffcee8│+0x0004: 0x00000000   ← $ebp
0xffffceec│+0x0008: 0xf7d89cc3  →   add esp, 0x10
0xffffcef0│+0x000c: 0x00000000
0xffffcef4│+0x0010: 0x00000000
0xffffcef8│+0x0014: 0xf7da3029  →   add ebx, 0x1f4deb
0xffffcefc│+0x0018: 0xf7d89cc3  →   add esp, 0x10
0xffffcf00│+0x001c: 0x00000001
────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x8048405 <main+000a>      push   ebp
    0x8048406 <main+000b>      mov    ebp, esp
    0x8048408 <main+000d>      push   ecx
●→  0x8048409 <main+000e>      sub    esp, 0x4
    0x804840c <main+0011>      sub    esp, 0xc
    0x804840f <main+0014>      push   0x80484b0
    0x8048414 <main+0019>      call   0x80482d0 <puts@plt>
    0x8048419 <main+001e>      add    esp, 0x10
    0x804841c <main+0021>      mov    eax, 0x0
────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "hello_world", stopped 0x8048409 in main (), reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048409 → main()
  • We can view a bunch of information:

a. Registers

b. Stack

c. Current position in the code

d. Threads

e. Trace

  1. Navigate through the code with nexti

Let's focus on the register esp. We can see the following line from the "stack" section before running the nexti instruction:

0xffffcee4│+0x0000: 0xffffcf00  →  0x00000001    ← $esp

4.1 1st nexti

Now, we can move through the program with the nexti command, which will simply execute the next instruction:

gef> nexti

Updated esp value:

0xffffcee0│+0x0000: 0x00000000   ← $esp
  • The stack value is decremented by 0x4 from the previous value

4.2 2nd nexti

Updated esp value:

0xffffced4│+0x0000: 0x00000000   ← $esp
  • The stack value is decremented by 0xC (decimal value of 12) from the previous value

4.3 3rd nexti

Now, the next instruction will push a new address into the stack:

0x804840f <main+0014>      push   0x80484b0

After running nexti again for the 3rd time, updated esp value:

0xffffced0│+0x0000: 0x080484b0  →  "hello world!"        ← $esp

We can see that the esp is decremented by 0x4, with the value 0x080484b0 pushed onto the stack, which is expected from the push command. We can also see that the stored address contains the value "hello world!".

We can confirm this with gdb:

gef➤  x/a 0xffffced0 # view address
0xffffced0:     0x80484b0
gef➤  x/10c 0x80484b0 # view 10 chars
0x80484b0:      0x68    0x65    0x6c    0x6c    0x6f    0x20    0x77    0x6f
0x80484b8:      0x72    0x6c
gef➤  x/s 0x80484b0 # view string
0x80484b0:      "hello world!"

Last updated