4

我正在阅读这本书Hacking: The Art of Exploitation , 2nd Edition 和简单的 C 程序

#include <stdio.h>
int main()
{
    int i;  
    for (i = 0; i < 10; i++)
    {
        puts("Hello, world!\n");
    }
    return 0;
}

书中列出了 gdb 调试会先修改 ebp 寄存器:

(gdb) x/i $eip 0x8048384 <main+16>: mov DWORD PTR [ebp-4],0x0

正如它解释的那样, 这条汇编指令会将值 0 移动到位于 EBP 寄存器中存储的地址的内存中,减 4。这是 C 变量 i 存储在内存中的位置;i 被声明为在 x86 处理器上使用 4 字节内存的整数

这对我来说很有意义,但是当我在“非常旧的 I386”Linux 笔记本电脑上测试确切的步骤时,我得到了以下结果:

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

所以在我的笔记本电脑上,它显示的是 [ebp-0xc],而不是 [ebp-4]。根据我的理解,“0xc”作为十六进制将是 12,所以它将是 12 字节?如果是这样,为什么?

这是我笔记本电脑上这个简单程序(gdb)反汇编主程序的整个汇编转储

Dump of assembler code for function main:
   0x00401199 <+0>: lea    ecx,[esp+0x4]
   0x0040119d <+4>: and    esp,0xfffffff0
   0x004011a0 <+7>: push   DWORD PTR [ecx-0x4]
   0x004011a3 <+10>:    push   ebp
   0x004011a4 <+11>:    mov    ebp,esp
   0x004011a6 <+13>:    push   ebx
   0x004011a7 <+14>:    push   ecx
   0x004011a8 <+15>:    sub    esp,0x10
   0x004011ab <+18>:    call   0x4010a0 <__x86.get_pc_thunk.bx>
   0x004011b0 <+23>:    add    ebx,0x2e50
=> 0x004011b6 <+29>:    mov    DWORD PTR [ebp-0xc],0x0
   0x004011bd <+36>:    jmp    0x4011d5 <main+60>
   0x004011bf <+38>:    sub    esp,0xc
   0x004011c2 <+41>:    lea    eax,[ebx-0x1ff8]
   0x004011c8 <+47>:    push   eax
   0x004011c9 <+48>:    call   0x401030 <puts@plt>
   0x004011ce <+53>:    add    esp,0x10
   0x004011d1 <+56>:    add    DWORD PTR [ebp-0xc],0x1
   0x004011d5 <+60>:    cmp    DWORD PTR [ebp-0xc],0x9
   0x004011d9 <+64>:    jle    0x4011bf <main+38>
   0x004011db <+66>:    mov    eax,0x0
   0x004011e0 <+71>:    lea    esp,[ebp-0x8]
   0x004011e3 <+74>:    pop    ecx
   0x004011e4 <+75>:    pop    ebx
   0x004011e5 <+76>:    pop    ebp
   0x004011e6 <+77>:    lea    esp,[ecx-0x4]
   0x004011e9 <+80>:    ret    
End of assembler dump.
4

1 回答 1

4

sub esp,0x10

在堆栈上为变量和其他东西分配了 16 个字节(四个寄存器)的空间。

mov DWORD PTR [ebp-0xc],0x0

似乎是对 slot 的第一个引用ebp-0xc,并且它被初始化为零。看过之后cmp DWORD PTR [ebp-0xc],0x9main+60我确定这是来自循环i = 0的初始化部分。for

编译器可以将变量放在它想要的地方,并且在确定性的同时它会随着编译器的补丁版本而改变。

于 2021-02-07T19:40:49.160 回答