为什么 EIP 在本地缓冲区结束之前被覆盖?

逆向工程 拆卸 数据库 缓冲区溢出
2021-06-29 11:56:47

我正在做一个简单的缓冲区溢出练习,这是来源:

//vuln.c
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
    char buf[256];
    strcpy(buf,argv[1]);
    printf("Input:%s\n",buf);
    return 0;
}

遵守gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609在Ubuntu 16.04.6(i686的)这样的(ASLR禁用):

$ gcc -g -fno-stack-protector -z execstack -o vuln vuln.c

gdb 反汇编:

Dump of assembler code for function main:
   0x0804843b <+0>:     lea    ecx,[esp+0x4]
   0x0804843f <+4>:     and    esp,0xfffffff0
   0x08048442 <+7>:     push   DWORD PTR [ecx-0x4]
   0x08048445 <+10>:    push   ebp
   0x08048446 <+11>:    mov    ebp,esp
   0x08048448 <+13>:    push   ecx
   0x08048449 <+14>:    sub    esp,0x104
   0x0804844f <+20>:    mov    eax,ecx
   0x08048451 <+22>:    mov    eax,DWORD PTR [eax+0x4]
   0x08048454 <+25>:    add    eax,0x4
   0x08048457 <+28>:    mov    eax,DWORD PTR [eax]
   0x08048459 <+30>:    sub    esp,0x8
   0x0804845c <+33>:    push   eax
   0x0804845d <+34>:    lea    eax,[ebp-0x108]
   0x08048463 <+40>:    push   eax
   0x08048464 <+41>:    call   0x8048310 <strcpy@plt>
   0x08048469 <+46>:    add    esp,0x10
   0x0804846c <+49>:    sub    esp,0x8
   0x0804846f <+52>:    lea    eax,[ebp-0x108]
   0x08048475 <+58>:    push   eax
   0x08048476 <+59>:    push   0x8048510
   0x0804847b <+64>:    call   0x8048300 <printf@plt>
   0x08048480 <+69>:    add    esp,0x10
   0x08048483 <+72>:    mov    eax,0x0
   0x08048488 <+77>:    mov    ecx,DWORD PTR [ebp-0x4]
   0x0804848b <+80>:    leave
   0x0804848c <+81>:    lea    esp,[ecx-0x4]
   0x0804848f <+84>:    ret
End of assembler dump.

当我用以下内容覆盖 EIP 时:

aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzzAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ1111111111111111111111111111111111111111111111111111

它给了我EIP: 0x5a5a5a5a ('ZZZZ'),这意味着返回地址的偏移量是 208。那么分配 256 字节缓冲区时怎么可能呢?main() 堆栈布局如何?我认为它应该是这样的:

|  argc
|  argv
|  Return address
|  Caller's EBP       <-- EBP
|  Alignment
|  Local variables    <-- buf ends here
|  ...
|  Local variables    <-- buf starts here
|  ...
|  ...
|  ...                <-- ESP
V
Lower addresses

我也很困惑为什么当参数字符串的长度大于 260 时我无法控制 EIP。这就是我的意思。

这是运行的结果 gdb-peda$ r `python -c 'print "A"*260'`

sct-1

这是运行的结果 gdb-peda$ r `python -c 'print "A"*261'`

sct-2

这是运行的结果 gdb-peda$ r `python -c 'print "A"*262'`

在此处输入图片说明

非常感谢帮助。谢谢!

1个回答

esp函数末尾值是根据ecx堆栈中存储计算的该值立即存储在缓冲区的“上方”(具有更高的地址),在您的情况下,该缓冲区具有260字节而不是256(注意sub esp, 0x104- 这背后的原因是16在每次函数调用之前保持堆栈与字节对齐)。那么为什么提供260字节会导致分段错误呢?

因为您提供的是261字节,因为NULLC 中每个字符串的末尾都有一个额外的字节!那么会发生什么,您实际上是在覆盖ecx存储在 stack 上的值的最低有效字节您将其设置为0x00,因此它很可能会降低其值。在函数的末尾,esp获取值ecx-0x4=previous_ecx/256-4而不是previous_ecx-4,因此reteip根据该值进行设置如您所见,esp很可能已减小,因此现在它指向缓冲区内的“ZZZZ”。下图显示了程序的堆栈布局: 堆栈布局

当您只输入“A”时,会发生完全相同的事情。当您放置更多“A”时,情况会略有变化,但只需查看ecx显示gdb:它0x41在末尾和NULL之前字节中获得更多,导致esp更改为更多随机值。