基本 C 程序的堆栈操作
我正在反汇编这个基本的 C 代码,试图找出哪些操作 都是在堆栈上完成的。我在 vm、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
谢谢
Im disassembling this basic C code, trying to figure out what operations
are done on the stack. Im doing in it on a vm, 32 bit, gcc 4.4.3, ubuntu based
distro. I compiled the code with this flags.
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;
}
The problems is that i cannot figure out why on operation 3, the stack
pointer is moved 0x58, the char is 80 characters long, shouldnt it be 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.
Im stuck on it, i see later that is taking the exected lenght but what
is the program making between those ops ?¿
0x080483f6 <+18>:lea -0x50(%ebp),%eax
Thank you
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
编译器可以自由地安排堆栈,但它认为合适。
The compiler is free to arrange the stack however it sees fit.
其他 8 个字节用于 strcpy 的参数。编译器已经意识到,它可以简单地从堆栈指针中减去额外的 8 个字节,然后将寄存器存储到内存中,而不是将它们推入堆栈。这意味着堆栈指针只需调整一次。
The other 8 bytes are for the arguments to strcpy. Rather than push them on to the stack, the compiler has realised that it can simply subtract an extra 8 bytes from the stack pointer and then store the registers to memory. This means that the stack pointer only has to be adjusted once.
它可能会分配更多位置来存储传入的参数(argv、argc)。和/或它需要更多的本地存储。编译器会做任何他们想要实现的高级代码,相同的代码将产生数十/数百个不同的汇编语言序列,具体取决于编译器、版本和优化设置以及编译器本身编译时的配置/构建设置。
不过,您经常会看到这种堆栈帧,通常是由于性能和指令集功能/限制的组合。如果您移动堆栈指针一次或使用另一个寄存器对其进行复制,则编码和调试会更容易,在函数内,所有内容都引用一个静态点,而函数的准备、调用和清理会与真正的堆栈指针混淆。
您经常还会看到堆栈帧为传入的参数和其他局部变量留出了空间,即使优化已经消除了这些变量实际在堆栈上花费任何时间的需要。预先确定对堆栈帧的需求和大小,然后进行优化,并且编译器并不总是返回并意识到如果它对函数进行另一次传递,它可以使堆栈帧更小。同样,如果编译器编写者知道其堆栈帧始终以传入参数开始,然后按顺序局部变量开始,则编译器编写者可以更轻松地进行调试,非常快速且易于读取和调试代码,这只是一个示例。
Oli 的答案是,底线是,只要实现你的代码,编译器就可以做任何它想做的事情。我对此的扩展是,相同的高级代码的输出根据编译器和选项的不同而有很大差异。而且它很少是完美优化的。
it is probably allocating a couple more locations for storing the passed in parameters (argv, argc). and/or it needs some more local storage. Compilers do whatever they want to implement the high level code, the same code will produce dozens/hundreds of different assembly langauge sequences depending on the compiler, version, and optimization settings as well as configure/build settings when the compiler itself was compiled.
You often see this sort of a stack frame though and usually due to a combination of performance and instruction set features/limitations. Much easier to code and debug if you move the stack pointer once or make a copy of it with another register, within the function everything is referenced to one static point while the prepparing, calling, and cleaning up of functions messes with the real stack pointer.
You will often also see that the stack frame leaves room for the passed in parameters and other local variables even if optimization has removed the need for those variables to actually spend any time on the stack. Up front the need for a stack frame and size is determined and optimization comes later and the compiler doesnt always go back and realize that if it makes another pass on the function it can make the stack frame smaller. Likewise the compiler writer can more easily debug if they know that their stack frame always starts with passed in parameters then the local variables in order, very fast and easy to read and debug the code, just an example.
Bottom line though is Oli's answer, the compiler can do whatever it wants so long as it implements your code. My extension to that is the output from the same high level code varies widely depending on the compiler and options. And it is rarely perfectly optimized.