Windows栈溢出利用
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Windows 栈溢出分析
摘要
众所周知,缓冲区溢出是目前最为常见的漏洞利用方式,特别是栈溢出,原理简单,危害大。在Windows xp sp2以后,微软增加了许多安全检查措施来杜绝缓冲区溢出的发生。本文介绍两种Windows xp下两种常见的栈溢出利用方式,重点分析利用S.E.H进行栈溢出,并且归纳总结防范和检测方法。
第一章绪论
1.1 栈溢出定义
程序的局部变量一般都存放在栈空间内,如果用户输入的数据超过了定义的长度,就会非法覆盖栈空间的其他数据,这种现象就是栈溢出。
1.2栈溢出普通利用方式
1.2.1 利用函数返回点
函数调用约定描述了函数传递参数方式和栈协同工作的技术细节。不同的操作系统,不同的语言,不同的编译器在实现函数调用时的原理虽然基本相同,但具体的调用约定还是有差别的,包括参数传递方式,参数入栈顺序是从右向左还是从左向右,函数返回时候恢复堆栈平衡的操作在子函数进行还是在母函数中进行。下表列出了几种调用方式。
对于Visual C++,可支持以下三种函数调用约定,如下表
默认情况下,VC使用_stdcall的调用方式。本文以下讨论如不另加说明,即指这种默认的调用方式。
函数调用大致包括以下几个步骤:
(1)参数入栈:将参数从右向左依次压入系统栈中;
(2)返回地址入栈:将当前代码区调用指令的下一条指令地址压入栈中,供函数返回时继续执行;
(3)代码区跳转:处理器从当前代码区跳转到被调函数的入口地址;
(4)栈帧调整,包括保存当前栈帧状态值,已备后面恢复本栈帧时使用。将当前栈帧切换到新栈帧。给新栈帧分配空间。
对于_stdcall调用约定,函数调用时用到的汇编指令序列如下:
Push 参数;从右至左
Call 函数地址;
Push ebp;
Mov ebp,esp;
Sub esp ,xxx;
类似地,函数返回的步骤如下:
(1)保存返回值:通常将函数的返回值保存在Eax;
(2)弹出当前栈帧,恢复上一个栈帧。
(3)跳转:按照函数返回地址跳回母函数中继续执行。
相关的汇编指令序列如下:
Add esp,xxx;
Pop ebp;
Retn
可以看到函数返回点保存在栈中,且栈中的数据是可以被任意覆盖的,这就为利用提供了可能性。
第二章 S.E.H利用分析
2.1 结构化异常处理(SEH)
操作系统或程序在运行,难免会遇到各种各样的错误,如除零,非法内存访问,文件打开错误,内存不足,磁盘读写错误,外设操作失败。为了保证系统在遇到错误时不至于崩溃,仍能够健壮稳定地继续运行下去,windows会对运行在其中的程序提供一次补救的机会来处理错误这种机制就是异常处理机制。
S.E.H即异常处理结构体(Structure Exception Handler),它是windows异常处理机制所采用的重要数据结构,每个S.E.H包含两个DWORD指针:S.E.H链表指针和异常处理函数句柄,共8个字节,如下图
下面分别对用户模式下的原始型SEH和封装型SEH 进行讨论。
2.1.1 原始型SEH
SEH 的进程相关类型是整个进程作用范围的异常处理函数,通过WIN32 API 函数SetUnhandledExceptionFilter 进行注册,而操作系统内部使用一个全局变量来记录这个顶层的处理函数,因此只能有一个全局性的异常处理函数。而线程相关类型的作用范围是本线程内,并且可注册多个,甚至可以嵌套注册。两者相比线程相关类型在实际应用中使用较为广泛,因此此处重点对此类型进行研究。
当线程初始化时,会自动向栈中安装一个异常处理结构,作为线程默认的异常处理。SEH 最基本的数据结构是保存在堆栈中的称为EXCEPTION_REGISTRATION 的结构体,结构体包括2 个元素:第1 个元素是指向下一个EXCEPTION_REGISTRATION 结构的指针(prev),第2 个元素是指向异常处理程序的指针(handler)。这样一来,基于堆
栈的异常处理程序就相互连接成一个链表。异常处理结构在堆栈中的典型分布如图1 所示。最顶端的异常处理结构通过线程控制块(TEB)0 Byte 偏移处指针标识,即FS:[0]处地址。
用于进行实际异常处理的函数原型可表示如下:
EXCEPTION_DISPOSITION __cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
)
该函数的最重要的2 个参数是指向_EXCEPTION_RECORD 结构的ExceptionRecord 参数和指向_CONTEXT 结构的ContextRecord 参数,前者主要包括异常类别编码、异常发生地址等重要信息;后者主要包括异常发生时的通用寄存器、调试寄存器和指令寄存器的值等重要的线程执行环境。而用于注册异常处理函数的典型汇编代码可表示如下:
PUSH handler ; handler 是新的异常处理函数地址
PUSH FS:[0] ;指向原来的处理函数的地址压入栈内
MOV FS:[0],ESP ;注册新的异常处理结构
当异常发生时,操作系统的异常分发函数在进行初始处理后,如果异常没有被处理就会开始在上图所示的线程堆栈上遍历异常处理链,直到异常被处理,如果仍没有注册函数处理异常,则将异常交给缺省处理函数或直接结束产生异常的进程。