我有以下程序
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 int check_authentication(char *password){
6 char password_buffer[16];
7 int auth_flag =0;
8
9
10 strcpy(password_buffer, password);
11
12 if(strcmp(password_buffer, "brillig" ) == 0 )
13 auth_flag = 1;
14 if(strcmp(password_buffer, "outgrabe") == 0)
15 auth_flag = 1;
16
17 return auth_flag;
18 }
19
20 int main(int argc, char *argv[]){
21 if (argc<2){
22 printf("Usage: %s <password>\n", argv[0]);
23 exit(0);
24 }
25
26 if(check_authentication(argv[1])){
27 printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
28 printf(" Access Granted.\n");
29 printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n");
30 }
31 else {
32 printf("\n Access Denied. \n");
33 }
34 }
我正在运行它通过gdb提供30个字节的As ...我正在设置以下断点
(gdb) break 9
Breakpoint 1 at 0x80484c1: file auth_overflow2.c, line 9.
(gdb) break 16
Breakpoint 2 at 0x804850f: file auth_overflow2.c, line 16.
(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
到目前为止一切顺利。一切都按照它应该进行到下一个断点
Breakpoint 1, check_authentication (password=0xbffff6d2 'A' <repeats 30 times>)
at auth_overflow2.c:10
10 strcpy(password_buffer, password);
(gdb) x/s password_buffer
0xbffff484: "\364\237\374\267\240\205\004\b\250\364\377\277\245", <incomplete sequence \352\267>
(gdb) x/x &auth_flag
0xbffff494: 0x00
现在我们看到以下信息:
变量auth_flag位于地址0xbffff494中,变量缓冲区位于地址0xbffff484中。由于var auth_flag的地址大于缓冲区的地址,并且堆栈朝向较低的地址增长,这意味着缓冲区变量中的额外(缓冲区溢出)字节将不会超过auth_flag。对吗?
但是gdb有不同的意见......
(gdb) cont
Continuing.
Breakpoint 2, check_authentication (
password=0xbf004141 <Address 0xbf004141 out of bounds>)
at auth_overflow2.c:17
17 return auth_flag;
(gdb) x/s password_buffer
0xbffff484: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff494: 0x41
和......
(gdb) x/16xw &auth_flag
0xbffff494: 0x41414141 0x41414141 0x41414141 0xbf004141
0xbffff4a4: 0x00000000 0xbffff528 0xb7e8bbd6 0x00000002
0xbffff4b4: 0xbffff554 0xbffff560 0xb7fe1858 0xbffff510
0xbffff4c4: 0xffffffff 0xb7ffeff4 0x080482bc 0x00000001
我们看到auth_flag被这些0x41(= A)覆盖,尽管这个变量位于堆栈的较低位置。为什么会这样?
答案 0 :(得分:5)
堆栈增长方向与超出缓冲区时额外字节的位置无关。来自strcpy
的超出总是会进入更高的地址(除非到目前为止溢出到地址为0,这是非常不可能的)
答案 1 :(得分:1)
对象存储在内存中,从较低的地址到较高的地址。由于您无法保证参数密码所引用的字符串长度小于16,因此您的代码无效。 实际上在本地缓冲区中没有任何需要password_buffer。 该函数可以按以下方式编写
_Bool check_authentication( const char *password )
{
return ( strcmp( password, "brillig" ) == 0 || strcmp( password, "outgrabe" ) == 0 );
}
您可以在函数实现中使用int类型而不是返回类型_Bool。无论如何,将返回1或0。
答案 2 :(得分:1)
编译器可以自由地重新排序变量堆栈,因此在这种情况下,它始终是int变量之前的char数组。这使得程序容易受到基于堆栈的缓冲区溢出的影响。
为了更改以下内容:
(gdb) x/s password_buffer
0xbffff484: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff494: 0x41
如预期答案如下:
(gdb) x/s password_buffer
0xbffff494: 'A' <repeats 30 times>
(gdb) x/x &auth_flag
0xbffff484: 0x00
我们只需在编译期间添加-fstack-protector-all
参数,结果将如预期一样。反之亦然,也许您可以使用-O0
或-fno-stack-protector
。
答案 3 :(得分:0)
如果您正在读取超过15个字节,那么您将获得该字节。 strcpy将查找字符串的结尾。您可以使用strncpy之类的东西来复制有限数量的字符。