堆栈溢出 - 内存中的缓冲区位置

3

我正在研究缓冲区溢出(在IA32架构上),并希望通过这个示例程序澄清一件特定的事情:

int main(int argc, char **argv) {
  char array[512];
  if(argc > 1)
    strcpy(array, argv[1]);
}

我跟随汇编代码执行过程中ebp、esp寄存器的变化:

对于函数main的汇编代码转储如下:

   0x080483c4 <+0>:     push   ebp
   0x080483c5 <+1>:     mov    ebp,esp
   0x080483c7 <+3>:     sub    esp,0x208
   0x080483cd <+9>:     cmp    DWORD PTR [ebp+0x8],0x1
   0x080483d1 <+13>:    jle    0x80483ed <main+41>
   0x080483d3 <+15>:    mov    eax,DWORD PTR [ebp+0xc]
   0x080483d6 <+18>:    add    eax,0x4
   0x080483d9 <+21>:    mov    eax,DWORD PTR [eax]
   0x080483db <+23>:    mov    DWORD PTR [esp+0x4],eax
   0x080483df <+27>:    lea    eax,[ebp-0x200]
   0x080483e5 <+33>:    mov    DWORD PTR [esp],eax
   0x080483e8 <+36>:    call   0x80482f4 <strcpy@plt>
   0x080483ed <+41>:    leave
   0x080483ee <+42>:    ret

esp、ebp寄存器的值如下:

program start
  esp: 0xbffff24c
  ebp: 0xbffff2c8

push ebp
  esp: 0xbffff248
  ebp: 0xbffff2c8

mov ebp,esp
  esp: 0xbffff248
  ebp: 0xbffff248

sub esp,0x208
  esp: 0xbffff040
  ebp: 0xbffff248

strcpy call (ebp is overwritten)
  esp: 0xbffff250
  ebp: 0x41414141

当我输入了520个字符的 'a',导致 EBP 和 EIP 被溢出。内存看起来像这样:
Lower Memory Addresses
0xbffff070:     0xbffff078      0xbffff492      0xaaaaaaaa      0xaaaaaaaa
0xbffff080:     0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa
0xbffff090:     0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa
0xbffff0a0:     0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa
0xbffff0b0:     0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa
...
0xbffff270:     0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa      0xaaaaaaaa
Higher Memory Addresses

我感兴趣的问题是:为什么缓冲区从地址0xbffff078开始,而ESP指向地址0xbffff040(当栈上保留本地变量 - 缓冲区 - 的位置时)。缓冲区应该保存在0xbffff040地址处。有人能解释一下为什么不是这样吗?


你的 'a' 字符是怎么变成了 0xaaaaaaaa 的?你展示的程序是完整的吗? - Carl Norum
你确定 array 的地址是 0xbffff078,而不是 0xbffff048 吗?也许 strcpy 的后续操作尝试进行一些栈操作,覆盖了 0xbffff070 处的 8 个字节? - aschepler
内存转储是否来自与寄存器跟踪相同的程序调用? - aaz
嗨,我想我解决了它:aaz你的答案帮了我。当然我在想什么,答案是如此明显。输出不来自程序的同一次调用:一个来自gdb运行,另一个直接来自bash,所以当然不一样了,因为gdb在堆栈上使用更多空间,所以它的本地变量地址比来自bash调用的地址低。 我会标记为:问题已解决。谢谢。 - eleanor
1个回答

5
编译器决定同时在栈上为本地变量和传递给 strcpy() 的参数分配空间。请注意,and 是表示“和”的连词,不是“与”的意思。
sub    esp,0x208                # 0x200 for array, 8 for 2 pointers
...
mov    DWORD PTR [esp+0x4],eax  # argv[1], not push
mov    DWORD PTR [esp],eax      # array

因此,该数组实际上位于esp+8(或者编译器所称的ebp-0x200)。
编辑:
esp在程序调用之间是不同的,特别是取决于参数的数量。 0xbffff0400xbffff070
缓冲区位于0xbffff048,而不是0xbffff040。或者位于0xbffff078,而不是0xbffff070。询问调试器以检查其位置。
在内存转储中,缓冲区具有0xaa(我猜这是调用之前未初始化的内存)。在此之前,您可以看到传递给strcpy()的两个指针。

是的,我犯了一个错误,它应该是0xbffff048,但这仍然无法解释为什么它在0xbffff078而不是应该在0xbffff048(因为栈上已经为其保留了空闲空间)。 - eleanor
我猜在进行内存转储之前,您重新启动了程序而没有带参数。因此,堆栈帧更高,缓冲区填充为“0xaa”而不是“0x41”(“a”)。是这种情况吗? - aaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接