【问题标题】:Why does my stack get increased to 0x38 for char[30]为什么我的 char[30] 堆栈增加到 0x38
【发布时间】:2015-01-13 01:18:55
【问题描述】:

在这个方法中

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

我希望堆栈增加 0x20

但 gdb 显示它增加了 0x38

0x08048450 <+3>:  sub $0x38, %esp

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

  • Ubuntu 140.04 32 位
  • 编译命令:cc -ggdb -fno-stack-protector blah.c -o blah
  • CC --version - 4.8.2
  • 在 Oracle Virtual Box 4.3.20 中运行

我在做什么 只是想玩弄缓冲区溢出并找到我正在关注的this 示例

disas lame

【问题讨论】:

  • 您忘记启用优化。传出参数也可以直接从堆栈中分配,然后对齐。
  • @Jester 谢谢,我尝试使用 3 个不同的选项 -O、-O1、-O2 重新编译,它们都产生相同的程序集
  • "gdb 显示它增加了 0x38":相对于 what 增加了?一个空函数?
  • 堆栈指针必须保持 16 字节对齐,以便留下 0x280x38 的可能性(push %ebx 使用 4 个字节,call 使用另外 4 个字节,这就是8 的来源)。您的缓冲区四舍五入到 0x20,我们需要额外的 4 个字节用于传出参数。不清楚为什么 0x28 不起作用。
  • @Jester,16 字节对齐是针对 x64 的,不是吗? asm 中似乎没有任何内容表明 64 位(例如使用 rax)并且 OP 声明它是 Ubuntu 32 位。

标签: c assembly x86


【解决方案1】:

对于初学者,您的缓冲区向上舍入到 0x20 字节,这并不奇怪。传出参数还需要 4 个字节。所以我们有0x24字节的净需求。

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

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

【讨论】:

    【解决方案2】:

    您最好的选择是简单地查看函数的汇编代码,并找出它对从espebp 偏移的区域做了什么(取决于您的调用约定)。

    例如,它可能会将它们用于各种临时用途。或者它可能正在为要发送到gets 和/或printf 的参数预先分配空间。或者它可能试图将堆栈指针与某个“分辨率”对齐,例如,放宽 SSE 对象对齐。

    或者它可能意识到使用gets 的人往往不了解缓冲区溢出的危险,它给了你一点额外的空间作为喘息的空间:-)

    归根结底,如果不查看编译器生成的内容,我们将无法判断。然而,即使这些信息,编译器分配的为什么可能并不明显,因为我们可能仍在处理不完整的信息。

    根据您添加的汇编程序转储,getsprintf 的缓冲区(已偷偷地优化为 puts)在堆栈指针之上 18 个字节,没有立即使用由中间区域组成。

    所以,除非gets/puts 将它用于某事(不太可能)或过程链接表确实(仍然不太可能,但它是convoluted beast),否则您可能只需要假设它对您的平台来说是矫​​枉过正或对齐要求,生成自编译器。

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

    根据您对char[15] 分配0x28char[16] 分配0x38 的评论,这似乎是堆栈指针的16 字节对齐要求(它以8 结尾,因为有八个已用作调用一部分的字节:返回地址本身,以及之前的 ebx)。

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

    【讨论】:

    • 我相信你可能是正确的,这是编译器试图弥补我潜在的愚蠢。我将数组减少到 20,它仍然使用 0x38,我将数组减少到 10,它使用 0x28。这条线似乎是一个由 16 个凸块组成的数组,分配到 0x38
    • @Wjdavis5,如果它只是为了补偿,我希望它会随着你的价值而不是量子价值上下波动。建议您尝试 10 到 20 之间的值,看看它是否曾经给您 0x30。如果不是,则可能是建议的对齐问题,编译器试图为 SSE 等内容保持 16 字节对齐。
    • 堆栈是否必须在 beforeafter call 16 字节对齐?这可能会导致更多的内存使用。
    • 仅供参考,我的测试表明它与 gets 无关,也与编译器将 2 参数 printf 优化为 1 参数 puts 无关。
    • 另外,clang 使用0x28,这与我在上面评论中的解释相匹配。看起来这是一个 gcc 错误/特殊性。
    猜你喜欢
    • 2013-11-08
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 2014-12-11
    • 2011-07-26
    • 2011-09-04
    • 2012-07-30
    相关资源
    最近更新 更多