Chinaunix首页 | 论坛 | 博客
  • 博客访问: 265435
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2008-12-02 11:12:43

前面说过,gcc4以上版本会对缓冲区溢出做出保护,那么这篇文章旨在使大家明白gcc到底是怎么做保护的。
我们在《缓冲区溢出系列二之缓冲区溢出实例详解》中提到过,用gcc4以上版本编译的时候,可以用选项-fno-stack-protector来关闭对缓冲区溢出的保护,那么我们现在就先来对比一下没做保护和做了保护到底有什么区别。
分别加上保护和不加保护来编译《缓冲区溢出系列二之缓冲区溢出实例详解》中的源程序p.c
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的内容暂时放入ebp0x14这个内存地址处
然后下面两句是将%gs0x14的内容放入ebp0x4这个内存地址处
最后两句是再将原eax的值(暂存在ebp0x14中的值)恢复。
可以看出加保护以后,只是在将eax压栈之间,先压入%gs0x14的内容。
那么%gs0x14里面是什么东西呢?
gs寄存器是32x866个段寄存器(ESCSSSDSFSGS)中的一个,大家可能比较熟悉16位的x86下的4个段寄存器(ESCSSSDS),32位的多了两个段寄存器FSGS
经查阅资料,gcc4以上版本对缓冲区溢出的保护机制是这样的:在调用strcpy等一系列需要传递字符串参数的函数时,在压入传递进来的字符串参数前,先压入一个随即数(这里即%gs0x14),然后在函数返回时再检测一下%gs0x14和堆栈中原先压入的值是不是一致的,如果不一致,那么肯定有缓冲区溢出攻击。
由于这个随即值在栈中的位置是位于返回地址和传递的字符串参数之间的,所以要利用缓冲区溢出攻击来覆盖返回地址,那么就肯定也覆盖了这个随即值。
来看看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
 
可以看到这时候ebp0xbffff4d8,而从源代码
0x08048460 :   mov    %gs:0x14,%eax
0x08048466 :   mov    %eax,-0x4(%ebp)
中可以看出,%gs0x14是存放在了内存地址ebp0x4(=0xbffff4d4)中了,所以, 0xbffff4d4中就是%gs0x14的内容了,看看内存
 
(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) |
给主人留下些什么吧!~~