Linux下的栈溢出案例分析

摘要:
本文主要演示Linux平台下的栈溢出,首先根据理论对示例代码进行溢出攻击;结果是溢出攻击成立,但是与设想的有差别;然后采用GDB调试工具对发生的意外,进行深入的分析。

Linux下用GDB调试可加载模块

Fedora 16 x64 构建vim+vimGDB+GDB C/C++调试环境

GDB调试程序用法

GDB+GDBserver无源码调试Android 动态链接库的技巧

使用hello-gl2建立ndk-GDB环境(有源码和无源码调试环境)

Ubuntu上用GDB调试printf源码

Linux下用GDB调试可加载模块

Ubuntu下使用GDB断点Go程序

测试的平台:
1.  Ubuntu 9;  gcc 4.4.1;  Gdb 7.0-ubuntu
2.  Ubuntu系统安装在VirtualBox 3.2.8虚拟机上;

示例代码如下:

#include<string.h>
void overflow(char* arg)
{
 char buf[12];
 strcpy(buf, arg);
}

int main(int argc, char *argv[])
{
 if(argc > 1)
  overflow(argv[1]);
 return 0;
}

如果按照一般的方式编译:
gcc –o stackoverflow stackoverflow.c
linux系统能够探测到程序中的stack  overflow,从而终止程序,如下图所示:

Linux下的栈溢出案例分析-GDB调试操练

那有没有办法让系统不探测到stack overflow,此处可以在编译时,禁用堆栈保护,具体命令如下:
gcc –fno-stack-protector –o stackoverflow stackoverflow.c
然后采用gdb调试stackoverflow,

Linux下的栈溢出案例分析-GDB调试操练

这里的输出跟设想的存在很大的差别,因为设想中的函数栈如下:

Linux下的栈溢出案例分析-GDB调试操练

前面的12个字节填充buf,然后接下来的4个字节填充ebp,最后的4个字节填充RET地址,那么照理说,这里的eip应该是0x65656565,那为什么此处是0x61616161,刚好是aaaa的值呢?

根据单步调试的结果,发现eip变为0x61616161是在main函数退出后达到的,按照设想应该是在overflow退出时,eip变为0x65656565。

为什么overflow退出后还能回到main函数?可能的原因:输入的字符串没有覆盖掉ret地址,但是字符串却意外地将main的返回地址给覆盖掉了?

但就算是覆盖,为什么覆盖的值没有采用e的值,而是采用的a的值?要知道a是在字符串的起始处?这点的确让人奇怪。

1. 我们还是采用一步步调试的方式来观察问题所在,先看下gcc编译后的反汇编代码:
使用到的命令:
set disassembly-flavor intel  //将汇编设定为intel风格;
disassemble main  //反汇编main函数;

Main函数:

1. push ebp
2. mvo ebp, esp
3. and esp, 0xfffffff0
4. sub esp, 0x10
5. cmp DWORD PTR [ebp+0x8], 0x1
6. jle  0x804841d <main+31>
7. mov eax, DWORD PTR [ebp+0xc]
8. add eax, 0x4
9. mov eax, DWORD PTR [eax]
10. mov DWORD PTR [esp], eax
11. call 0x80483e4 <overflow>
12. mov eax, 0x0
13. leave
14. ret

然后再来看下,overflow的反汇编代码,命令:disassemble overflow
Overflow函数:

1. push ebp
2. mov ebp, esp
3. sub esp, 0x28
4. mov  eax, DWORD PTR[ebp+0x8]
5. mov  DWORD PTR[esp+0x4], eax
6. lea  eax, [ebp-0x14]
7. mov  DWORD  PTR [esp], eax
8. call  0x804831c <strcpy@plt>
9. leave
10. ret

我们单步调试上述的指令,关注其中esp值的变化。总图如下,后面是对其中每一步的分析:

Linux下的栈溢出案例分析-GDB调试操练

在完成main.1后,命令p $esp后,esp的值变为:Esp = 0xbffff438
Main.3后,esp的值变为0xbffff430,估计是用于对齐;
Main.4后,esp的值为0xbffff420;
Main.7-10,这里主要将argv[]的arg[1]字符串的首地址取出来,并且将其放置在esp中,此时esp的值为0xbffff420;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/318a8a144bd2066946208b6e426d1a01.html