用户层和内核层异常的处理流程
文章目錄
- 內核層異常的處理流程
- 用戶層異常和內核層異常
- KiDispatchException函數詳解
- RtlDispatchException函數的執行流程
- 用戶層異常的處理流程
- 用戶異常的處理流程
內核層異常的處理流程
之前已經了解過異常處理機制的執行流程:異常記錄 異常分發 異常的處理。這次我們學習一下內核異常的分發與處理。
用戶層異常和內核層異常
異常可以發生在用戶空間,也可以發生在內核空間。無論是CPU異常還是模擬異常,是用戶層異常還是內核層異常,都要通過KiDispatchException函數進行分發。
我們首先來了解內核異常是如何分發的。
KiDispatchException函數詳解
用IDA打開ntkrnlpa.exe,找到KiDispatchException函數
核心的功能從這里開始,這個函數做的第一件事就是將Trap_frame備份到context 為返回三環做準備
接著判斷先前模式,如果是0,則說明是內核層產生的異常,如果是1,則說明是用戶層產生的異常。
這個函數用于處理用戶層和內核層所有的異常,這個地方用于區分是三環的異常還是零環的異常。
接著判斷是否是第一次調用
接著判斷是否有內核調試器,如果有內核調試器,會先調用內核調試器。
如果這個函數的返回值為1,說明內核調試器已經處理,就將CONTEXT再轉成Trap_Frame直接返回。
如果調試器沒有處理,也就是返回0,直接跳轉
如果沒有內核調試器,或者內核調試器沒有處理,就會調用RtlDispatchException函數,這個函數專門負責調用異常處理函數來處理異常
接下來會判斷RtlDispatchException這個函數的返回值,如果返回失敗,會再次判斷是否有內核調試器。如果有內核調試器就調用這個內核調試器,如果沒有的話則會進行跳轉
系統直接藍屏
KiDispatchException函數執行流程總結
RtlDispatchException函數的執行流程
RtlDispatchException在內部會調用RtlpGetRegistrationHead,繼續跟進這個函數
RtlpGetRegistrationHead將FS:0保存到eax之后返回。我們知道FS:0在零環的時候指向的是KPCR,而KPCR的第一個成員就是ExceptionList
ExceptionList這個成員是一個指針,,它指向了一個結構體 _EXCEPTION_REGISTRATION_RECORD
typedef struct _EXCEPTION_REGISTRATION_RECORD {struct _EXCEPTION_REGISTRATION_RECORD *Next;PEXCEPTION_ROUTINE Handler; } EXCEPTION_REGISTRATION_RECORD;這個結構體有兩個成員,第一個成員指向下一個_EXCEPTION_REGISTRATION_RECORD,如果沒有下一個_EXCEPTION_REGISTRATION_RECORD結構體,那么這個地方的值是-1。第二個成員是異常處理函數。
RtlDispatchException的作用就是遍歷異常鏈表,調用異常處理函數,如果異常被正確處理了,該函數返回1。如果當前異常處理函數不能處理該異常,那么調用下一個異常處理函數,以此類推。如果到最后也沒有函數能處理這個異常,返回0。
用戶層異常的處理流程
異常如果發生在內核層,處理起來比較簡單,因為異常處理函數也在0環,不用切換堆棧,但是如果異常發生在3環,就意味著必須要切換堆棧,回到三環執行異常處理函數。
切換堆棧的處理方式與用戶APC的執行過程幾乎是一樣的,唯一的區別就是執行用戶APC時返回3環后執行的函數是KiUserApcDispatcher,而異常處理時返回3環后執行的函數是KiUserExceptionDispatcher
用戶異常的處理流程
下面來分析KiDispatchException對于用戶層異常是如何處理的。
VOID KiDispatchException(ExceptionRecord, ExceptionFrame, TrapFrame, PreviousMode, FirstChance)首先將Trap_Frame備份到Context結構體
.text:004256C3 cmp byte ptr [ebp+arg_C], 0再接著判斷先前模式,如果是0 說明是用戶層的異常,如果是1 就是用戶層的異常
.text:0042572E cmp [ebp+FirstChance_1], 1接著會判斷是否是第一次調用
.text:00425738 cmp _KiDebugRoutine, edi ; 判斷是否存在內核調試器再次判斷是否存在內核調試器
.text:00425777 call _KiDebugRoutine ; 調用內核調試器如果存在內核調試器則調用內核調試器,將異常信息發給內核調試器
.text:004257AD push edi .text:004257AE push 1 .text:004257B0 push esi .text:004257B1 call _DbgkForwardException@12 ; DbgkForwardException(x,x,x)如果沒有內核調試器,或者內核調試器沒有處理,就會調用DbgkForwardException函數將異常發送給3環調試器。3環調試器如果不存在或者沒有處理的話,就會開始修改寄存器,準備返回3環
.text:004258C7 mov eax, _KeUserExceptionDispatcher; .text:004258CC mov [ebx+68h], eax ;其中最關鍵的修改是這兩行,這里eax的值是一個全局變量KeUserExceptionDispatcher;在操作系統初始化的時候,會給這個全局變量賦一個值,這個值就是ntdll.KiUserExceptionDispatcher函數
流程總結:
總結
以上是生活随笔為你收集整理的用户层和内核层异常的处理流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CPU和软件模拟异常的执行流程
- 下一篇: 编译器扩展SEH