eip 寄存器地址与断点地址不匹配(断点设置在主函数)

逆向工程 拆卸 调试 数据库
2021-07-05 23:32:54

我最近开始阅读一本关于逆向工程的书。在其中一个示例中,它使用 gdb(调试器)来反汇编主文件。所以我做到了,输出如下

(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.

现在我在 main 处放置一个断点来检查 eip 寄存器值并将其与 main 函数处的地址进行比较,如下所示

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

问题来了,在我的例子中,eip 寄存器的值与 main 的地址不匹配(如下所示),

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

而在示例中提到它将匹配。请帮我解决这个问题。提前致谢。

更新:检查 eip 处的指令,地址与 eip 匹配,但与上面显示的内存中的地址不匹配

(gdb) x/i $eip
=> 0x565561aa <main+29>:    mov    DWORD PTR [ebp-0xc],0x0
2个回答

这不是内存地址不匹配;它是指令在运行时之前的文件偏移量与指令的运行时内存地址。

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

    此处,该值0x000011aa是指令在 ELF 文件中的位置。

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

    这里,0x565561aa是ELF文件加载到内存后指令的内存地址。

为什么会有差异?主要原因是这里的 ELF 二进制文件是与位置无关的可执行文件 (PIE),因此在加载程序之前无法知道绝对内存地址,因为程序可以加载到内存中的任何位置(位置无关)。由于绝对内存地址直到运行时才知道,如果gdb用于在执行命令之前反汇编位置无关代码 (PIC)run,则将打印文件偏移量。


那么我们怎么知道代码确实是位置无关的呢?看拆机就知道了。

我们可以分辨的第一种方法是查看最左边的列,通常我们希望在该列中看到内存地址 附近0x8048000,这是 GCC 编译的 x86 二进制文件中的规范入口点。但是,我们在这里看到的值接近0x0000118d,因此更有可能是文件偏移量而不是内存地址。

更引人注目的是,在位置无关代码中,使用指令指针相对寻址而不是绝对地址。这是我们在您帖子的反汇编中看到的:

 <snip>
 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
 </snip>

这里的所有寻址都是相对的。调用__x86.get_pc_thunk.bx是必要的,因为下一条指令的地址将用于计算指令指针的相对偏移量。

更多信息:

共享库中的位置无关代码 (PIC)

什么是 PLT/GOT?

我在阅读“剥削的艺术”一书后遇到了同样的问题。正如@julian 所说,到达断点需要进行反汇编例如:

  1. 启动 gdb
  2. 添加断点
  3. 拆卸

    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>
    (gdb)
    

现在指令指针寄存器(RIP,因为在我的情况下它是 64 位)指向与 main 断点相同的地址。