前面说过,gcc4以上版本会对缓冲区溢出做出保护,那么这篇文章旨在使大家明白gcc到底是怎么做保护的。
我们在《缓冲区溢出系列二之缓冲区溢出实例详解》中提到过,用gcc4以上版本编译的时候,可以用选项-fno-stack-protector来关闭对缓冲区溢出的保护,那么我们现在就先来对比一下没做保护和做了保护到底有什么区别。
xulei@xulei-desktop:~/bufferoverflow$ gcc -o noprotect p.c -fno-stack-protect
xulei@xulei-desktop:~/bufferoverflow$ gcc -o protect p.c
分别得到了不带保护的目标文件noprotect和带保护的目标文件protect。
然后用gdb反汇编一下,看看他们的汇编代码到底有什么不同
无缓冲区溢出保护的情况下:
xulei@xulei-desktop:~/bufferoverflow$ gdb noprotect (gdb) disas main Dump of assembler code for function main: 0x08048421 : lea 0x4(%esp),%ecx 0x08048425 : and $0xfffffff0,%esp 0x08048428 : pushl -0x4(%ecx) 0x0804842b : push %ebp 0x0804842c : mov %esp,%ebp 0x0804842e : push %ecx 0x0804842f : sub $0x14,%esp 0x08048432 : mov %ecx,-0xc(%ebp) 0x08048435 : mov -0xc(%ebp),%eax 0x08048438 : cmpl $0x2,(%eax) 0x0804843b : jne 0x8048452 0x0804843d : mov -0xc(%ebp),%edx 0x08048440 : mov 0x4(%edx),%eax 0x08048443 : add $0x4,%eax 0x08048446 : mov (%eax),%eax 0x08048448 : mov %eax,(%esp) 0x0804844b : call 0x80483f4 0x08048450 : jmp 0x804846a 0x08048452 : mov -0xc(%ebp),%edx 0x08048455 : mov 0x4(%edx),%eax 0x08048458 : mov (%eax),%eax 0x0804845a : mov %eax,0x4(%esp) 0x0804845e : movl $0x804854b,(%esp) 0x08048465 : call 0x804832c 0x0804846a : add $0x14,%esp 0x0804846d : pop %ecx 0x0804846e : pop %ebp 0x0804846f : lea -0x4(%ecx),%esp 0x08048472 : ret End of assembler dump. (gdb) disas vulFunc Dump of assembler code for function vulFunc: 0x080483f4 : push %ebp 0x080483f5 : mov %esp,%ebp 0x080483f7 : sub $0x18,%esp 0x080483fa : mov 0x8(%ebp),%eax 0x080483fd : mov %eax,0x4(%esp) 0x08048401 : lea -0xa(%ebp),%eax 0x08048404 : mov %eax,(%esp) 0x08048407 : call 0x804831c 0x0804840c : lea -0xa(%ebp),%eax 0x0804840f : mov %eax,0x4(%esp) 0x08048413 : movl $0x8048540,(%esp) 0x0804841a : call 0x804832c 0x0804841f : leave 0x08048420 : ret End of assembler dump. (gdb) |
有缓冲区溢出保护的情况下:
xulei@xulei-desktop:~/bufferoverflow$ gdb protect (gdb) disas main Dump of assembler code for function main: 0x080484a3 : lea 0x4(%esp),%ecx 0x080484a7 : and $0xfffffff0,%esp 0x080484aa : pushl -0x4(%ecx) 0x080484ad : push %ebp 0x080484ae : mov %esp,%ebp 0x080484b0 : push %ecx 0x080484b1 : sub $0x14,%esp 0x080484b4 : mov %ecx,-0xc(%ebp) 0x080484b7 : mov -0xc(%ebp),%eax 0x080484ba : cmpl $0x2,(%eax) 0x080484bd : jne 0x80484d4 0x080484bf : mov -0xc(%ebp),%edx 0x080484c2 : mov 0x4(%edx),%eax 0x080484c5 : add $0x4,%eax 0x080484c8 : mov (%eax),%eax 0x080484ca : mov %eax,(%esp) 0x080484cd : call 0x8048454 0x080484d2 : jmp 0x80484ec 0x080484d4 : mov -0xc(%ebp),%edx 0x080484d7 : mov 0x4(%edx),%eax 0x080484da : mov (%eax),%eax 0x080484dc : mov %eax,0x4(%esp) 0x080484e0 : movl $0x80485cb,(%esp) 0x080484e7 : call 0x8048374 0x080484ec : add $0x14,%esp 0x080484ef : pop %ecx 0x080484f0 : pop %ebp 0x080484f1 : lea -0x4(%ecx),%esp 0x080484f4 : ret End of assembler dump. (gdb) disas vulFunc Dump of assembler code for function vulFunc: 0x08048454 : push %ebp 0x08048455 : mov %esp,%ebp 0x08048457 : sub $0x28,%esp 0x0804845a : mov 0x8(%ebp),%eax 0x0804845d : mov %eax,-0x14(%ebp) 0x08048460 : mov %gs:0x14,%eax 0x08048466 : mov %eax,-0x4(%ebp) 0x08048469 : xor %eax,%eax 0x0804846b : mov -0x14(%ebp),%eax 0x0804846e : mov %eax,0x4(%esp) 0x08048472 : lea -0xe(%ebp),%eax 0x08048475 : mov %eax,(%esp) 0x08048478 : call 0x8048364 0x0804847d : lea -0xe(%ebp),%eax 0x08048480 : mov %eax,0x4(%esp) 0x08048484 : movl $0x80485c0,(%esp) 0x0804848b : call 0x8048374 0x08048490 : mov -0x4(%ebp),%eax 0x08048493 : xor %gs:0x14,%eax 0x0804849a : je 0x80484a1 0x0804849c : call 0x8048384 <__stack_chk_fail@plt> 0x080484a1 : leave 0x080484a2 : ret End of assembler dump.
|
通过对比发现main函数反汇编以后都一样,不同的是vulFun函数反汇编以后,加保护的多了几条语句(上面用红色标出的即是)。
那么这几条语句就是gcc4以上版本对缓冲区溢出攻击做出了保护了,找到我们要研究问题的对象了,那么下来再针对这几条汇编代码进行研究,也就不难办到了。
稍微解释一下以上红色的那几行代码
0x0804845d : mov %eax,-0x14(%ebp)
0x08048460 : mov %gs:0x14,%eax
0x08048466 : mov %eax,-0x4(%ebp)
0x08048469 : xor %eax,%eax
0x0804846b : mov -0x14(%ebp),%eax
首先,将eax的内容暂时放入ebp-0x14这个内存地址处
然后下面两句是将%gs:0x14的内容放入ebp-0x4这个内存地址处
最后两句是再将原eax的值(暂存在ebp-0x14中的值)恢复。
可以看出加保护以后,只是在将eax压栈之间,先压入%gs:0x14的内容。
那么%gs:0x14里面是什么东西呢?
gs寄存器是32位x86的6个段寄存器(ES、CS、SS、DS、FS和GS)中的一个,大家可能比较熟悉16位的x86下的4个段寄存器(ES、CS、SS、DS),32位的多了两个段寄存器FS和GS。
经查阅资料,gcc4以上版本对缓冲区溢出的保护机制是这样的:在调用strcpy等一系列需要传递字符串参数的函数时,在压入传递进来的字符串参数前,先压入一个随即数(这里即%gs:0x14),然后在函数返回时再检测一下%gs:0x14和堆栈中原先压入的值是不是一致的,如果不一致,那么肯定有缓冲区溢出攻击。
由于这个随即值在栈中的位置是位于返回地址和传递的字符串参数之间的,所以要利用缓冲区溢出攻击来覆盖返回地址,那么就肯定也覆盖了这个随即值。
来看看vulFunc函数调用返回时的代码吧。
0x08048490 : mov -0x4(%ebp),%eax
0x08048493 : xor %gs:0x14,%eax
0x0804849a : je 0x80484a1
0x0804849c : call 0x8048384 <>
果然,先将以前压栈的随即值和gs寄存器里的进行比较,如果不相同,则调用stack_chk_fail,那么如果到这一步,程序基本上也就崩了,缓冲区溢出的美梦也就在瞬间破灭了。
以上是在理论上讲解了一下gcc4以上版本对缓冲区溢出攻击做出的保护,下面,具体用gdb跟踪一下,看一看内存里的具体情况。
我们直接调到strcpy函数之后,来看看内存里的情况
(gdb) b *0x0804847d
Breakpoint 2 at 0x804847d
(gdb) c
Continuing.
Breakpoint 2, 0x0804847d in vulFunc ()
(gdb) i r
eax 0xbffff4ca -1073744694
ecx 0xbffff4c9 -1073744695
edx 0x9 9
ebx 0xb7fcbff4 -1208172556
esp 0xbffff4b0 0xbffff4b0
ebp 0xbffff4d8 0xbffff4d8
esi 0x8048510 134513936
edi 0x80483a0 134513568
eip 0x804847d 0x804847d
eflags 0x200246 [ PF ZF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
可以看到这时候ebp是 0xbffff4d8,而从源代码
0x08048460 : mov %gs:0x14,%eax
0x08048466 : mov %eax,-0x4(%ebp)
中可以看出,%gs:0x14是存放在了内存地址ebp-0x4(=0xbffff4d4)中了,所以, 0xbffff4d4中就是%gs-0x14的内容了,看看内存
(gdb) x/40x $esp
0xbffff4b0: 0xbffff4ca 0xbffff706 0x00000000 0x00000000
0xbffff4c0: 0x00000000 0xbffff706 0x4141f6e3 0x41414141
0xbffff4d0: 0xb7004141 0x03133000 0xbffff4f8 0x080484d2
0xbffff4e0: 0xbffff706 0x08049ff4 0xbffff508 0xbffff510
0xbffff4f0: 0xb7ff0f50 0xbffff510 0xbffff568 0xb7e88685
0xbffff500: 0x08048510 0x080483a0 0xbffff568 0xb7e88685
0xbffff510: 0x00000002 0xbffff594 0xbffff5a0 0xb7fe2b38
0xbffff520: 0x00000001 0x00000001 0x00000000 0x08048270
0xbffff530: 0xb7fcbff4 0x08048510 0x080483a0 0xbffff568
0xbffff540: 0xd98a2179 0xf76c9569 0x00000000 0x00000000
可以看出%gs-0x14的内容是紫色标出的 0x03133000。由本人的经验粉红色的0x080484d2是函数vulFunc的返回地址,如果看过《缓冲区溢出系列二之缓冲区溢出实例详解》还没这个经验的话,那我讲得就太失败了。不过,也可以看看main的反汇编代码,看看0x080484d2是不是call 0x8048454 后面的那条指令,如果是,那就它就是返回地址无疑了。而浅蓝色标出的8个41个一个00就是我们的参数字符串AAAAAAAA。现在可以很清楚的看到%gs-0x14的内容的 0x03133000是位于参数和返回地址之间了吧。这次要进行缓冲区溢出攻击是不是就不那么容易了(至少我是没有成功的攻击过)。
下面说说本人的一些想法吧,纯属扯淡,可以不看。
在gcc保护模式下要进行缓冲区溢出攻击的话,就我自己的理解,要修改返回地址,有两种途径。
第一,你可以很准确的定位到返回地址在内存中的位置,这样的话就只修改存放返回地址的这一块内存就行了,其他原封不动,不过准确定位感觉在实际中比较困难;
第二,可以象常规方法那样覆盖从参数到返回地址甚至再向后覆盖,不过,如果你同时能修改gs寄存器中的值,使它和内存中相应地址的值一致就OK了。具体能不能实现,我没有试过,不过估计gs寄存器是不能随便让程序员动的。
如果你有什么见解,欢迎留言!
阅读(1584) | 评论(0) | 转发(0) |