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,就可以調試到達異常處理函數斷點處。