为什么缓冲区的末尾和保存的帧指针之间有8个字节

Why are there 8 bytes between the end of a buffer and the saved frame pointer?

本文关键字:之间 指针 8个 字节 保存 缓冲区 为什么      更新时间:2023-10-16

我正在为课程做一个砸栈练习,我已经完成了作业,但有一个方面我不理解。

这是目标程序:

#include <stdio.h>                                             
#include <stdlib.h>                                            
#include <string.h>                                            
int bar(char *arg, char *out)                                  
{                                                              
  strcpy(out, arg);                                            
  return 0;                                                    
}                                                              
void foo(char *argv[])                                         
{                                                              
  char buf[256];                                               
  bar(argv[1], buf);                                           
}                                                              
int main(int argc, char *argv[])                               
{                                                              
  if (argc != 2)                                               
    {                                                          
      fprintf(stderr, "target1: argc != 2n");                 
      exit(EXIT_FAILURE);                                      
    }                                                          
  foo(argv);                                                   
  return 0;                                                    
}                                                              

以下是在禁用ASLR的情况下,在运行Ubuntu 12.04x86虚拟机上用于编译它的命令。

gcc -ggdb -m32 -g -std=c99 -D_GNU_SOURCE -fno-stack-protector  -m32  target1.c   -o target1
execstack -s target1

当我查看堆栈上这个程序的内存时,我发现buf的地址是0xbffffc40。此外,保存的帧指针存储在0xbffffd48,返回地址存储在0xbffffd4c

这些特定地址并不相关,但我观察到,即使buf只有长度256,距离0xbffffd48 - 0xbffffc40 = 264。象征性地,这个计算是$fp - buf

为什么buf的末尾和堆栈上存储的帧指针之间有额外的8字节

以下是函数foo的一些反汇编。我已经检查过了,但我没有看到这个记忆区域有任何明显的用法,除非它是隐含的(即某些指令的副作用)。

   0x080484ab <+0>:     push   %ebp                    
   0x080484ac <+1>:     mov    %esp,%ebp               
   0x080484ae <+3>:     sub    $0x118,%esp             
   0x080484b4 <+9>:     mov    0x8(%ebp),%eax          
   0x080484b7 <+12>:    add    $0x4,%eax               
   0x080484ba <+15>:    mov    (%eax),%eax             
   0x080484bc <+17>:    lea    -0x108(%ebp),%edx       
   0x080484c2 <+23>:    mov    %edx,0x4(%esp)          
   0x080484c6 <+27>:    mov    %eax,(%esp)             
   0x080484c9 <+30>:    call   0x804848c <bar>         
   0x080484ce <+35>:    leave                          
   0x080484cf <+36>:    ret                            
Basile Starynkevitch因提及alignment而获奖。

事实证明,gcc 4.7.2默认将帧边界与4字边界对齐。在32位模拟硬件上,即16字节。由于保存的帧指针和保存的指令指针一起只占用8个字节,编译器在buf结束后再放入8个字节以将堆栈帧的顶部与16字节的边界对齐。

使用下面的附加编译器标志,8个字节将消失,因为8个字节足以与2字边界对齐。

-mpreferred-stack-boundary=2