SEH, SAFESEH相关
SEH, SAFESEH相关
1,触发seh异常
让目标程序Read/Write无效地址,如果和栈底相邻的内存只读,尝试覆盖超出栈底
2,如何找到(显示)要覆盖的SEH
od语法:dd fs:[0]
softice语法: dd fs:0
3,覆盖SEH时需要填充的跳转地址
需要找一个可以跳转成功的
pop ?
pop ?
retn
的代码地址。
od语法:Ctrl+B/L 5? 5? c3
softice语法:s -a addr -L length 5? 5? c3
其中"5?"代表"58-5F"之间的任意值
4,ntdll!KiUserExceptionDispatcher()流程
VEH <-- winxp才有
↓
SEH <-- win2000从这里开始
↓
UEF 当程序被调试时,UnhandledExceptionFilter() SetUnhandledExceptionFilter()
5,ntdll!KiUserExceptionDispatcher()检查SEH handler过程
1) 检查handler是否在线程TEB指定的Stack范围内(fs:[4]~fs:[8]),如果在其中,拒绝执行.
2) 检查handler是否在已加载模块列表(exe和dll), 如果handler不在这些模块地址范围内,执行.
3) 如果handler在模块地址范围内,开始检查已注册异常处理程序列表.
检查过程:
--------------
a) if((DLLCharacteristics&0xFF00) == 0x0400), 拒绝执行(No SEH);否则,继续检查.
b) Load Configuration Directory地址为0(即不存在Load Configuration Directory结构,说明编译时没有设置/safeseh选项),停止检查,执行.
Load Configuration Directory结构存在,继续检查:
+00h directory_size // 目录长度介于[0,0x48),停止检查,执行.
...
+40h handlers[] // SEH handler数组指针(元素是SEH handler RVA地址),if(handlers[]==0),停止检查,执行.
+44h handler_num // SEH handler数组元素个数,if(handler_num==0),停止检查,执行.
c) 然后开始逐个匹配,发现匹配,调用;没有发现匹配,拒绝调用.
6,关于DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]
这个是在winnt.h中的定义
typedef struct {
DWORD Size;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD GlobalFlagsClear;
DWORD GlobalFlagsSet;
DWORD CriticalSectionDefaultTimeout;
DWORD DeCommitFreeBlockThreshold;
DWORD DeCommitTotalFreeThreshold;
DWORD LockPrefixTable; // VA
DWORD MaximumAllocationSize;
DWORD VirtualMemoryThreshold;
DWORD ProcessHeapFlags;
DWORD ProcessAffinityMask;
WORD CSDVersion;
WORD Reserved1;
DWORD EditList; // VA
DWORD SecurityCookie; // VA
DWORD SEHandlerTable; // VA
DWORD SEHandlerCount;
} IMAGE_LOAD_CONFIG_DIRECTORY32, *PIMAGE_LOAD_CONFIG_DIRECTORY32;
SEHandlerTable 是指向一个Seh处理函数Rva的表格。SEHandlerCount是这个表格的长度。如果这个表格存在,那么只有该表格中的Seh处理函数才是合法的处理函数。如果异常发生时,顺序查找并执行Fs:[0]中的处理函数时,如果认为当前函数非法,则Seh无法继续执行,程序会中止。而且连 UnhandledExceptionFilter都无法执行到。除非PE在被调试,依靠调试器来恢复。
每个PE有一个单独表格。如kernel32.dll和user32.dll有各自的表格。当PE被载入时,PE的基址,大小、 SEHandlerTable(表格的地址)、SEHandlerCount(长度)会被存在一个表格中。当一个异常发生时,系统每个PE的基址和大小检查当前seh处理函数属于哪一个PE,然后取出相应的表格地址和长度。由于是载入时就已经取出,载入后SEHandlerTable和 SEHandlerCount就没什么用处了,对它进行修改当然也没什么用了。但修改表格内容还是有效的。
如果seh处于动态申请的内存中,因为不处于任何一个PE Image内,所以seh是没有任何限制的,否则如果不在相应表格中,会导致PE中止。visual c++的try..catch等的seh处理函数会自动加入该表格。但如果使用inline asm对fs:[0]进行操作加seh是无效的,如果发生异常只会导致PE中止。
目前基本所有的壳软件都是将loadconfig删除,对该PE基本没什么影响。但如果要保留的话,则需要将Pe Image内的seh处理函数加入到该表格中。微软称这个表格中的处理函数为"safe handler",
关掉safe handler的开关在Liker|CommandLine 加入/SAFESEH:NO
尝试动态修改内存中的SEHandlerTable,except_handler顺利得到控制权.
7,针对Win2003/WinXP SP2对SEH handler(异常处理函数)地址的突破方法
1) 不在stack里面, 在heap里.
对IE等浏览器,用js等heapspray分配堆,用堆内地址覆盖SEH handler.
2) 在已加载模块的范围外.
用代码页的跳转地址,比如0x7FFA1571(win2000/xp/2003 chs通用地址)
3) 在系统dll里面,且是该dll注册过的异常处理函数.
利用已注册的SEH handler,利用性不大.
8,OD的safeseh插件OllySSEH
用其分析已加载模块,查看哪些DLL是/SafeSEH OFF:
/Safeseh ON 只能选已注册的SEH handler地址来跳.
/Safeseh OFF 可以选里面的地址(pop/pop/ret地址)来跳.
No SEH (DLLCharacteristics&0xFF00) == 0x0400, 不能跳.
下载地址:http://www.openrce.org/downloads/details/244/OllySSEH