C 和 ASM 之间的堆栈缓冲区大小不同

逆向工程 部件
2021-06-14 03:00:37

鉴于该功能:

void vuln( char * arg ) {
    char buf[256];
    strcpy(buf, arg);
}

拆解于:

       0x0804842b      55             push ebp                                                                                                                                                                                          
       0x0804842c      89e5           mov ebp, esp                                                                                                                                                                                      
       0x0804842e      81ec08010000   sub esp, 0x108                                                                                                                                                                                    
       0x08048434      83ec08         sub esp, 8                                                                                                                                                                                        
       0x08048437      ff7508         push dword [arg_8h]                                                                                            
       0x0804843a      8d85f8feffff   lea eax, ebp - 0x108                                                                                                                                                                              
       0x08048440      50             push eax                                                                                                           
       0x08048441      e8bafeffff     call sym.imp.strcpy                                                                                             
       0x08048446      83c410         add esp, 0x10                                                                                                                                                                                     
       0x08048449      c9             leave                                                                                                                                                                                             
       0x0804844a      c3             ret             

当参数为 264 = 0x108 个字符并且我期望 256 个字节时,它会溢出。为什么编译器增加了 8 个字节sub esp,8

1个回答
  • 根据SYS V i386 ABI,在执行CALL指令之前,堆栈必须至少与操作系统字对齐

    摘自 Peter Cordes对 x86 程序集中堆栈对齐责任的回答

    多年来,i386 System V ABI 一直保证/要求 ESP+4 在进入函数时是 16B 对齐的。(即 ESP 必须在 CALL 指令之前以 16B 对齐,因此堆栈上的 args 从 16B 边界开始。这与 x86-64 System V 相同。)

  • 此外,GCC 默认将堆栈与 16 字节边界对齐。为了实现这一点,GCC 将在函数堆栈帧内的一个区域中分配额外的空间,该区域被 ABI 认为是不受限制的:

    其他方面取决于编译器和正在编译的代码。标准调用序列不定义最大堆栈帧大小,也不限制语言系统如何使用标准堆栈帧的“未指定”区域。

  • 在 ABI 中,函数返回地址被认为是当前堆栈帧的一部分:

i386 堆栈框架布局

这些信息足以让我们确定为什么编译器会生成在堆栈上创建未使用空间的代码。让我们检查代码,重点关注导致堆栈帧内存分配的指令:

   0x0804842b      55             push ebp                     ( 1 )                                                                                                                                                                     
   0x0804842c      89e5           mov ebp, esp                                                                                                                                                                                      
   0x0804842e      81ec08010000   sub esp, 0x108               ( 2 )                                                                                                                                                                     
   0x08048434      83ec08         sub esp, 8                   ( 3 )                                                                                                                                                                    
   0x08048437      ff7508         push dword [arg_8h]          ( 4 )                                                                                  
   0x0804843a      8d85f8feffff   lea eax, ebp - 0x108                                                                                                                                                                              
   0x08048440      50             push eax                     ( 5 )                                                                                      
   0x08048441      e8bafeffff     call sym.imp.strcpy          ( 6 )                                                                                   
   0x08048446      83c410         add esp, 0x10                                                                                                                                                                                     
   0x08048449      c9             leave                                                                                                                                                                                             
   0x0804844a      c3             ret             

  1. 我们现在知道返回地址被认为是当前堆栈帧的一部分。i386 机器为 32 位架构,因此返回地址占用 4 个字节的空间,执行该指令push ebp会将堆栈指针递减 4 个字节。

    • 4 + 4 = 8,因此此时堆栈帧的大小为 8 个字节。
  2. 正如您所指出的,这会创建 264 字节的空间。

    • 8 + 264 = 272
  3. sub esp, 8- 有问题的具体说明。在堆栈帧上创建 8 个额外字节的空间。

    • 272 + 8 = 280
  4. 和以前一样,push具有添加 4 个字节的空间的效果。

    • 280 + 4 = 284
  5. 另一个push

    • 284 + 4 = 288
  6. 现在是执行call指令的时候了。此时堆栈帧的大小为 288 字节。让我们检查是否与 16 字节边界对齐:

    • 288 % 16 = 0(感谢 RadLexus)。堆栈帧正确对齐。


结论:

编译器用于sub esp, 8将堆栈帧对齐到 16 字节边界。

也可以看看: