c - 堆栈中的溢出技术

标签 c stack-overflow

int main(void) {
   problem2();
}

void doit2(void) {
    int overflowme[16];
    //overflowme[37] =0;
}

void problem2(void) {
    int x = 42;
    doit2();
    printf("x is %d\n", x);
    printf("the address of x is 0x%x\n", &x);
}

有人能帮助我理解为什么doit2函数中的overflowme[37]=0;会覆盖x的值吗?(请在说明中包括doit2函数的程序计数器和帧指针)谢谢!
它每次都在x86 windows机器上运行(好的!)使用Project属性->配置属性-> C/C++>代码生成->基本运行时检查设置为“默认”。所以这不是一个未定义的行为。

最佳答案

就像其他人所说的,这取决于目标和编译器,但是对于您来说,这些是不变的,并且代码中没有任何其他东西看起来像是在向堆栈引入随机性(相对来说),所以它每次都会做相同的事情。
系统堆栈通常从高地址向下扩展到低地址。如果堆栈指针是0x1234,并且您推送一个值(在32位{4字节}系统上),那么堆栈指针将变为0x1230。
数组的地址从最低地址到最高地址。如果你有

char a[2];

[0]在0x0122,那么[1]在0x0123。
您在doit2中的数组是一个自动变量,意味着它是在函数输入时创建的,并在函数退出时被删除。自动变量必须存在于堆栈或寄存器中。由于它是一个数组,编译器将其放在RAM中而不是寄存器中要复杂得多(这使得索引更容易,因为它只是将索引*大小添加到数组的第一个成员的地址)。由于堆栈在RAM中,编译器将数组放在堆栈上。
为该数组在堆栈上分配空间意味着堆栈指针小于该数组不存在时的值。堆栈指针在sizeof(int)*16中时最有可能指向overflowme[0]
还有其他东西可能在堆栈上,还有一些东西必须在堆栈上。堆栈上必须包含的内容是调用函数时推送到那里的返回指针。在32位系统上,每一个字节应该占4个字节。可能在堆栈上的东西(如果编译器想使用它)是前一个帧指针。(显式*)x86-32上的堆栈帧只是ESP和EBP之间的空间,但它们不是必需的,因此通常不使用它们,而EBP只是用作通用寄存器(可用的更通用的寄存器通常是好的)。
不过,使用堆栈帧很有用,因为它们使调试变得更容易,因为ESP和EBP充当局部变量边缘的标记。堆栈帧有时是必要的,例如使用doit2或C99的可变大小自动数组时,因为它们允许alloca或等效指令而不是mov EPB, ESP丢弃函数的局部变量空间,因此编译器不必知道帧的大小。它们还允许相对于EBP来处理局部变量,而不是在sub size_of_local_variable, ESP变化的情况下处理ESP。在这种情况下,除非通过调用函数更改并还原EBP,否则它在当前函数结束之前不会更改。
在没有优化的情况下进行编译时,编译器经常使用堆栈帧,因为它们使调试更容易。使用堆栈帧对代码建模,并在证明不需要堆栈帧后将代码转换为不使用堆栈帧也更容易。
因此,EBP的前一个值可能存在,也可能不存在于返回地址(在alloca中的某处)和problem2doit2中的最后一个元素之间的堆栈中。编译器还可以自由地在堆栈上放置任何其他感觉上的东西,所以谁知道还有什么可能在那里呢。
overflowme的局部变量可以放入寄存器或堆栈中。在没有优化的情况下进行编译时,即使局部变量可以进入寄存器,它们也常常会进入堆栈。
所以,假设堆栈上有problem2'sint x数组、一个旧的帧指针、一个返回地址和doit2'soverflowme(还有{下面的一些东西,实际上是在一个更高的地址}下)。
由于problem2与将x的第一个元素的地址添加到(i*{the size of&(overflowme[i])})相同,并且旧的EBP位于overflowme的最后一个元素之后,而返回地址位于旧的EBP之后,int位于返回地址之后,因此,overflowme肯定会被缓冲区溢出所占用。
为什么37的指数会出现这种情况还不清楚。指针数学(假设上面我所述的项在数组和int x之间的堆栈上)并不建议它应该基于4字节指针(32位机器),但是如果这是8字节指针系统(64位机器),那么数学更接近于我所期望的x的地址(如果x)。编译器还可以继续并为调用x分配堆栈空间(格式字符串后面的变量参数必须在堆栈上),这将影响数学(同时也鼓励编译器将sizeof(int) == 8放在堆栈上,因为它无论如何都必须将其推送到堆栈上)。
如果您想更详细地回答您的问题,请查看此代码的程序集并计算出确切的地址。
即使EBP没有用作帧基指针,您也可以认为堆栈帧在那里,但是帧不会被框起来。

关于c - 堆栈中的溢出技术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2885994/

相关文章:

c++ - 排序堆栈溢出和比较数以及负交换数

c - “struct in6_addr”没有名为 ‘s6_addr32’ 的成员,带有 -ansi

c - 检测 char 数组中有多少行

c - 管道输出的重复

c++ - 如何在 c/c++ 的命令行程序中创建 --help 选项?

Android:EditText TextChangedListener 上的堆栈溢出错误

java - finally block 中的堆栈溢出错误处理

hadoop - 了解 stackoverflow 底层软件基础设施

c++ - 为 C/C++ 程序编写检测工具

c++ - 使用 mingw 编译时增加堆栈大小?