以下信息来自于我在Ubuntu-14.04系统上使用gcc版本4.8.4作为编译器和gdb版本7.7.1作为调试器运行的结果。
首先,缓冲区溢出是由strcpy函数引起的,如果你溢出了buf
,使其覆盖了auth
的内存位置,但接下来的if-else块将覆盖你的更改。
其次,您可以通过在调试器中查看堆栈来了解发生了什么。我对您的代码进行了轻微修改,通过将auth
初始化为0xbbbbbbbb
(只是为了找到auth在堆栈上的位置)。
在main函数上设置断点并进入函数,我们可以检查各个寄存器的值:
(gdb) info reg
rax 0x0 0
rbx 0x0 0
rcx 0x0 0
rdx 0x7fffffffdf30 140737488346928
rsi 0x7fffffffdf18 140737488346904
rdi 0x2 2
rbp 0x7fffffffde30 0x7fffffffde30
rsp 0x7fffffffddf0 0x7fffffffddf0
[... some lines removed ...]
rip 0x400652 0x400652 <main+37>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
从这里我们可以看到栈从0x7fffffffddf0
延伸到0x7fffffffde30
。现在,在调用strcpy之前停止,我们可以查看堆栈:
(gdb) x/76xb $rsp
0x7fffffffddf0: 0x18 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffddf8: 0x1d 0x07 0x40 0x00 0x02 0x00 0x00 0x00
0x7fffffffde00: 0x30 0xde 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde08: 0x00 0x00 0x00 0x00 0xbb 0xbb 0xbb 0xbb
0x7fffffffde10: 0xd0 0x06 0x40 0x00 0x00 0x00 0x00 0x00
0x7fffffffde18: 0x40 0x05 0x40 0x00 0x00 0x00 0x00 0x00
0x7fffffffde20: 0x10 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde28: 0x00 0x2b 0x25 0x07 0xdd 0x7a 0xc0 0x6d
0x7fffffffde30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffde38: 0x45 0x6f 0xa3 0xf7
从这里可以看出,auth
位于内存地址0x7fffffffde0c
。
我将passwordAAAAAAAA111
设置为命令行参数,现在我们可以单步执行strcpy调用并再次查看内存:
(gdb) x/76xb $rsp
0x7fffffffddf0: 0x18 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffddf8: 0x1d 0x07 0x40 0x00 0x02 0x00 0x00 0x00
0x7fffffffde00: 0x30 0xde 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde08: 0x00 0x00 0x00 0x00 0xbb 0xbb 0xbb 0xbb
0x7fffffffde10: 0x70 0x61 0x73 0x73 0x77 0x6f 0x72 0x64
0x7fffffffde18: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffde20: 0x31 0x31 0x31 0x31 0x00 0x7f 0x00 0x00
0x7fffffffde28: 0x00 0x2b 0x25 0x07 0xdd 0x7a 0xc0 0x6d
0x7fffffffde30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffde38: 0x45 0x6f 0xa3 0xf7
(gdb)
从这里,我们可以看出auth
的值没有被改变(注意从0x7fffffffde0c开始仍存在四个0xbb)。同时,我们现在可以看到密码存储在内存中的位置,它从0x7fffffffde10
开始。我使用的四个'A'代表了四个0x41,而我使用的四个'1'则代表了四个0x31。
所以,在我的系统上,我看不到有任何可能会溢出到auth
变量的方法。
最后,你提出的问题,请记住命令行参数被视为字符数组,因此在命令行传递类似于AAAA1
的东西将导致数组[0x41 0x41 0x41 0x41 0x31]被传递给你的程序。你想让你的程序接收的实际上是[0x41 0x41 0x41 0x41 0x01 0x00 0x00 0x00](假设32位,小端架构)。你将面临两个问题:
1. 0x01是一个不可打印字符
2. 0x00作为空终止符将在第一个null处停止字符串输入。
对于问题2,只使用简单输入并没有太多可做的; 然而,正如其他人建议的那样,解决问题1的方法是创建一个驱动程序来构建输入缓冲区,然后将其传递给程序。
printf("%x %x\n",&buffer,&auth);
,但是输出的结果是28feb0 28feac
,看来是因为填充优化所导致的。在堆栈中变量的顺序不能保证与声明的顺序相同。 - Jean-François Fabre"%x"
打印指针,而应该使用"%p"
。此外,指针必须强制转换为void *
类型。不使用"%p"
来打印(void *
)指针是未定义行为。 - Some programmer dude