堆栈上的局部变量位置不变

时间:2016-04-06 18:47:05

标签: c buffer-overflow

我目前正在阅读一本书安全漏洞,并且已经开始讨论基于堆栈的缓冲区溢出。它给出了一个类似于下面的例子。

//overFlowTest.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void main(int argv, char* argv[])
{
    int i = 0;
    char buffer[4];
    strcpy(buffer, argv[1]);
    if(i)
    {
        printf("overwrote i\n");
    }
}

当我使用比为该变量分配的可用空间更长的输入参数编译并运行程序时,我按预期得到以下内容(因为我覆盖了i变量,因为它有堆栈上数字较大的地址(堆栈中较低)而不是&#34;缓冲区&#34;)。

# gcc overFlowTest.c
# ./a.out AAAAA
overwrote buffer
#

但是当我改变局部变量的创建顺序时,我认为它们会以相反的顺序被推入堆栈,缓冲区溢出将无效。

//overFlowTest.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void main(int argv, char* argv[])
{
    char buffer[4];
    int i = 0;
    strcpy(buffer, argv[1]);
    if(i)
    {
        printf("overwrote i\n");
    }
}

但事实并非如此,因为我得到了同样的结果。

# gcc overFlowTest.c
# ./a.out AAAAA
overwrote buffer
#

关于为什么会发生这种情况的任何想法?

1 个答案:

答案 0 :(得分:3)

所以,我在探索这个问题时发现了一些有趣的信息。

一,这个问题不会与clang一起复制。当我编译第二个程序时,我没有看到如下所示的print语句:

$ clang so.c -o so.out
so.c:4:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main(int arg, char* argv[])
^
so.c:4:1: note: change return type to 'int'
void main(int arg, char* argv[])
^~~~
int
1 warning generated.

$ clang so2.c -o so2.out
so2.c:4:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main(int arg, char* argv[])
^
so2.c:4:1: note: change return type to 'int'
void main(int arg, char* argv[])
^~~~
int
1 warning generated.

$ ./so.out AAAAAAAAAAAAAAAAAAA
overwrote i

$ ./so2.out AAAAAAAAAAAAAAAAAAA

$

但是,如果我们对gcc做同样的事情,我们会发现它们都失败了。

$ gcc so.c -o so.exe

$ gcc so2.c -o so2.exe

$ ./so.exe AAAAAAAAAAAAAAAA
overwrote i

$ ./so2.exe AAAAAAAAAAAAAAAA
overwrote i

$

进一步了解一下,让我们看看这些

的程序集
$ gcc so.c -S -masm=intel

$ gcc so2.c -S -masm=intel

$ diff so.s so2.s
1c1
<       .file   "so.c"
---
>       .file   "so2.c"

$

正如你所看到的,这里唯一的区别是文件名(我也测试了所有优化关闭,结果相同)。

现在,让我们尝试使用clang。

$ clang -S -mllvm --x86-asm-syntax=intel so.c
so.c:4:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main(int arg, char* argv[])
^
so.c:4:1: note: change return type to 'int'
void main(int arg, char* argv[])
^~~~
int
1 warning generated.

$ clang -S -mllvm --x86-asm-syntax=intel so2.c
so2.c:4:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main(int arg, char* argv[])
^
so2.c:4:1: note: change return type to 'int'
void main(int arg, char* argv[])
^~~~
int
1 warning generated.

$ diff so.s so2.s
26c26
<       lea     rax, qword ptr [rbp - 24]
---
>       lea     rax, qword ptr [rbp - 20]
29c29
<       mov     dword ptr [rbp - 20], 0
---
>       mov     dword ptr [rbp - 24], 0
34c34
<       cmp     dword ptr [rbp - 20], 0
---
>       cmp     dword ptr [rbp - 24], 0

$

看起来clang会将两个文件编译成不同的版本;这可以防止我被覆盖。

总之,这两个文件产生不同输出的原因是因为代码产生了未定义的行为 - 编译器不受任何标准的约束,并且可以生成最简单的文件。