栈溢出原理

程序在运行前会预留一些内存空间,这些内存空间用于临时储存I/O数据。这种预留的内存空间称为缓冲区。
缓冲区溢出是指计算机向缓冲区内填充的数据超过了缓冲区本身的容量,导致合法的数据被覆盖。从安全编程的角度出发,理想情况下程序在执行复制、赋值等操作时,需要检查数据的长度,且不允许输人超过缓冲区长度的数据。但有时开发人员会假设数据长度总是与所分配的储存空间相匹配,并默认程序在运行时传递的数据都是合法的,所以不一定会主动添加对数据合法性的检测,这就为缓冲区溢出埋下了隐患。
根据缓冲区所处的不同内存空间及分配方式,缓冲区溢出可以分为栈溢出和堆溢出两种。

栈溢出原理

栈(stack)是一种基本的数据结构,由编译器自动分配、释放。从数据结构的层次理解,栈是一种先进后出的线性表,符合先进后出的原则。在程序中,栈中保存了函数需要的重要信息,并在函数执行结束时被释放。程序可借助栈将参数传递给子函数。栈也用于存放局部变量、函数返回地址。栈赋予程序一个方便的空间来访问函数的局部变量,并传递函数执行后的返回信息。
这里举个实例:

#include "stdafx.h"
#include 
#include 
void testFunc(char *Buf)
{
	char testBuf[8];
	memcpy(testBuf,buf,8)'
	return;
}
int main()
{
	char Buf[64]={0};
	memset(Buf,0x41,64);
	testFunc(Buf);
	return 0;
}

编译后用OllyDbg加载目标程序,使程序运行至main函数入口处,汇编代码如下:

00401071 mov	ebp, esp								//栈操作的指令
00401073 sub	esp, 80									//设置mian函数的栈空间
00401079 push	ebp
0040107A push 	esi
0040107B push	edi
0040107C lea	edi, dword ptr[ebp-80]					//
0040107F mov 	ecx, 20									//
00401084 mov	eax, CCCCCCCC							//
00401089 rep	stos dword ptr es:[edi]					//这四行负责初始化栈空间
0040108B mov	byte ptr[ebp-40],0
0040108F mov 	ecx, 0F
00401094 xor	eax, eax
00401096 lea	edi, dword ptr[ebp-3F]
00401099 rep 	stos dword ptr es:[edi]
0040109B stos	word ptr es:[edi]
0040109D stos	byte ptr es:[edi]
0040109E push	40
004010A0 push	41
004010A2 lea	eax,dword ptr[ebp-40]
004010A5 push	eax
004010A6 call	memset
004010AB add	esp ,0c

程序在一开始就将原ebp压栈保存,重新调整栈结构,并利用“sub esp,80”指令来设置main函数的栈空间。从上面的代码可以看出,Buf[64]对应于从[ebp-40]处开始的64字节栈空间,而这些都是main函数在初始化的时由系统自动分配的。
栈是向低地址方向生长的,而变量在栈中是向高地址方向生长的,因此,当栈里面的变量被赋予的值超过其最大分配缓冲区的大小时,就会覆盖前面push到栈里的返回地址,导致函数在返回时发生错误,这就是栈溢出。

你可能感兴趣的:(pwn)