形形色色的内存攻击技术
狙击Windows异常处理机制
S.E.H概述
S.E.H为异常处理结构体,包含两个DWORD指针:S.E.H链表指针和异常处理函数句柄。
相关要点:
- S.E.H结构体存放在系统栈中;
- 当线程初始化时,会自动向栈中安装一个S.E.H,作为线程默认的异常处理
- 如果源程序使用了
__try{}__except{}
或者Assert
宏等异常处理机制,编译器最终会通过向当前函数栈帧中安装一个S.E.H实现异常处理 - 栈中一般会存在多个S.E.H
- 栈中多个S.E.H通过链表指针在栈内由栈顶向栈底串成单向链表,位于链表最顶端的S.E.H通过T.E.B(线程环境块)0字节偏移处的指针标识
- 当异常产生时,系统会中断程序,从T.E.B的0字节偏移处取出距离栈顶最近的S.E.H,使用异常处理函数句柄所指向的代码来处理异常
- 当离“事故现场”最近的异常处理函数运行失败时,就会顺着S.E.H链表依此尝试其他的处理函数
- 如果所有异常处理函数都不能处理,系统采用默认的异常处理函数。
从程序设计的角度来说,S.E.H就是在系统关闭程序之前,给程序一个执行预先设定大的回调函数的机会。
S.E.H存在的问题:
- S.E.H存放在栈内,溢出缓冲区的数据可能淹没S.E.H
- 可使S.E.H的异常处理函数的入口地址更改为shellcode的地址
- 溢出后错误的栈帧或堆块数据往往会触发异常
- 当Windows开始处理溢出后的异常时,会错误地执行shellcode
在栈溢出中利用S.E.H
示例代码:
#include <windows.h>
char shellcode[]="\x90\x90\x90......";
DWORD MyExceptionhandle(void){
printf("got an exception, press Enter to kill process!\n")
getchar();
ExitProcess(1);
}
void test(char * input){
char buf[200];
int zero=0;
__asm int 3
__try{
strcpy(buf,input);
zero=4/zero;
}
__except(MyExceptionhandle()){
}
}
void main(){
test(shellcode)