缓冲区溢出原理

一、实验目的

  1. 掌握缓冲区溢出原理
  2. 理解CALL指令和返回地址的概念
  3. 观察正常程序的栈空间与存在溢出问题程序的栈情况

二、实验环境

  1. 系统环境:Windows环境
  2. 软件环境:C++ollydbg.exeidaq.exe

三、实验原理

  • 通过向程序的缓冲区(堆、栈等)中写入超出其长度的数据,造成缓冲区溢出。缓冲区的溢出可以破坏程序执行流程,使程序转向执行其它命令。利用缓冲区溢出可以达到攻击主机的目的。

四、实验步骤

  1. 打开Windows7虚拟机,编写一个overrun.dsw工程文件。overrun.cpp代码如下:
#include "stdio.h"
#include "string.h"
char name[]="overrun";
int main()
{
char buffer[8];
strcpy(buffer,name);
printf("%s\n",buffer);
getchar();
return 0;
}
  • 代码中我们创建一个局部空间为8个字节的buffer字符数组,然后利用strcpy函数将含有7个字节name字符串中的数据拷贝到buffer数组中,运行程序,字符串输出正常,按下回车,显示正常。如下图所示程序正常运行:
    缓冲区溢出原理
  1. 分析当name数组中数据超过8个字节的情况,将代码段name字符串修改长度为10个字符“overrun123456”,overrun.cpp代码如下:
#include "stdio.h"
#include "string.h"
char name[]="overrun123456";
int main()
{
char buffer[8];
strcpy(buffer,name);
printf("%s\n",buffer);
getchar();
return 0;
}

  • 此时将name[ ]13个字符大小的数组复制到buffer[8]数组中,再次运行程序,字符串输出正常,按下回车,出现错误。如下图所示:
    缓冲区溢出原理
  • 此时查看问题详细信息,可以发现异常偏移:00000036说明缓冲区溢出了导致程序运行发生错误。

正常程序分析:

  • 对于上述两种情况,首先分析正常程序。使用ollydbg.exe程序,将实验程序文件overrun.exe拖入其中,如下图所示:缓冲区溢出原理

  • 本次实验我们需要分析main函数,所以需要判定main函数的地址以及定位调用main函数的语句。首先打开idaq.exe程序,将正常程序overrun.exe拖入其中。如下图所示:缓冲区溢出原理

  • 然后双击jmp_main_0,按下空格键,找到main函数的地址为00401010(数据以实际情况为准),如下图所示:
    缓冲区溢出原理

  • 在ollydbg.exe界面左上角地址栏点击右键,选择Go to,选择Expression,在弹出的搜索栏中输入已获取的main函数地址,点击ok按钮。如下图所示:跳转至main函数地址
    缓冲区溢出原理
    缓冲区溢出原理

  • 获取main函数位置之后,如下图所示我们可以知道main函数由00401005处的语句跳过来。由于缓冲区溢出与栈空间紧密相关,现在我们来分析调用main函数前后栈空间的情况,如下图所示:main函数由00401005处的语句跳转
    缓冲区溢出原理

  • 接下来定位调用main函数的call语句。在idaq.exe界面中,点击main函数地址出,按下ctrl+x组合键,显示由call_main_0跳转到当前位置,按下ok按钮跳转至call_main_0,如下图所示:
    缓冲区溢出原理
    获取调用main函数的位置00401694
    缓冲区溢出原理

  • 在ollydbg.exe中跳转至call main函数的位置00401694(数据以实际情况为准),如下图所示:
    缓冲区溢出原理

  • 接下来分析调用call main函数前后栈空间的情况。一般当程序执行call的时候,会分为两步,第一步将call语句下一条语句的地址入栈,第二步jmp跳转至call语句所在地址的位置。当call语句执行完之后程序会将下一条语句的地址出栈,告诉程序call语句执行完之后继续执行这个地址的指令,我们一般将这个地址称为返回地址。如下图所示,下一条语句的地址为00401699
    缓冲区溢出原理

  • 点击00401694处然后按下F2添加断点,按F9执行当前断点位置,再按F7单步执行进入main函数,发现下一条语句的地址00401699入栈,如下图所示:
    缓冲区溢出原理

  • 源程序中首先创建了8个字符大小的char数组,当我们进入main函数的时候,首先会给这个局部变量分配空间。按F8继续执行push ebp指令。观察栈顶情况发现EBP入栈。继续按F8执行完SUB ESP,4C后观察栈的情况,发现已经分配相应空间。如下图所示:EBP入栈
    缓冲区溢出原理

  • 按F8继续执行完缓冲区溢出原理
    后观察栈的情况,发现分配的局部空间被CC填充。程序为了容错性以及自身的健壮性会用CC(int 三断点)填充,如果有未知的程序跳转这边区域便不会出现崩溃的情况,如下图所示:局部空间被CC填充
    缓冲区溢出原理

  • 按F8继续执行至下一条call指令,程序可以正常跳转至00401699处执行,如下图所示:call调用的函数地址为00401005
    缓冲区溢出原理

异常程序分析::

  • 接下来分析溢出程序调用strcoy前后栈的情况,在工具软件安装包中打开ollydbg.exe和idaq.exe程序,分别将实验程序overrun.exe拖入其中,此处步骤和上面步骤同理省略…
    缓冲区溢出原理

  • 进入idaq.exe界面调转到00401694
    缓冲区溢出原理

  • 发现返回地址和EBP都被字符串覆盖
    缓冲区溢出原理
    缓冲区溢出原理

    结论:通过实验发现返回地址和EBP都被字符串所覆盖,实验程序跳转至错误的返回地址处没有任何指令,故程序报错。
    整理整个分析过程可以得知,本次实验中导致缓冲区溢出的原因是因为代码编写的不规范,导致程序运行过程中返回地址被覆盖,从而使得程序错误的运行。倘若覆盖返回地址的数据为攻击者精心构造的数据,那么程序就会按照被覆盖数据的引导来执行,从而达到攻击者某些不可告人的目的。

上一篇:Linux 中shell脚本设置开头固定格式的实现方法


下一篇:call指令的地址是怎么计算出来的。