为什么在C语言中以不同顺序声明的变量在堆栈中保持不变的顺序?它与类型有关吗?

Why variables declared in different order in the C language remain an unchanged order in the stack? Is it type-related?

请原谅我蹩脚的英语:p

当我注意到一些非常奇怪的事情时,我正在做缓冲区溢出编程练习。我初始化了一个 16 字节的字符数组和一个整数变量,并在两个 C 文件中以不同的顺序声明了它们,这两个文件的唯一区别是这两个变量的顺序。然而,在编译源代码之后,当我检查这两个变量在堆栈中的顺序时,我发现这两个不同的可执行文件在堆栈中的顺序在 gdb 中调试时完全相同。 char 指针位于较高地址,整数始终位于较低地址。

int check_authentication(char *password) {
        int testVariable = 2;
        int auth_flag = 0;
        char password_buffer[16];

        ...
}
int check_authentication(char *password) {
        int testVariable = 2;
        char password_buffer[16];
        int auth_flag = 0;
        
        ...
}

为了更直观,我打印了这些变量的地址,这是结果。

***@ubuntuserver:~/ethical/art$ gcc auth_overflow.c -o auth_overflow -m32 -g -fno-stack-protector
***@ubuntuserver:~/ethical/art$ gcc auth_overflow2.c -o auth_overflow2 -m32 -g -fno-stack-protector
***@ubuntuserver:~/ethical/art$ ./auth_overflow AAAA
&testVaraible -> 0xffffd4b4
&auth_flag -> 0xffffd4b8
password_buffer -> 0xffffd4bc

Access Denied.
***@ubuntuserver:~/ethical/art$ ./auth_overflow2 AAAA
&testVaraible -> 0xffffd4a4
password_buffer -> 0xffffd4ac
&auth_flag -> 0xffffd4a8

Access Denied.
***@ubuntuserver:~/ethical/art$ 

testVariable 是另一个变量,我用来测试整数的顺序是否会在堆栈中改变以便忽略它。

谁能告诉我为什么会这样?由于代码的结果与 Hacking: The Art Of Exploitation, 2nd Edition 一书中描述的不同,而且这是一本很老的书,我想也许有人可以告诉我是否有最新版本的 gcc 中有一些我还不知道的新机制。

谢谢。

C 标准不要求实现以任何特定顺序放置对象。

一个好的编译器不关心你把声明放在什么顺序。它会安排对象以方便充分利用内存。由于有些对象需要对齐,典型的安排是从某个对齐位置开始,将对齐要求最严格的对象放在那里,然后将要求逐渐宽松的对象放在它们之后。

在对齐要求并列的情况下,通常没有理由关心对象的顺序。它们可能已被放入树或散列 table 或其他内部编译器结构中,因为声明已被处理,并且它们可以按照它们在遍历该数据结构时碰巧从该数据结构中出现的顺序分配给内存。如果散列或树以标识符(名称)为键,则顺序可能由名称确定(在对齐要求之后),因此对于相同的名称和类型,您可能总是获得相同的对象顺序,而不管声明顺序如何。或者你可能不会,因为编译器可能以不同的方式设计。

对于 pushpopcallret 指令,堆栈仅充当 LIFO 堆栈。 ret 在固定位置找到地址:栈顶。

当堆栈用于数据时,一个区域被隔离(通过直接从 SP 中减去),然后可以随机访问,例如[rbp-0x38]。这里,栈只是一个区域,并不起栈的作用。所以访问顺序无关紧要。

与给变量命名的方式相同,而不是位置。 auth_flag 存储在某个地址,而不是三个中的第二个位置。