缓冲区溢出的原因是由于字符串处理函数(gets等)没有对数组的越界加以监视和限制,结果覆盖了老的堆栈数据。
程序在内存中的存储如图所示:程序是从内存低端向内存高端按顺序执行的,堆栈的生长方向与内存的生长方向相反,因
此在堆栈中压入的数据超过预先给堆栈分配的容量时,就会出现堆栈溢出,从而使得程序运行失败;
如果发生栈溢出的是大型程序还有可能会导致系统崩溃。
看一段简单程序的执行过程中对堆栈的操作和溢出的产生过程。
#include <stdio.h>
int main()
{
char name[16];
gets(name);
for(int i=0;i<16&&name[i];i++)
printf(,name[i]);
}
编译上述代码,输入“hello world!”结果会输出hello
world!,其中对堆栈的操作是先在栈底压入返回地址,接着
将栈指针EBP入栈,此时EBP等于现在的ESP,之后ESP减
16,即向上增长16个字节,用来存放name[]数组
执行完gets(name)之后,堆栈中的内容如见图
最后,从main返回,弹出ret里的返回地址并赋值给EIP,CPU继续执行EIP所指向的命令。
如果我们输入的字符串长度超过16个字节,例如输入:hello world!AAAAAAAAAAA,则当执行完gets(name)之
后,堆栈的情况如图
由于输入的字符串太长,name数组容纳不下,只好向堆栈的底部方向继续写‘A’。这些‘A’覆盖了堆栈的老
的元素,从图4可以看出,EBP,ret都已经被‘A’覆盖了。从main返回时,就必然会把‘AAAA’的ASCII码——
0x41414141视作返回地址,CPU会试图执行0x41414141处的指令,结果出现难以预料的后果,这样就产
生了一次堆栈溢出。假如使用的操作系统为Win9X的话,会得到那个经典的“该程序执行了非法操作”的对话框。