为什么我的 char[30] 堆栈增加到 0x38

Why does my stack get increased to 0x38 for char[30]

在这个方法中

void lame(void) {
 char small[30];
 gets(small);
 printf("%s\n",small);
}

我希望堆栈增加 0x20

但是 gdb 显示它增加了 0x38

0x08048450 <+3>:  sub [=11=]x38, %esp

我不太确定回答这个问题需要哪些信息,所以如果我遗漏了什么,请告诉我,我会添加:

我在做什么 只是想解决缓冲区溢出问题,发现 this 我正在关注的示例

disas lame

你最好的选择是简单地查看函数的汇编代码并找出它在esp和[=11=偏移的区域中做了什么](取决于您的调用约定)。

例如,它可能将它们用于各种临时对象。或者它可能正在为要发送到 gets and/or printf 的参数预分配 space。或者它可能试图将堆栈指针对齐到某些 "resolution",例如,缓和 SSE 对象对齐。

或者它可能认识到使用 gets 的人往往不了解缓冲区溢出的危险,并且它为您提供了少量额外的呼吸空间 space :-)

最重要的是,如果不查看编译器生成的内容,我们无法判断。然而,即使 有了 这些信息,关于 为什么 编译器分配的分配比请求的多,可能并不明显,因为我们可能仍在使用不完整的信息。

根据您添加的汇编程序转储,getsprintf 的缓冲区(已偷偷优化为 puts)比 [=] 高 18 个字节52=] 堆栈指针并且没有立即使用中间区域。

因此,除非 gets/puts 将它用于某事(不太可能)或程序链接 table 确实如此(仍然不太可能,但它是 convoluted beast),您可能只需要假设它是由编译器生成的针对您的平台的矫枉过正或对齐要求。

您当然可以使用各种数组大小对其进行测试,以查看它如何影响分配的 space。这可能至少会提供一些额外的信息,使理论更可行。

根据您关于 char[15] 分配 0x28char[16] 分配 0x38 的评论,这似乎是堆栈指针的 16 字节对齐要求(它以 8 结尾,因为调用中已经使用了八个字节:return 地址本身,以及之前的 ebx).

而且,事实上,如果您在网上搜索 gcc 16 byte alignment stack pointer,您会发现一些关于 gcc 是否在做正确的事情的非常热烈的讨论,但是,在所有这些讨论中, gcc 将堆栈对齐到 16 个字节(正确或错误)的基本事实。

首先,您的缓冲区四舍五入为 0x20 字节,这不足为奇。传出参数需要 4 个字节。所以我们有 0x24 字节的净要求。

更新后的调用约定要求堆栈指针必须保持 16 字节对齐。由于 push %ebx 使用 4 个字节,另外 4 个字节用于 call 指令放入堆栈的 return 地址,因此调整必须从 16 的倍数减去 8 个字节. 这与我们的 0x24 字节的最低要求相结合意味着分配 0x28 应该足够了。 gcc 而不是为总共 0x38 分配 16 个额外字节似乎是至少 gcc 版本 4.4 到 4.9 表现出的错误或特性。请注意 clangllvm-gcc 都分配了预期的 0x28 字节。

我的测试排除了与使用 gets 或编译器将 2 个参数 printf 优化为 1 个参数 puts 有关的额外 space。