为什么有一个缓冲的结束和保存的帧指针之间的8个字节? [英] Why are there 8 bytes between the end of a buffer and the saved frame pointer?

查看:194
本文介绍了为什么有一个缓冲的结束和保存的帧指针之间的8个字节?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我做了一个课程堆栈溢出的锻炼,我已经完成了任务,但有一个方面,我不明白。

下面是目标程序:

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;INT栏(字符*阿根廷,字符*出)
{
  的strcpy(出,ARG);
  返回0;
}无效美孚(字符* argv的[])
{
  焦炭BUF [256];
  酒吧(ARGV [1],BUF);
}INT主(INT ARGC,CHAR *的argv [])
{
  如果(的argc!= 2)
    {
      fprintf中(标准错误,目标1:ARGC = 2 \\ n);
      出口(EXIT_FAILURE);
    }
  富(ARGV);
  返回0;
}

下面是用于编译的命令,运行的Ubuntu 12.04的 86 虚拟机 ASLR 禁用。

  gcc的-ggdb -m32 -g -std = C99 -D_GNU_SOURCE -fno-堆栈保护-m32 target1.c -o目标1
execstack -s目标1

当我看着堆在这个程序的内存,我看到 BUF 的地址为 0xbffffc40 。此外,保存的帧指针存储在 0xbffffd48 ,返回地址存储在 0xbffffd4c

这些具体的地址是不相关的,但我观察到,即使 BUF 只有长度 256 的距离 0xbffffd48 - 0xbffffc40 = 264 。象征性的,这种计算是 $ FP - BUF

为什么有 8 BUF 的结束和对存储的帧指针之间额外字节栈?

下面是函数的一些拆卸。我已经分析过了,但我没有看到,内存区域的任何明显的使用,除非它是隐式的(即某些指令的副作用)。

  0x080484ab 1 + 0计算值:推%EBP
   0x080484ac 1 + 1&GT ;: MOV%ESP,EBP%
   0x080484ae 1 + 3计算值:子$量0x118,%尤
   0x080484b4 1 + 9计算值:MOV位于0x8(%EBP),%eax中
   0x080484b7 1 + 12计算值:加$为0x4,%eax中
   0x080484ba 1 + 15计算值:MOV(%EAX),EAX%
   0x080484bc 1 + 17计算值:LEA -0x108(%EBP),%EDX
   0x080484c2 1 + 23计算值:MOV%EDX,为0x4(%尤)
   0x080484c6 1 + 27计算值:MOV EAX%(%ESP)
   0x080484c9 1 + 30计算值:调用0x804848c< BAR>
   0x080484ce 1 + 35计算值:离开
   0x080484cf 1 + 36计算值:保留


解决方案

巴西莱Starynkevitch得到了提对齐奖

原来, GCC 4.7.2 默认帧边界对齐到4字的边界。在32位的仿真硬件,即16字节。由于保存的帧指针和保存的指令指针一起只占用8个字节,编译器放在另外8个字节 BUF 结束后对准栈帧的顶部一个16字节边界。

使用下列额外的编译标志,8个字节消失,由于8个字节是足以对准的2字边界

  -m preferred堆栈边界= 2

I am doing a stack-smashing exercise for coursework, and I have already completed the assignment, but there is one aspect that I do not understand.

Here is the target program:

#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 != 2\n");                 
      exit(EXIT_FAILURE);                                      
    }                                                          
  foo(argv);                                                   
  return 0;                                                    
}                                                              

Here are the commands used to compile it, on an x86 virtual machine running Ubuntu 12.04, with ASLR disabled.

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

When I look at the memory of this program on the stack, I see that buf has the address 0xbffffc40. Moreover, the saved frame pointer is stored at 0xbffffd48, and the return address is stored at 0xbffffd4c.

These specific addresses are not relevant, but I observe that even though buf only has length 256, the distance 0xbffffd48 - 0xbffffc40 = 264. Symbolically, this computation is $fp - buf.

Why are there 8 extra bytes between the end of buf and the stored frame pointer on the stack?

Here is some disassembly of the function foo. I have already examined it, but I did not see any obvious usage of that memory region, unless it was implicit (ie a side effect of some instruction).

   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 gets the prize for mentioning alignment.

It turns out that gcc 4.7.2 defaults to aligning the frame boundary to a 4-word boundary. On 32-bit emulated hardware, that is 16 bytes. Since the saved frame pointer and the saved instruction pointer together only take up 8 bytes, the compiler put in another 8 bytes after the end of buf to align the top of the stack frame to a 16 byte boundary.

Using the following additional compiler flag, the 8 bytes disappears, because the 8 bytes is enough to align to a 2-word boundary.

-mpreferred-stack-boundary=2

这篇关于为什么有一个缓冲的结束和保存的帧指针之间的8个字节?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆