軟件調試(9) - 深入探討Windows結構化異常(SEH)底層實現
r3/r0一樣, FS:[0] 保存著SEH的鏈表頭
應用層:
TEB與TIB結構
TEB中的起始處是指向TIB的地址,TIB第一個字段位exceptionList即為鏈表表頭
內核層
KPCR與TIB結構
KPCR->TIB
TIB的ExceptionList字段
-------------------------------------------------------
該字段位於TIB的起始處
登記異常函數
-------------------------------------------------------
用戶態版本RtlDispatchException
-------------------------------------------------------
(內核中的異常分發:修改EIP到R3的KiUserExceptionDispatcher->)RtlDispatchException->先調用RtlCallVectoredExceptionHandlers嘗試尋找VEH->如果沒有則調用RtlpGetRegisterationHead依次遍歷鏈表 直至-1
透過ExecuteHandler內部調用pfnHandler所指向的異常處理函數(即鏈表元素)
scopetable
--------------------------------------------------------
範圍表是一個數組,用於標誌__try __except() 範圍
數組元素為以下結構:
TryLevel
--------------------------------------------------------
多層Try時使用
__except_handler3按照trylevel來尋找scopetable_entry結構,找到後調用結構中的lpnFilter所指定的過濾表達式
_except_handler3工作:
---------------------------------------------------------
位於__try{}中的代碼發生異常時,異常分發函數便會調用_except_handler3這樣的處理函數,他的工作如下:
他的調用路徑為: RtlDispatchException-> ExecuteHandler -> ExecuteHandler2 -> _except_handler3 (透過_EXCEPTION_REGISTRATION結構) ->尋找scopetable的異常塊地址 __except{}
1. 將第二個參數pRegsitrationRecord從系統默認的EXCEPTION_REGISTRATION_RECORD結構強制轉化為包含擴展字段的_EXCEPTION_REGISTRATION
2. 先從pRegistration結構提取trylevel字段,傳給局部變量,按照它的值從scopetable索引出scopetable_entry結構
3. 從scopetable提出lpfnFilter字段,如不為空,調用函數->即評估過濾表達式,若為空跳到第五步
4. 判斷是否為exception_continue_search
5. 判斷scopetable_entry結構的previousTryLevel是否-1,不等於則用previousTryLevel交給局部變量,再重覆跳到第2步;否則跳到第六步
6. 返回DISPOSITION_CONTINUE_SEARCH, 讓系統繼續尋找其他異常處理器
兩者不同之處:
------------------------------------------------------------
SEH _Try{} _except{};
1. 由劃一的_except_handler3 來處理所有SEH異常,
2. 先壓入trylevel和scopetable指針 用作找出異常處理代碼, 棧中會形成以下結構
總結:
兩種方法製造SEH
1. 直接保存fs:[0],再插入新處理函數,指向新節點
2. 使用try..except方法,透過統一處理函數_except_handler3處理
分別: 前者由ExecuteHandle2->直接執行我們添加的異常處理函數, 後者為集中到_except_handler3中由scopetable決定調用哪一個級別(trylevel)的異常處理函數
RtlDispatchException(第一次異常處理)查找失敗會調用NtRaiseException引發第二輪異常處理,成功則調用NtContiue繼續讓程序運行,兩者都不會返回到KiUserExceptionDispatcher
參考:Windows軟件調試
應用層:
TEB與TIB結構
TEB中的起始處是指向TIB的地址,TIB第一個字段位exceptionList即為鏈表表頭
內核層
KPCR與TIB結構
KPCR->TIB
TIB的ExceptionList字段
-------------------------------------------------------
該字段位於TIB的起始處
typedef struct _EXCEPTION_REGISTERATION_RECORD{ struct _EXCEPTION_REGISTERATION_RECORD *next; //0xFFFFFFFF代表最後一個節點 ULONG handler; //異常處理器的地址(需符合seh函數原形) }
EXCEPTION_DISPOSITION SehHandler(_EXCEPTION_RECORD *exceptionRecord, //處理的異常 void * EstablisherFrame, //異常函數的EBP _CONTEXT *contextRecord, //線程上下文 void * DispatcherContext //分發函數 )TIB自身不在棧中,而SEH鏈表則是存在棧中
登記異常函數
-------------------------------------------------------
push seh_handler //處理函數地址 棧空間: push FS:[0] //舊的鏈表節點處理器地址 FS:[0] move FS:[0],ESP //登記新結構 SEH_HANDLER ..... mov esp,dword ptr fs:[0] //保存下一個結構地址 pop dword ptr fs:[0] //將下一個結構的地址變成鏈表頭
用戶態版本RtlDispatchException
-------------------------------------------------------
(內核中的異常分發:修改EIP到R3的KiUserExceptionDispatcher->)RtlDispatchException->先調用RtlCallVectoredExceptionHandlers嘗試尋找VEH->如果沒有則調用RtlpGetRegisterationHead依次遍歷鏈表 直至-1
透過ExecuteHandler內部調用pfnHandler所指向的異常處理函數(即鏈表元素)
scopetable
--------------------------------------------------------
範圍表是一個數組,用於標誌__try __except() 範圍
數組元素為以下結構:
struct scopetable_entry { DWORD previousTryLevel; //之前一個Trylevel FARPROC lpfnFilter; //過濾表達式起始地址 FARPROC lpfnHandler; //異常處理函數地址 }
TryLevel
--------------------------------------------------------
多層Try時使用
__except_handler3按照trylevel來尋找scopetable_entry結構,找到後調用結構中的lpnFilter所指定的過濾表達式
_except_handler3工作:
---------------------------------------------------------
位於__try{}中的代碼發生異常時,異常分發函數便會調用_except_handler3這樣的處理函數,他的工作如下:
他的調用路徑為: RtlDispatchException-> ExecuteHandler -> ExecuteHandler2 -> _except_handler3 (透過_EXCEPTION_REGISTRATION結構) ->尋找scopetable的異常塊地址 __except{}
1. 將第二個參數pRegsitrationRecord從系統默認的EXCEPTION_REGISTRATION_RECORD結構強制轉化為包含擴展字段的_EXCEPTION_REGISTRATION
2. 先從pRegistration結構提取trylevel字段,傳給局部變量,按照它的值從scopetable索引出scopetable_entry結構
3. 從scopetable提出lpfnFilter字段,如不為空,調用函數->即評估過濾表達式,若為空跳到第五步
4. 判斷是否為exception_continue_search
5. 判斷scopetable_entry結構的previousTryLevel是否-1,不等於則用previousTryLevel交給局部變量,再重覆跳到第2步;否則跳到第六步
6. 返回DISPOSITION_CONTINUE_SEARCH, 讓系統繼續尋找其他異常處理器
兩者不同之處:
------------------------------------------------------------
SEH _Try{} _except{};
1. 由劃一的_except_handler3 來處理所有SEH異常,
2. 先壓入trylevel和scopetable指針 用作找出異常處理代碼, 棧中會形成以下結構
_struct _EXCEPTION_REGISTRATION{ struct _EXCEPTION_REGISTRATION *prev; //ebp-10 void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT,PEXCEPTION_RECORD); //ebp-c struct scoptable_entry *scoptable; //ebp-8 int trylevel; //ebp-4 int ebp; //ebp }
總結:
兩種方法製造SEH
1. 直接保存fs:[0],再插入新處理函數,指向新節點
2. 使用try..except方法,透過統一處理函數_except_handler3處理
分別: 前者由ExecuteHandle2->直接執行我們添加的異常處理函數, 後者為集中到_except_handler3中由scopetable決定調用哪一個級別(trylevel)的異常處理函數
RtlDispatchException(第一次異常處理)查找失敗會調用NtRaiseException引發第二輪異常處理,成功則調用NtContiue繼續讓程序運行,兩者都不會返回到KiUserExceptionDispatcher
參考:Windows軟件調試
Comments
Post a Comment