通关栈溢出(三):攻击SEH
本文主要介绍利用SEH来实现栈溢出
OS: Windows XP SP2, Window 7,Windows 2000
Tools:Ollydbg、IDA pro、AsmToE(汇编转机器码)、010Editor、VC6.0、Xcode、Windbg、VS7.0
S.E.H
Structture Exception Handler: 异常处理结构体。它是windows异常处理机制所采用的重要数据结构。
每个S.E.H包含两个DWORD指针:SEH链表指针和异常处理函数句柄,共8个字节。
简单点就是,多个SEH是用链表串起来的,因此每个SEH都包含当前的Exception handler和一个next指针
- SEH结构体存放在系统栈中
- 当线程初始化时,会自动向栈中安装一个SEH作为线程默认的异常处理
- 如果程序员代码中使用了__try{}__except{}或者Assert宏等异常处理机制,编译器将最终通过向当前函数栈帧中安装一个SEH来实现异常处理
- 栈中一般同时会存在多个SEH
- 栈中的多个SEH通过链表指针在栈内由栈顶向栈底串成单项链表,位于链表最顶端的SEH通过TEB(线程环境块)0字节偏移处的指针标识
- 当异常发生时,操作系统会中断程序,并首先从TEB的0字节偏移处取出距离栈顶最近的S.E.H,使用异常处理函数句柄所指向的代码来处理异常。
- 当离“事故现场”最近的异常处理函数运行失败时,将顺着SEH链依次尝试其他的异常处理函数
- 如果程序安装的所有异常处理函数都不能处理,系统将采用默认的异常处理函数。通常,这个函数会弹出一个错误对话框然后强制关闭程序。
攻击
从上面的定义我们不难看出,SEH是存放在栈里的。这也就说明,如果发生栈溢出,那么溢出的代码是可以覆盖掉SEH的入口地址的。而此时只要我们再手动触发一下目标异常,就能跳转到Shellcode了
下图是函数调用时栈内的情况,如果在程序中设置了try-except,那么SEH节点会更多
栈布局 |
---|
局部变量 |
security_cookie : gs校验码,操作系统用于检测是否有溢出,需要操作系统支持且编译时开启 |
入栈寄存器 |
SEH节点 |
返回地址 |
函数参数 |
虚函数表 |
1 |
|
拖入ollydbg动态调试,选择View下的SEH chain选项,就能看到当前栈中的SEH表的情况。
从图中能看出,0012FF18是离栈顶最近的SEH。
接着我们在调试的栈窗口看到的数据也能验证我们的想法:
地址0012ff1c的位置已经被OD自动标记为了SEhandler,address-4就是它的next指针所处的位置,与SEH chain中看到的数据一样。
另外,从上图的stack布局能看出来,buf的起始地址为12FF40,最终地址为0012FF0C,局部变量zero的位置是12FF10,只要我们能够覆盖buf到12FF18,让它指向shellcode,那么在发生异常时,就会执行到恶意代码。
因此,构造shellcode如下:
1 | char shellcode[] = |
小结
以上就是SEH的攻击方法,因为SEH本身就放在栈中,那么发生缓冲区溢出时攻击者是一定能控制SEH的next的,这也让SEH的攻击成为了栈溢出的一种经典方法。