GCC保留的空间比局部变量所需的空间多

时间:2015-12-06 16:50:38

标签: buffer-overflow memory assembly gcc

我试图了解缓冲区溢出是如何工作的,所以我正在研究各种简单的例子,涉及C和函数gets()和puts()。这些程序的源代码如下:

#include<stdio.h>
GetInput()
{
    char buffer[8];

    gets(buffer);
    puts(buffer);
}

main();
{
    GetInput();

    exit 0;
}

我正在使用以下行编译它:

gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack demo.c -mpreferred-stack-boundary=2 -g -o demo

GCC版本是4.4.3,32位系统和内核2.6.32

调用GetInput()时,应将main()的返回地址压入de stack,然后存储先前的EBP记录,然后为本地var buffer 分配8个字节,所以为了覆盖RET地址,我应该输入12个字节和预期的RET地址。

但情况并非如此,当我将其加载到GDB中并解散GetInput()时,它会说:

0x080483f4 <+0>:    push   %ebp
0x080483f5 <+1>:    mov    %esp,%ebp
0x080483f7 <+3>:    sub    $0xc,%esp    <------- 
0x080483fa <+6>:    lea    -0x8(%ebp),%eax
0x080483fd <+9>:    mov    %eax,(%esp)
0x08048400 <+12>:   call   0x804830c <gets@plt>
0x08048405 <+17>:   lea    -0x8(%ebp),%eax
0x08048408 <+20>:   mov    %eax,(%esp)
0x0804840b <+23>:   call   0x804832c <puts@plt>
0x08048410 <+28>:   leave  
0x08048411 <+29>:   ret

我已经标记了保留12个字节而不是8个字节的行。

任何人都可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

我尝试在https://gcc.godbolt.org/中使用不同版本的GCC编译代码。

gcc 4.4.7
and
gcc 4.8.2
GetInput():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $12, %esp
        leal    -8(%ebp), %eax
        movl    %eax, (%esp) <---------
        call    gets
        leal    -8(%ebp), %eax
        movl    %eax, (%esp)
        call    puts
        leave
        ret

gcc 4.9.0
GetInput():
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        leal    -8(%ebp), %eax
        pushl   %eax <---------------
        call    gets
        addl    $4, %esp
        leal    -8(%ebp), %eax
        pushl   %eax
        call    puts
        addl    $4, %esp
        leave
        ret

注意缓冲区的地址如何传递给gets(),在GCC 4.4.7和4.8.2中,编译器减去12个字节,地址直接写入堆栈顶部。而对于GCC 4.9.0,只减去了8个字节,需要额外的PUSH。所以,是的,看起来额外的4个字节是缓冲区的地址。