还是使用前面缓冲区溢出的那个程序,使用-fstack-protector编译选项。
gcc -O1 -m32-fstack-protector-o bufovf bufovf.c
对映像文件进行反汇编:
objdump -d bufovf
得到如下结果:
bufovf: file format elf32-i386
……
08048524 <getline>:
8048524: 55 push %ebp
8048525: 89e5 mov %esp,%ebp
8048527: 83ec 28 sub $0x28,%esp
804852a: 895d f4 mov %ebx,0xfffffff4(%ebp)
804852d: 89 75 f8 mov %esi,0xfffffff8(%ebp)
8048530: 897d fc mov %edi,0xfffffffc(%ebp)
8048533: 65 a1 14 00 00 00 mov %gs:0x14,%eax//gs段地址偏移20的内存中值作为哨兵值装入eax
8048539: 8945 f0 mov %eax,0xfffffff0(%ebp)//ebp-16=eax,保存哨兵值到栈中
804853c: 31c0 xor %eax,%eax//eax=0
804853e: 8d75 e8 lea 0xffffffe8(%ebp),%esi
8048541: 8934 24 mov %esi,(%esp)
8048544: e85f fe ff ff call 80483a8<gets@plt>
8048549: 89f7 mov %esi,%edi
804854b: fc cld
804854c: b9ff ff ff ff mov $0xffffffff,%ecx
8048551: b800 00 00 00 mov $0x0,%eax
8048556: f2ae repnz scas %es:(%edi),%al
8048558: f7d1 not %ecx
804855a: 83e9 01 sub $0x1,%ecx
804855d: 890c 24 mov %ecx,(%esp)
8048560: e873 fe ff ff call 80483d8<malloc@plt>
8048565: 89c3 mov %eax,%ebx
8048567: 8974 24 04 mov %esi,0x4(%esp)
804856b: 8904 24 mov %eax,(%esp)
804856e: e891 ff ff ff call 8048504<strcpy>
8048573: 89d8 mov %ebx,%eax
8048575: 8b 55 f0 mov 0xfffffff0(%ebp),%edx//edx=ebp-16
8048578: 6533 15 14 00 00 00 xor %gs:0x14,%edx//与哨兵值进行异或运算
804857f: 7405 je 8048586<getline+0x62>//等于0表示栈没有破坏,跳转到清理栈代码,继续运行
8048581: e862 fe ff ff call 80483e8<__stack_chk_fail@plt>//如果栈被破坏,则执行栈检查失败处理函数。
8048586: 8b5d f4 mov 0xfffffff4(%ebp),%ebx
8048589: 8b75 f8 mov 0xfffffff8(%ebp),%esi
804858c: 8b7d fc mov 0xfffffffc(%ebp),%edi
804858f: 89ec mov %ebp,%esp
8048591: 5d pop %ebp
8048592: c3 ret
……
[root@bogon asm]# ./bufovf
Input>12345678
12345678
[root@bogon asm]# ./bufovf
Input>123456789
*** stack smashing detected ***: ./bufovfterminated
Aborted
当字符数达到9个时,程序检查到了栈被破坏,直接异常退出。按理说,当字符数达到8个时,栈就会被破坏(加上一个结尾0字符),没有被破坏,只能说明哨兵值的低位字节恰好是0。
做一个实验检查一下哨兵值:
修改一下getline函数:
char *getline()
{
char buf[8];
char *result;
gets(buf);
result = malloc(strlen(buf));
strcpy(result,buf);
sleep(-1);
return(result);
}
[root@bogon asm]# ./bufovf
Input>1234567
gdb att 6355
(gdb) bt
#0 0x00494402 in __kernel_vsyscall ()
#1 0x00e91fd0 in __nanosleep_nocancel () from /lib/libc.so.6
#2 0x00e91e1f in sleep () from /lib/libc.so.6
#3 0x080485af in getline ()
#4 0x080485f1 in main ()
(gdb) info frame 3
Stack frame at 0xbff9c630:
eip= 0x80485af in getline; saved eip 0x80485f1
called by frame at 0xbff9c640, caller of frameat 0xbff9c600
Arglist at 0xbff9c628, args:
Locals at 0xbff9c628, Previous frame's sp is0xbff9c630
Saved registers:
ebpat 0xbff9c628, eip at 0xbff9c62c
(gdb) x /20x 0xbff9c610
0xbff9c610: 0x34333231 0x00373635 0x0c3fd200 0x00f55ff4
0xbff9c620: 0x001d0ca0 0x00000000 0xbff9c638 0x080485f1
0xbff9c630: 0x080486e0 0xbff9c650 0xbff9c6a8 0x00e15e9c
0xbff9c640: 0x001d0ca0 0x08048620 0xbff9c6a8 0x00e15e9c
哨兵值的低位1个字节果然是0(小头排序)。