初步探究缓冲区溢出原理

来源:互联网 发布:胡家窝棚战斗 知乎 编辑:程序博客网 时间:2024/05/17 04:56
  在计算机内部,输入数据通常被存放在一个临时空间内,这个临时存放的空间就被称为缓冲区,缓冲区的长度事先已经被程序或者操作系统定义好了。向缓冲区内填充数据,如果数据的长度很长,超过了缓冲区本身的容量,那么数据就会溢出存储空间,而这些溢出的数据还会覆盖在合法的数据上,这就是缓冲区和缓冲区溢出的道理。
#include<stdio.h>#include<string.h>char name[] ="gongyue";int main(){char buffer[8];strcpy(buffer,name);printf("%s\n",buffer);getchar();return 0;}

我们将程序放入OD中观察一下,发现OD并没有断在main函数处,而是断在由许多系统自动生成的一些语句中,由于我们需要观察分析main函数的内容,所以为了更方便的分析,我们将程序放入IDA中观察

我们找到了main函数的地址,我们将地址00401010放入到OD中


我们发现跳转来自00401005,由于缓冲区溢出与栈的空间紧密相关,所以我们还要研究一下main函数前后的栈空间变化,在IDA中我们进行交叉引用,查找到在一个call语句在调用main函数


我们将地址00401694放入OD中,观察栈的变化


在观察之前,我们先来了解一下call这个指令,call在执行的时候先将call下面的语句入栈,然后jmp到call语句所在的位置,对于我们的程序,在每次执行call语句,都会将00401699这个地址入栈,然后在去执行call语句,当执行完call语句的时候,再将这个地址出栈,这样就知道下一步该去执行那条指令。我们又把这个地址称为返回地址,它就告诉call,执行完call这条指令就执行下面这个地址的语句,那我们先来看看当前栈的情况


我们可以发现当前栈顶的位置为12FF88,向下的3个参数就是main函数的3个参数。我们进入call这个语句的内部观察栈的变化

我们发现00401699这个地址已经入栈,压入的是一个返回地址(上面已说到),而下面的3个参数没有变化,当程序执行时,程序的首要任务就是char buffer[8]这个局部变量分配空间

我们执行 push ebp,在堆栈我们可以发现ebp已近入栈,在观察汇编代码程序给我们开辟了4c大小的空间,那么所到的位置是0012FF34。继续F8单步向下,执行到00401028处,我们可以看到堆栈全部变成了cc


这个主要是为了容错和保持程序的健壮性,用cc也就是int3断点来填充这个区域,如果有未知的程序跳到这个地方,就不会出现错误,而是被断下来了,这和溢出关系不大,但是我们的0012FF80和84处的栈空间始终没有被占用,这两个点我们要始终注意。再继续向下执行,我们,遇到call 00401048,进入到IDA中分析,为strcpy函数


我们可以发现strcpy函数已进入栈,先进入的是strcpy函数参数的name,之后的是第一个参数buffer,那么之后执行完这个函数,我们的字符串就将拷贝到0012FF78的位置,我们注意观察


这时我们可以看到12FF78处拷入了我们的字符串,而12FF80处依然保存的是父函数的ebp,12FF84依然保存的是返回地址,这时我们研究溢出时首要关注的点。


那我们再来看一下溢出的程序堆栈是什么样子的

之前的程序与正常的相同,我们要关注的是在strcpy函数传参之后的堆栈变化,仍然是00401031处,依然是将字符串传给0012FF78


但是我们一直强调的12FF80和84部分却被覆盖掉了,父函数被字符串覆盖,返回地址变成了6575.


这就是说我们的main函数在执行完之后,程序将返回到6575处,那6575处是什么样子呢。我们F8单步运行

什么也没有!!!在往下走就会发生报错。

我想大家已经明白溢出的原理了吧,就是我们的返回地址被字符串覆盖,当我们的地址要返回的时候,返回的是一个无效的地址,导致程序出错.

假如我们返回的地址是一个有效的地址,而包含的指令又是一个有效的指令,那我们的程序就会毫不犹豫的去执行,如果我们想要有效的利用这个漏洞,我们就可以构造一个有效的地址,将我们想要执行的代码写入到这个地址,这就是shellcode。我在下一篇就简单的谈谈漏洞的利用。











1 0
原创粉丝点击