给定该函数:

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字节。为什么编译器用sub esp,8增加8个字节?

评论

可能是因为strcpy

为什么呢什么 ?怎么样 ? :-)

#1 楼



根据SYS V i386 ABI,在执行CALL指令之前,堆栈必须以最小操作系统字对齐。

彼得·科德斯(Peter Cordes)对x86组装中的堆栈对齐的责任的回答的摘录:在函数输入时为16B对齐。 (即,ESP必须在CALL指令之前对齐16B,因此堆栈上的args从16B边界开始。这与x86-64 System V相同。)



另外,GCC通过defualt将堆栈对齐到16字节边界。为此,GCC将在函数的堆栈框架内的某个区域中分配额外的空间,而该区域不会被ABI限制:


其他区域取决于编译器和所编译的代码。标准调用序列没有定义最大堆栈框架大小,也没有限制语言系统如何使用标准堆栈框架的“未指定”区域。


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



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

   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             

返回地址被认为是当前堆栈帧的一部分。 i386机器具有32位体系结构,因此返回地址占用4个字节的空间,执行指令push ebp将使堆栈指针减少4个字节。


4 + 4 = 8,所以此时堆栈帧的大小为8个字节。



,这将创建264个字节的空间。


8 + 264 = 272



sub esp, 8-有问题的特定说明。在堆栈框架上额外创建8个字节的空间。






和以前一样,push具有添加4个字节空间的效果。


280 + 4 = 284



另一个push。 />
284 + 4 = 288



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







结论:

编译器使用sub esp, 8来保持堆栈框架对齐到16字节的边界。

另请参见:


https://stackoverflow.com/questions/4175281/what-does-it-mean-对齐堆栈
https://software.intel.com/zh-cn/forums/intel-isa-extensions/topic/291241


评论


@ usr2564301是的,很好。我会改变它。谢谢。

– julian♦
17年12月29日在23:49

极好的答案@SYS_V。谢谢。您能解释一下为什么它为char buf [256]保留0x108(264)字节而不是0x100吗?

– Kartone
17年12月30日在1:07

@Kartone我能做的最好的猜测是:如果为缓冲区分配了264个字节,则我们有8 + 264 + 8 + 4 + 4 = 288,其中288%16 =0。但是,如果为缓冲区分配了256个字节,对于给定的代码,我们取而代之的是8 + 256 + 8 + 4 + 4 = 280,其中280%16 = 8,这意味着堆栈未正确对齐16字节边界。要对此进行更正,编译器将必须生成其他指令以对齐堆栈。

– julian♦
17/12/30在1:24



@Kartone没问题,欢迎您

– julian♦
17年12月30日在21:44