在这个程序中解释esp-ebp

时间:2013-11-01 22:10:35

标签: c assembly x86 cpu-registers

我有这个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 +返回地址)或其他什么,我错过了什么?

3 个答案:

答案 0 :(得分:3)

这在很大程度上取决于你的体系结构和编译器标志,所以不可能指向一个单一的东西并在这里说“这一定是它”。但是,我可以给你一些你可能会发现有帮助的建议。

首先,考虑堆栈边界。您可能听说过GCC的-mpreferred-stack-boundary = X标志。如果没有,它基本上告诉编译器更喜欢堆栈上的值为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

换句话说,只有局部变量位于比基本指针低的内存中。老实说,可能还有一些事情可以影响我忘记包含的堆栈上的局部变量的大小,但我希望这可以帮助你解决一些问题。继续浏览您的程序,你会弄明白的。 :)

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

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

编辑:完全正确! (他说的......)