我用gcc编译了这段代码(gcc -ggdb -mpreferred-stack-boundary = 2 -o demo demo.c)并反编译它以查看程序集(我知道它使用的是不安全的函数,这是用于缓冲区的练习溢出):
#include<stdio.h>
CanNeverExecute()
{
printf("I can never execute\n");
exit(0);
}
GetInput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
main()
{
GetInput();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是GetInput()函数的程序集:
(gdb) disas GetInput
Dump of assembler code for function GetInput:
0x08048432 <+0>: push ebp
0x08048433 <+1>: mov ebp,esp
0x08048435 <+3>: sub esp,0xc
=> 0x08048438 <+6>: lea eax,[ebp-0x8]
0x0804843b <+9>: mov DWORD PTR [esp],eax
0x0804843e <+12>: call 0x8048320 <gets@plt>
0x08048443 <+17>: lea eax,[ebp-0x8]
0x08048446 <+20>: mov DWORD PTR [esp],eax
0x08048449 <+23>: call 0x8048340 <puts@plt>
0x0804844e <+28>: leave
0x0804844f <+29>: ret
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
这是Main()函数的程序集:
(gdb) disas main
Dump of assembler code for function main:
0x08048450 <+0>: push ebp
0x08048451 <+1>: mov ebp,esp
0x08048453 <+3>: call 0x8048432 <GetInput>
0x08048458 <+8>: mov eax,0x0
0x0804845d <+13>: pop ebp
0x0804845e <+14>: ret
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
我在第13行设置断点(获取(缓冲区))
从Main(),我可以看到ebp值被压入堆栈.然后,当调用GetInput()函数时,ret地址也被压入堆栈.进入GetInput函数后,ebp值再次被压入堆栈.现在这是我感到困惑的地方:
0x08048435 <+3>: sub esp,0xc
缓冲区变量只有8个字节,因此应从esp中减去8个字节以允许缓冲区局部变量.
堆栈:
(gdb) x/8xw $esp
0xbffff404: 0x08048360 0x0804847b 0x002c3ff4 0xbffff418
0xbffff414: 0x08048458 0xbffff498 0x00147d36 0x00000001
(gdb) x/x &buffer
0xbffff408: 0x0804847b
Run Code Online (Sandbox Code Playgroud)
0x08048458是ret地址,0xbffff418是ebp的旧值,缓冲区变量的4个字节是0x0804847b,所以我猜其他4个字节是0x002c3ff4.但是堆栈上似乎还有另外4个字节.
所以我的问题是,如果只需要8个字节,为什么减去12个字节呢?额外的4个字节是多少?
谢谢
这是因为
mov DWORD PTR [esp],eax
Run Code Online (Sandbox Code Playgroud)
显然,你puts和gets实现需要的参数被压入堆栈.
价值[ebp-0xc]实际上是[esp]现在,这就是为什么dword提前保留.
为什么会这样?这样做,这样更有效,因为你不必pop和push,但只是移动eax上[esp],这样你就饶了至少一个指令.但是,我想这段代码经历了一些优化,因为这个很聪明.
| 归档时间: |
|
| 查看次数: |
265 次 |
| 最近记录: |