我有这个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
+返回地址)或其他什么,我错过了什么?
答案 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?;))在内存中重要的四字线;前瞻思维等等。
编辑:完全正确! (他说的......)