【发布时间】:2020-05-11 09:50:45
【问题描述】:
我有以下代码来展示基于堆栈的缓冲区溢出。
int check_authentication(char *password) {
int auth_flag = 0;
char password_buffer[16];
strcpy(password_buffer, password);
if(strcmp(password_buffer, "Admin") == 0)
auth_flag = 1;
return auth_flag;
}
在这里,当用户输入任何长度大于 16 的字符串时,将允许访问。为了显示其他不溢出 auth_flag 的情况,我有以下代码:
int check_authentication(char *password) {
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer, password);
if(strcmp(password_buffer, "Admin") == 0)
auth_flag = 1;
return auth_flag;
}
由于堆栈作为 LIFO 工作,auth_flag 的地址应该低于第二个示例中的password_buffer。断点位于strcpy 的 GDB 如下所示:
(gdb) x/16xw password_buffer
0x61fefc: 0x696d6441 0x7659006e 0xc9da078f 0xfffffffe
0x61ff0c: 0x00000001 0x76596cad 0x00401990 0x0061ff38
0x61ff1c: 0x00401497 0x00ae1658 0x00000000 0x0028f000
0x61ff2c: 0x00400080 0x0061ff1c 0x0028f000 0x0061ff94
(gdb) x/x &auth_flag
0x61ff0c: 0x00000001
我预计password_buffer 将从0x61ff10 开始,紧跟在auth_flag 之后。我哪里错了?
我在 Windows 10 上使用 gcc(gcc 版本 9.2.0(MinGW.org GCC Build-20200227-1)和 gdb(GNU gdb (GDB) 7.6.1),未修改 SEHOP 或 ASLR。
【问题讨论】:
-
我不知道 GCC 如何将局部变量映射到堆栈位置的细节,但您所指的 LIFO 行为通常是指推送和弹出,而这不是局部变量的存储方式。在那里,编译器通常会计算在入口处保留的单个内存块。我知道的大多数编译器都按照声明的顺序密切分配本地变量,从堆栈上的较高地址到较低地址,但我认为
c的标准中没有指定一种或另一种方式。 -
您的意思可能是“分配”而不是“分配”。您需要注意,局部变量不会单独或按给定顺序推送到堆栈上。相反,所需的内存量只是在函数入口的堆栈上分配。如何在该内存区域中对变量进行排序取决于编译器。
-
如果你想保证一个东西在内存中物理上跟随另一个,把它们放在同一个包含结构中。如果它们在函数中独立声明,那么它们可以按任何物理顺序分配,并且它们之间可能会出现其他东西。事实上,如果编译器优化了你的代码,那么
auth_flag可能会保存在一个寄存器中,甚至没有内存地址。结构成员也可能发生同样的情况。 -
如果堆栈中存在
auth_flag,我确实会感到惊讶。根据架构/调用约定,它可能只是将结果放在eax/r0寄存器或类似的东西中。正如您可以see here 一样,从源代码中删除标志会生成相同的指令。
标签: c memory gdb allocation buffer-overflow