I recently started reading a book about reverse engineering. In one of the examples it uses gdb (debugger) to disassemble the main. So I did it, and the output is below

(gdb) disass main
Dump of assembler code for function main:
   0x0000118d <+0>: lea    ecx,[esp+0x4]
   0x00001191 <+4>: and    esp,0xfffffff0
   0x00001194 <+7>: push   DWORD PTR [ecx-0x4]
   0x00001197 <+10>:    push   ebp
   0x00001198 <+11>:    mov    ebp,esp
   0x0000119a <+13>:    push   ebx
   0x0000119b <+14>:    push   ecx
   0x0000119c <+15>:    sub    esp,0x10
   0x0000119f <+18>:    call   0x1090 <__x86.get_pc_thunk.bx>
   0x000011a4 <+23>:    add    ebx,0x2e5c
   0x000011aa <+29>:    mov    DWORD PTR [ebp-0xc],0x0
   0x000011b1 <+36>:    jmp    0x11c9 <main+60>
   0x000011b3 <+38>:    sub    esp,0xc
   0x000011b6 <+41>:    lea    eax,[ebx-0x1ff8]
   0x000011bc <+47>:    push   eax
   0x000011bd <+48>:    call   0x1030 <puts@plt>
   0x000011c2 <+53>:    add    esp,0x10
   0x000011c5 <+56>:    add    DWORD PTR [ebp-0xc],0x1
   0x000011c9 <+60>:    cmp    DWORD PTR [ebp-0xc],0x9
   0x000011cd <+64>:    jle    0x11b3 <main+38>
--Type <RET> for more, q to quit, c to continue without paging--
   0x000011cf <+66>:    mov    eax,0x0
   0x000011d4 <+71>:    lea    esp,[ebp-0x8]
   0x000011d7 <+74>:    pop    ecx
   0x000011d8 <+75>:    pop    ebx
   0x000011d9 <+76>:    pop    ebp
   0x000011da <+77>:    lea    esp,[ecx-0x4]
   0x000011dd <+80>:    ret    
End of assembler dump.

And now I put a breakpoint at main to check the eip register value and compare it with address at main function as shown below

(gdb) info register eip
eip            0x565561aa          0x565561aa <main+29>

And here comes the problem, eip register value is not matching with the address at main in my case (shown below),

0x000011aa <+29>:   mov    DWORD PTR [ebp-0xc],0x0

whereas in the example it was mentioned that it will match. Please help me in solving this. Thanks in advance.

Update : examining instruction at eip , address matches with eip but not with address in the memory dumb shown above

(gdb) x/i $eip
=> 0x565561aa <main+29>:    mov    DWORD PTR [ebp-0xc],0x0
  • 1
    "a book about reverse engineering" what book is that? Maybe it's relevant to check what exactly is written there. – Paweł Łukasik May 19 '19 at 6:25
  • The name of the book is "Hacking - The art of exploitation" by Jon Erickson. The mentioned example above is from Pg. 26,27 – Finding_me May 20 '19 at 9:11

It is not a mismatch in memory addresses; it is a file offset of the instruction prior to runtime vs. the runtime memory address of the instruction.

  • 0x000011aa <+29>: mov DWORD PTR [ebp-0xc],0x0

    Here, the value 0x000011aa is the location of the instruction within the ELF file.

  • 0x565561aa <main+29>: mov DWORD PTR [ebp-0xc],0x0

    Here, 0x565561aa is the memory address of the instruction after the ELF file has been loaded into memory.

Why the difference? The main reason is that the ELF binary here is a position-independent executable (PIE) and therefore the absolute memory addresses cannot be known until after the program has been loaded, since the program can be loaded anywhere in memory (position independence). Since the absolute memory addresses are not known until runtime, if gdb is used to disassemble position independent code (PIC) prior to the run command being executed, the file offsets will be printed instead.

So how do we know that the code is indeed position independent? We can tell just by looking at the the disassembly.

The first way we can tell is by looking at the leftmost column, where normally we would expect to see memory addresses near 0x8048000, the canonical entry point in x86 binaries compiled by GCC. However, the values we see here are near 0x0000118d, and so are more likely to be file offsets rather than memory addresses.

More compellingly, in position-independent code, instruction pointer-relative addressing is used instead of absolute addresses. This is what we see in the disassembly in your post:

 0x0000119f <+18>:    call   0x1090 <__x86.get_pc_thunk.bx>  <- get address of next instruction
 0x000011a4 <+23>:    add    ebx,0x2e5c                      <- store location of GOT in ebx
 0x000011aa <+29>:    mov    DWORD PTR [ebp-0xc],0x0         <- move 0 to GOT entry
 0x000011b1 <+36>:    jmp    0x11c9 <main+60>                 
 0x000011b3 <+38>:    sub    esp,0xc
 0x000011b6 <+41>:    lea    eax,[ebx-0x1ff8]                <- load data relative to GOT location stored in ebx
 0x000011bc <+47>:    push   eax
 0x000011bd <+48>:    call   0x1030 <puts@plt>               <- function from DLL, address in PLT at runtime

All addressing here is relative. The call to __x86.get_pc_thunk.bx is necessary, because the address of the next instruction will be used to calculate relative offsets to the instruction pointer.

More info:

Position Independent Code (PIC) in shared libraries

What is PLT/GOT?

| improve this answer | |

I had the same problem following the "Art of exploitation" book. As @julian said, you will need to disassemble after you reached the breakpoint such as:

  1. start gdb
  2. add breakpoint
  3. run
  4. disassemble

    gdb -q firstprog
    Reading symbols from firstprog...done.
    (gdb) break main
    Breakpoint 1 at 0x642: file main.c, line 6.
    (gdb) run
    Starting program: /home/bin/Debug/firstprog 
    Breakpoint 1, main () at main.c:6
    6     for(i = 0; i < 10; i++) {
    (gdb) disassemble main
    Dump of assembler code for function main:
       0x000055555555463a <+0>:   push   rbp
       0x000055555555463b <+1>:   mov    rbp,rsp
       0x000055555555463e <+4>:   sub    rsp,0x10
    => 0x0000555555554642 <+8>:   mov    DWORD PTR [rbp-0x4],0x0
       0x0000555555554649 <+15>:  jmp    0x55555555465b <main+33>
       0x000055555555464b <+17>:  lea    rdi,[rip+0xa2]        # 0x5555555546f4
       0x0000555555554652 <+24>:  call   0x555555554510 <puts@plt>
       0x0000555555554657 <+29>:  add    DWORD PTR [rbp-0x4],0x1
       0x000055555555465b <+33>:  cmp    DWORD PTR [rbp-0x4],0x9
       0x000055555555465f <+37>:  jle    0x55555555464b <main+17>
       0x0000555555554661 <+39>:  mov    eax,0x0
       0x0000555555554666 <+44>:  leave  
       0x0000555555554667 <+45>:  ret    
    End of assembler dump.
    (gdb) info register rip
    rip            0x555555554642 0x555555554642 <main+8>

Now the instruction pointer register (RIP because it's 64bit in my case) points to the same address as the main's breakpoint.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.