堆栈上的局部变量位置不变
Local variable position on stack not changing
我目前正在阅读一本关于安全漏洞的书,并且来到了关于基于堆栈的缓冲区溢出的部分。它给出了一个类似于下面的例子。
//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");
}
}
当我编译 运行 程序时,其输入参数比分配给该变量 "AAAAA" 的可用 space 长,我按预期得到以下结果(因为我覆盖了 i 变量,因为它在堆栈上的地址(在堆栈中较低)比 "buffer").
# 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
#
关于为什么会发生这种情况有什么想法吗?
所以,在探索这个问题的过程中,我发现了一些有趣的信息。
一,这个问题没有用clang复现。当我编译第二个程序时,我没有看到打印语句,如下所示:
$ 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将这两个文件编译成不同的版本;这可以防止 i 被覆盖。
总之,这两个文件产生不同输出的原因是因为代码产生了未定义的行为——编译器不受任何标准的约束,可以产生最简单的结果。
我目前正在阅读一本关于安全漏洞的书,并且来到了关于基于堆栈的缓冲区溢出的部分。它给出了一个类似于下面的例子。
//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");
}
}
当我编译 运行 程序时,其输入参数比分配给该变量 "AAAAA" 的可用 space 长,我按预期得到以下结果(因为我覆盖了 i 变量,因为它在堆栈上的地址(在堆栈中较低)比 "buffer").
# 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
#
关于为什么会发生这种情况有什么想法吗?
所以,在探索这个问题的过程中,我发现了一些有趣的信息。
一,这个问题没有用clang复现。当我编译第二个程序时,我没有看到打印语句,如下所示:
$ 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将这两个文件编译成不同的版本;这可以防止 i 被覆盖。
总之,这两个文件产生不同输出的原因是因为代码产生了未定义的行为——编译器不受任何标准的约束,可以产生最简单的结果。