今天在阅读完 Linux下缓冲区溢出攻击的原理及对策,后对文章中的一个例子进行了验证。但是对example2进行调试时,发现由于gcc版本不同,因此在自己的机器无法调试通过。通过GDB对该程序进行调试,最后找出了问题,过程如下:
例子中的源代码如下1 int function(int a, int b, int c) {
2 char buffer[14];
3 int sum;
4 int *ret;
5
6 ret = buffer + 20;
7 (*ret) += 10;
8 sum = a + b + c;
9 return sum;
10 }
11
12 void main() {
13 int x;
14
15 x = 0;
16 function(1,2,3);
17 x = 1;
18 printf("%d\\n",x);
19 }
这段代码的原理,在原文中有解释,简单的将就是通过定义的buffer变量的地址,定位到保存function函数返回main函数时的返回地址的 位置,然后修改返回地址的值,将c源码中第17行x=1的赋值运算跳过。使最后x的输出为0。而在本机上没有成功的原因是由于function函数在调运 时,为变量分配空间时,由于编译器存在差异,因此分配的空间大小不同导致。那么如何能够知道准确的ret address的值呢?让我们使用gdb来进行调试。
首先编译源码
gcc -g -o example2 example2.c
gdb example2
(gdb) disassemble main
Dump of assembler code for function main:
0×0804840d
0×08048411
0×08048414
0×08048417
0×08048418
0×0804841a
0×0804841b
0×0804841e
0×08048425
0×0804842d
0×08048435
0×0804843c
0×08048441
0×08048448
0×0804844b
0×0804844f
0×08048456
0×0804845e
0×0804845f
0×08048460
0×08048463
End of assembler dump.
(gdb) disassemble function
Dump of assembler code for function function:
0×080483c4
0×080483c5
0×080483c7
0×080483ca
0×080483d0
0×080483d3
0×080483d5
0×080483d8
0×080483db
0×080483de
0×080483e1
0×080483e3
0×080483e6
0×080483e9
0×080483eb
0×080483ee
0×080483f1
0×080483f4
0×080483f7
0×080483fa
0×080483fd
0×08048404
0×08048406
0×0804840b
0×0804840c
End of assembler
main+47行的call function指令,除了会将执行权交给function外,还会将function返回后执行的下一条指令的地址0×08048441压入栈中,这个是call指令自动执行的,同时通过function中第一条指令:
0×080483c4
将寄存器ebp的值也压入栈中,
0×080483c5
将寄存器esp的值交给ebp
0×080483c7
完成空间申请等..
我们来观察寄存器的状态的变化
(gdb) break *0×080483c7
在call function后break一下
(gdb)r
(gdb) info registers
我们只关心esp和ebp的值:
esp 0xbfc53500
ebp 0xbfc53528
目前的堆栈数据结构为:
—————–
0×08048441 返回main时,所执行的下一条指令的地址
—————–
0xxxxxxxxx 原先ebp中的值
—————– <—该处的地址值为ebp中的值0xbfc53528
buffer
buffer
buffer <—该处的地址值在gdb中使用p &buffer能得知为0xbfc53516
…..
—————–
我们知道ebp保持的值和存放返回地址内存区域的差距是4个字节,我们可以通过gdb进行验证0xbfc3528+4处保存的内容是否是正确的返回地址:
(gdb) x 0xbfc5352c
0xbfc5352c: 0×08048441 没有问题
而&buffer的内存地址和ebp中的值相差为0×12,换算成十进制为18,加上ebp值和返回地址之间相差的4个字节。
因此需要将源码中第6行改为
ret = buffer + 22;
重新编译后,返回值为0,与目标相符
勘误:
正如Border所说, 第七行 (*ret) += 10;应改为
(*ret) += 7;
没有评论:
发表评论