此问题已经在这里有了答案:
我对如何在堆栈上对局部变量进行排序感到很困惑。我知道,(在Intel x86上)本地变量在代码中存储时从高位到低位地址存储。很明显,这段代码:
int i = 0;
char buffer[4];
strcpy(buffer, "aaaaaaaaaaaaaaa");
printf("%d", i);
产生这样的东西:
1633771873
i
变量被溢出的缓冲区覆盖。
但是,如果我交换前两行:
char buffer[4];
int i = 0;
strcpy(buffer, "aaaaaaaaaaaaaaa");
printf("%d", i);
输出绝对相同。
怎么可能? i
的地址比buffer
的地址低,因此缓冲区溢出应覆盖其他数据,但不能覆盖i
。还是我错过了什么?
关于局部变量的顺序没有规则,因此编译器通常可以自由地按自己喜欢的方式分配它们。但是另一方面,编译器将使用许多策略来减少发生您自愿尝试执行的操作的可能性。
这些安全增强措施之一是分配一个始终与其他标量变量相距甚远的缓冲区,因为可以超出范围地寻址数组,并且更倾向于膨胀相邻变量。另一个技巧是在数组之后添加一些陷阱空白空间,以创建边界问题的隔离。
无论如何,您都可以使用调试器查看程序集以确认变量的位置。
如果要查看编译器如何分配局部变量,请尝试使用gcc -S
进行编译,这将输出汇编代码。在汇编代码上,您可以看到编译器如何选择对变量进行排序。
在编译器选择排序局部变量时要记住的一点是,每个字符仅需要对齐1(这意味着它可以从内存的任何字节开始),另一方面,int必须对齐乘以4(这意味着它只能从被4整除的字节开始),因此,根据对齐方式,编译器在如何避免具有空字节数据方面具有自己的逻辑,这意味着它经常将相似类型的变量组合在一起按一定顺序因此,即使您这样定义它们:
int a;
char c;
int b;
char d;
编译器可能将内存中的int和chars组合在一起,因此从顶部的低端内存到底部的高端内存看起来像是这样:
low memory
| | | char d | char c|
| int b |
| int a |
high memory
每个||代表一个字节,整行代表4个字节。