SEH是应用最为广泛,却没有被微软公开技术之一,所有不同windows版本,SEH可能有所不同。
SEH链表位置:fs:[0]->线程信息块TIB,TIB.ExceptionList->SEH链表
一)有关SEH链表结构:
1)线程信息块TIB结构
kd> dt _NT_TIB
nt!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD ;SEH链表头
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB
2)链表节点
_EXCEPTION_REGISTRATION struc
prev dd ? ;下一个_EXCEPTION_REGISTRATION结构
handler dd ? ;异常处理函数地址
_EXCEPTION_REGISTRATION ends
3)异常处理函数约定
_Exception_Handler proc C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext
ret
_Exception_Handler endp
3.1)异常处理函数为系统回调,调用方式不是stdcall而是C方式调用。
3.2)参数说明
参数类型分别为:(一般只用前3个参数)
struct _EXCEPTION_RECORD * pExceptionRecord,
struct EXCEPTION_REGISTRATION * pRegistrationFrame,
struct _CONTEXT *pContextRecord,
void * pDispatcherContext
第一个参数
kd> dt _EXCEPTION_RECORD
nt!_EXCEPTION_RECORD
+0x000 ExceptionCode : Int4B ;异常代码
+0x004 ExceptionFlags : Uint4B ;异常标志
+0x008 ExceptionRecord : Ptr32 _EXCEPTION_RECORD
+0x00c ExceptionAddress : Ptr32 Void ;产生异常的地址
+0x010 NumberParameters : Uint4B
+0x014 ExceptionInformation : [15] Uint4B
第二参数
为新插入到SEH链表的新节点,利用此参数,既可恢复ebp、esp寄存器,也可通过堆栈传递数据。如:
线程代码为:
_Test proc
;********************************************************************
; 在堆栈中构造一个 EXCEPTION_REGISTRATION 结构
;********************************************************************
assume fs:nothing
push ebp
push offset _SafePlace
push offset _Handler
push fs:[0]
mov fs:[0],esp
;********************************************************************
; 会引发异常的指令
;********************************************************************
pushad
xor ebp,ebp
xor eax,eax
mov dword ptr [eax],0
popad ;这一句将无法被执行
_SafePlace:
invoke MessageBox,NULL,addr szSafe,addr szCaption,MB_OK
;********************************************************************
; 恢复原来的 SEH 链
;********************************************************************
pop fs:[0]
add esp,0ch
ret
_Test endp
异常处理函数为:
_Handler proc C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext
local @szBuffer[256]:byte
pushad
mov esi,_lpExceptionRecord
mov edi,_lpContext
assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT
invoke wsprintf,addr @szBuffer,addr szMsg,\
[edi].regEip,[esi].ExceptionCode,[esi].ExceptionFlags
invoke MessageBox,NULL,addr @szBuffer,NULL,MB_OK
;********************************************************************
; 将 EIP 指向安全的位置并恢复堆栈
;********************************************************************
mov eax,_lpSEH
push [eax + 8]
pop [edi].regEip
push [eax + 0ch]
pop [edi].regEbp
push eax
pop [edi].regEsp
assume esi:nothing,edi:nothing
popad
mov eax,ExceptionContinueExecution
ret
_Handler endp
第三个参数,cpu寄存器状态值
kd> dt _CONTEXT
nt!_CONTEXT
+0x000 ContextFlags : Uint4B
+0x004 Dr0 : Uint4B
+0x008 Dr1 : Uint4B
+0x00c Dr2 : Uint4B
+0x010 Dr3 : Uint4B
+0x014 Dr6 : Uint4B
+0x018 Dr7 : Uint4B
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B
+0x09c Edi : Uint4B
+0x0a0 Esi : Uint4B
+0x0a4 Ebx : Uint4B
+0x0a8 Edx : Uint4B
+0x0ac Ecx : Uint4B
+0x0b0 Eax : Uint4B
+0x0b4 Ebp : Uint4B
+0x0b8 Eip : Uint4B
+0x0bc SegCs : Uint4B
+0x0c0 EFlags : Uint4B
+0x0c4 Esp : Uint4B
+0x0c8 SegSs : Uint4B
+0x0cc ExtendedRegisters : [512] UChar
3.3)返回值
EXCEPTION_CONTINUE_SEARCH表示本异常处理函数不处理,系统将沿着链表查找下一个异常处理函数。
ExceptionContinueExecution表示返回到_lpContext.Eip指定的安全位置继续运行程序。
二)SEH链表图解
三)SEH调试
OD调试方法:在异常处理函数下断,当执行到异常指令的时候,根据状态栏提示按F9,就可以调试到达异常处理函数断点处。