缓冲区溢出编程心得 |
| 时间:2006-07-07 13:12:02 来源: 作者: |
前言:网上关于缓冲区溢出的资料也有很多,但我在阅读过程中发现介绍的都不是很明了,而且各网站也只是转贴老外的那篇译文而已,不仅内容有缺损,而且程序也无法调通,因为GCC版本不一样.经过几天的琢磨,终于明白了真正的原理,特地写出来分享.qLELinux联盟 qLELinux联盟 测试环境:qLELinux联盟 $ gcc -vqLELinux联盟 Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/specsqLELinux联盟 Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linuxqLELinux联盟 Thread model: posixqLELinux联盟 gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-24)qLELinux联盟 qLELinux联盟 $ gdb -vqLELinux联盟 GNU gdb Red Hat Linux (6.0post-0.20031117.6rh)qLELinux联盟 Copyright 2003 Free Software Foundation, Inc.qLELinux联盟 GDB is free software, covered by the GNU General Public License, and you areqLELinux联盟 welcome to change it and/or distribute copies of it under certain conditions.qLELinux联盟 Type "show copying" to see the conditions.qLELinux联盟 There is absolutely no warranty for GDB. Type "show warranty" for details.qLELinux联盟 This GDB was configured as "i386-redhat-linux-gnu".qLELinux联盟 qLELinux联盟 $ uname -aqLELinux联盟 Linux candy 2.4.21-9.EL #1 Thu Jan 8 17:03:13 EST 2004 i686 athlon i386 GNU/LinuxqLELinux联盟 qLELinux联盟 实例:qLELinux联盟 网上和我的这个实例雷同的也有,但是他们的是无法正确实现的.因为关键的跳转代码没有计算正确.(GCC版本问题,呵呵)qLELinux联盟 /************qLELinux联盟 * a.cqLELinux联盟 ************/qLELinux联盟 void function(void)qLELinux联盟 {qLELinux联盟 char buffer[5];qLELinux联盟 int* ret;qLELinux联盟 qLELinux联盟 ret=buffer+28;qLELinux联盟 (*ret)+=10;qLELinux联盟 }qLELinux联盟 qLELinux联盟 void main()qLELinux联盟 {qLELinux联盟 int x;qLELinux联盟 qLELinux联盟 x=0;qLELinux联盟 function();qLELinux联盟 qLELinux联盟 x=1;qLELinux联盟 printf("%d\n",x);qLELinux联盟 qLELinux联盟 return;qLELinux联盟 }qLELinux联盟 /*end*/qLELinux联盟 qLELinux联盟 懂C语言的人都会认为最后的输出结果是1,可惜输出结果为0.为什么呢?请听解释.qLELinux联盟 qLELinux联盟 实例分析:qLELinux联盟 相关堆栈的基础知识我就不罗嗦了,网上的介绍很多.qLELinux联盟 关键问题在于如何确定源代码qLELinux联盟 ret=buffer+28;qLELinux联盟 (*ret)+=10;qLELinux联盟 中的28 和 10qLELinux联盟 qLELinux联盟 编译(会有warning,不用管他.)qLELinux联盟 $gcc -g -o a a.c //加上-g 用来在gdb中调试qLELinux联盟 qLELinux联盟 $gdb aqLELinux联盟 (gdb)disas main //得到反汇编代码 如下:qLELinux联盟 Dump of assembler code for function main:qLELinux联盟 0x08048366 : push %ebpqLELinux联盟 0x08048367 : mov %esp,%ebpqLELinux联盟 0x08048369 : sub $0x8,%espqLELinux联盟 0x0804836c : and $0xfffffff0,%espqLELinux联盟 0x0804836f : mov $0x0,%eaxqLELinux联盟 0x08048374 : sub %eax,%espqLELinux联盟 0x08048376 : movl $0x0,0xfffffffc(%ebp)qLELinux联盟 0x0804837d : call 0x8048348 qLELinux联盟 0x08048382 : movl $0x1,0xfffffffc(%ebp)qLELinux联盟 0x08048389 : sub $0x8,%espqLELinux联盟 0x0804838c : pushl 0xfffffffc(%ebp)qLELinux联盟 0x0804838f : push $0x8048474qLELinux联盟 0x08048394 : call 0x8048288qLELinux联盟 0x08048399 : add $0x10,%espqLELinux联盟 0x0804839c : leaveqLELinux联盟 0x0804839d : retqLELinux联盟 End of assembler dump.qLELinux联盟 qLELinux联盟 (gdb)disas functionqLELinux联盟 Dump of assembler code for function function:qLELinux联盟 0x08048348 : push %ebpqLELinux联盟 0x08048349 : mov %esp,%ebpqLELinux联盟 0x0804834b : sub $0x28,%espqLELinux联盟 0x0804834e : lea 0xffffffe8(%ebp),%eaxqLELinux联盟 0x08048351 : add $0x1c,%eaxqLELinux联盟 0x08048354 : mov %eax,0xffffffe4(%ebp)qLELinux联盟 0x08048357 : mov 0xffffffe4(%ebp),%edxqLELinux联盟 0x0804835a : mov 0xffffffe4(%ebp),%eaxqLELinux联盟 0x0804835d : mov (%eax),%eaxqLELinux联盟 0x0804835f : add $0xa,%eaxqLELinux联盟 0x08048362 : mov %eax,(%edx)qLELinux联盟 0x08048364 : leaveqLELinux联盟 0x08048365 : retqLELinux联盟 End of assembler dump.qLELinux联盟 qLELinux联盟 可以得知当main中执行 0x0804837d : call 0x8048348 < function> 时 会将下一条指令的地址保存在堆栈中. 即 0x08048382 我们的目的就是要想这个值修改成下一条指令的地址 0x08048389 这样就达到了屏蔽 x=1 这条语句了. 关键问题在于如何寻找保存0x08048382这个值的地址....qLELinux联盟 qLELinux联盟 继续使用gdbqLELinux联盟 (gdb) l //显示源代码(因为编译时用了 -g 参数)qLELinux联盟 5qLELinux联盟 6 ret=buffer+28;qLELinux联盟 7 (*ret)+=10;qLELinux联盟 8 }qLELinux联盟 9qLELinux联盟 10 void main()qLELinux联盟 11 {qLELinux联盟 12 int x;qLELinux联盟 13qLELinux联盟 14 x=0;qLELinux联盟 qLELinux联盟 (gdb)b 6 // 在关键处下断点 观察内存的值qLELinux联盟 Breakpoint 1 at 0x804834e: file a.c, line 6.qLELinux联盟 (gdb)b 7qLELinux联盟 Breakpoint 2 at 0x8048357: file a.c, line 7.qLELinux联盟 (gdb)r qLELinux联盟 Breakpoint 1, function () at rr.c:6qLELinux联盟 6 ret=buffer+28;qLELinux联盟 (gdb)i reg //观察寄存器的值 (注意ebp esp eip)qLELinux联盟 eax 0x0 0qLELinux联盟 ecx 0xbffff01c -1073745892qLELinux联盟 edx 0xbfffefa0 -1073746016qLELinux联盟 ebx 0xb75d4e58 -1218621864qLELinux联盟 esp 0xbfffef50 0xbfffef50qLELinux联盟 ebp 0xbfffef78 0xbfffef78qLELinux联盟 esi 0xbffff014 -1073745900qLELinux联盟 edi 0xb75d273c -1218631876qLELinux联盟 eip 0x804834e 0x804834eqLELinux联盟 eflags 0x200286 2097798qLELinux联盟 cs 0x23 35qLELinux联盟 ss 0x2b 43qLELinux联盟 ds 0x2b 43qLELinux联盟 es 0x2b 43qLELinux联盟 fs 0x0 0qLELinux联盟 gs 0x33 51qLELinux联盟 qLELinux联盟 看见esp的值为0xbfffef50qLELinux联盟 接着查看内存中0xbfffef50后面的数据内容qLELinux联盟 (gdb)x/20x $espqLELinux联盟 0xbfffef50: 0x080483a0 0x08049564 0xbfffef68 0x08048265qLELinux联盟 0xbfffef60: 0x00000000 0x00000000 0xbfffef88 0x080483baqLELinux联盟 0xbfffef70: 0xb74ca4f3 0xb75d4e58 0xbfffef88 0x08048382qLELinux联盟 0xbfffef80: 0xb7600020 0x00000000 0xbfffefe8 0xb74b5748qLELinux联盟 0xbfffef90: 0x00000001 0xbffff014 0xbffff01c 0x00000000qLELinux联盟 可得到 在0xbfffef7c 中保存着 最初我们需要寻找的0x08048382 那么0xbfffef7c就是我们的程序里ret所要存储的内容了.qLELinux联盟 (gdb)p &bufferqLELinux联盟 $1 = (char (*)[5]) 0xbfffef60qLELinux联盟 因此得到我们的第一个数据28 至于10则可以从反汇编的代码得到(移到下一条指令).qLELinux联盟 qLELinux联盟 (gdb)nqLELinux联盟 (gdb)n //之后我们可以看到 0x08048382 变成了 0x0804838cqLELinux联盟 qLELinux联盟 (gdb)cqLELinux联盟 Continuing.qLELinux联盟 0qLELinux联盟 qLELinux联盟 Program exited with code 02.qLELinux联盟 qLELinux联盟 但真正想实现溢出提高权限 则需要shellcode 具体的定位方式需要具体分析,但原理和我上面将的是一样的了.qLELinux联盟 qLELinux联盟
|
|
|
|
|
|