2

我有这个 C 函数:

void hello(char * src) {
  char buffer[64];
  strcpy(buffer, src);
  printf("Hello %s !\n", buffer);
}

(其中包含我知道的安全问题)

它的 x86 程序集是

push   ebp
mov    ebp,esp
sub    esp,0x58

为什么是 0x58(即 88)?我期望 64 + 4 + 4 + 4 (局部变量缓冲区 + 参数 + 旧ebp+ 返回地址)或其他东西,我错过了什么?

4

3 回答 3

4

这在很大程度上取决于您的体系结构和编译器标志,因此不可能在这里指向单一事物并说“这一定是它”。不过,我可以给你一些建议,你可能会觉得有帮助。

首先,考虑堆栈边界。您可能听说过-mpreferred-stack-boundary=XGCC 的标志。如果不是,它基本上告诉你的编译器更喜欢你在堆栈上的值是每个 2 X字节。然后,您的编译器将尝试优化您的程序,以使这些值尽可能适合堆栈。另一方面,诸如 GCC 修饰符__packed__将使编译器尝试将数据尽可能紧密地放入堆栈中。

还有堆栈保护器。基本上,GCC 会在堆栈上放置虚拟值,以确保缓冲区溢出除了对您的程序造成段错误外不会造成任何伤害(这并不有趣,但比攻击者控制指令指针更好)。您可以轻松地尝试一下:使用任何最新版本的 GCC 并让用户溢出缓冲区。您会注意到程序退出时会显示一条消息,即“检测到堆栈粉碎,已终止”。尝试使用 编译程序-fno-stack-protector,堆栈上分配的本地内存可能会更小。

最后,关于 cdecl 调用约定如何工作的一些小细节,您会弄错。参数在调用函数之前被压入堆栈,这意味着它们在堆栈中的内存更高(请记住,堆栈在内存中向下增长)。这是一个极其简化的函数示例,它需要 3 个参数并分配 2 个局部整数变量:

# First we push three arguments on the stack in reverse order as they 
# appear in C. The values don't matter here.
pushl $0xc
pushl $0xb
pushl $0xa

# A CALL instruction comes in here to get in the function. The return 
# address is placed on the stack.

# Assume we are in the function now. This function first saves the base 
# pointer, then sets the base pointer to the address in the stack pointer.
pushl %ebp
movl %esp, %ebp

# Now we can allocate our local variables. We need 8 bytes of space for 
# those 2 integer variables (note that this is an extremely simplified 
# example that doesn't consider what I just told you above).
subl $0x8, %esp
# Let's just put 1 and 2 in those variables.
movl $0x1, -4(%ebp)
movl $0x2, -8(%ebp)

# We're done. Put a return value in EAX, then restore the stack- and 
# base pointers.
movl $0x0, %eax
movl %ebp, %esp
popl %ebp
ret

所以基本上,我们的堆栈看起来有点像这样:

16(%ebp)     -> Argument 3
12(%ebp)     -> Argument 2
8(%ebp)      -> Argument 1
4(%ebp)      -> Return address
%ebp         -> Old %ebp pushed on the stack by function
-4(%ebp)     -> Local variable 1
-8(%ebp)     -> Local variable 2

换句话说,只有局部变量在比基指针更低的内存中。老实说,可能还有一些其他因素会影响堆栈上局部变量的大小,我忘记包括在内,但我希望这对您有所帮助。继续修改你的程序,你会弄明白的。:)

于 2013-11-01T22:52:04.140 回答
2

编译器保留更多空间的原因有很多。除了其他人所说的,如果你在 MSVC 上,也许这就是编辑和继续功能。没有编译器名称和编译选项,我不能告诉你更多

于 2014-06-20T01:59:23.037 回答
0

好吧,这是一个疯狂的猜测,但让我们把它跑到旗杆上,看看会发生什么。

也许编译器没有针对空间进行优化,这是一个字对齐调整,以节省移位字以跨四字边界加载寄存器。

查看值,0x58 & 8bytes -> 下一个四字边界 96 0x60。从内存中最不重要的四字线中弹出 ebp 更容易(或者它再次是字节序?;) )显着的四字线;前瞻性思维等等。

编辑:正是!(他说的……)

于 2013-11-01T23:15:24.720 回答