我正在拆解这个基本的C代码,试图找出哪些操作 在堆栈上完成。我在一个虚拟机,32位,gcc 4.4.3,ubuntu基础上做它 发行。我用这个标志编译了代码。
gcc -ggdb -mpreferred-stack-boundary = 2 -fno-stack-protector -o ExploitMe ExploitMe.c
#include<stdio.h>
#include<string.h>
main(int argc, char **argv)
{
char buffer[80];
strcpy(buffer, argv[1]);
return 1;
}
问题是我无法弄清楚为什么在操作3,堆栈 指针移动0x58,字符长度为80个字符,不应该是0x50吗?
dump of assembler code for function main:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
=> 0x080483e7 <+3>: sub $0x58,%esp
0x080483ea <+6>: mov 0xc(%ebp),%eax
0x080483ed <+9>: add $0x4,%eax
0x080483f0 <+12>:mov (%eax),%eax
0x080483f2 <+14>:mov %eax,0x4(%esp)
0x080483f6 <+18>:lea -0x50(%ebp),%eax
0x080483f9 <+21>:mov %eax,(%esp)
0x080483fc <+24>:call 0x804831c <strcpy@plt>
0x08048401 <+29>:mov $0x1,%eax
0x08048406 <+34>:leave
0x08048407 <+35>:ret
End of assembler dump.
我坚持下去,我后来看到这是采取了执行的长度,但是什么 这些操作之间的程序是什么?¿
0x080483f6 <+18>:lea -0x50(%ebp),%eax
谢谢
答案 0 :(得分:1)
编译器可以自由安排堆栈,但它认为合适。
答案 1 :(得分:1)
其他8个字节用于strcpy的参数。编译器已经意识到它可以简单地从堆栈指针中减去额外的8个字节,然后将寄存器存储到存储器中,而不是将它们推送到堆栈。这意味着堆栈指针只需要调整一次。
答案 2 :(得分:1)
它可能会分配更多的位置来存储传入的参数(argv,argc)。和/或它需要更多本地存储。编译器会执行任何他们想要实现的高级代码,相同的代码将生成数十个/数百个不同的汇编语言序列,具体取决于编译器,版本和优化设置以及编译器本身编译时的配置/构建设置。
您经常会看到这种堆栈帧,通常是由于性能和指令集功能/限制的组合。如果你移动堆栈指针一次或者用另一个寄存器复制它,就更容易编码和调试,在函数中,所有函数都被引用到一个静态点,而函数的预先调用,调用和清理会混淆真实的堆栈指针。
您经常会看到堆栈框架为传入的参数和其他局部变量留出空间,即使优化已经消除了这些变量实际花费在堆栈上的任何时间的需要。预先确定对堆栈帧和大小的需求并且稍后进行优化,并且编译器并不总是返回并且意识到如果它再次对该函数进行传递,则可以使堆栈帧更小。同样,编译器编写器可以更容易地调试,如果他们知道他们的堆栈帧总是以传入的参数开始,然后按顺序启动局部变量,非常快速且易于读取和调试代码,这只是一个例子。
虽然底线是Oli的答案,但只要它实现你的代码,编译器就可以做任何想做的事情。我对此的扩展是来自相同高级代码的输出根据编译器和选项的不同而有很大差异。它很少被完美地优化。