寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
概述
之前我們介紹了異常的記錄。既然記錄好的異常,我肯定需要分發來處理它。對於異常有來自使用者模式的異常和核心態的異常。在我們之前學習中,它們的區別還是挺大的。無論是核心的異常,還是使用者的異常,異常處理的重心我們得放到KiDispatchException
這個函式的分析上,先介紹相對簡單的核心異常處理流程。
核心異常處理
對於核心異常處理,我們先看看KiDispatchException
的處理流程:
KeContextFromKframes
將TrapFrame
備份到Context
,為返回3環做準備。- 判斷先前模式,0是核心呼叫反之是使用者層呼叫。
- 是否是第一次機會。
- 是否有核心偵錯程式。
- 如果沒有或者核心偵錯程式不處理。
- 呼叫
RtlDispatchException
,查詢並呼叫異常處理函式。 - 如果返回
FALSE
,再次判斷是否有核心偵錯程式,有呼叫,沒有直接藍屏。
下面我們通過反彙編的形式過一遍這個流程:
push 390h
push offset stru_401FA0
call __SEH_prolog
mov eax, ___security_cookie
mov [ebp+security_cookie], eax
mov esi, [ebp+ExceptionRecord]
mov [ebp+var_ExceptionRecord], esi
mov ecx, [ebp+ExceptionFrame]
mov [ebp+var_ExceptionFrame], ecx
mov ebx, [ebp+TrapFrame]
mov [ebp+var_TrapFrame], ebx
db 3Eh
mov eax, ds:0FFDFF020h
inc [eax+_KPRCB.KeExceptionDispatchCount] ; 異常派發數加1
mov [ebp+Context.ContextFlags], 10017h
cmp [ebp+PreviousMode], 1
jz short IsUserMode
cmp _KdDebuggerEnabled, 0
jz short KernelModeAndNoDBG
函式開始開始賦值一波,然後判斷否是為3環,但我們是核心的,故不會跳轉,然後cmp _KdDebuggerEnabled, 0
判斷偵錯程式是否開啟,具體將會在除錯篇進行講解,這裡就不多說了。
這裡我們沒有偵錯程式,就會跳轉到下面的彙編程式碼:
KernelModeAndNoDBG: ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
; KiDispatchException(x,x,x,x,x)+68↑j
lea eax, [ebp+Context]
push eax ; ContextRecord
push ecx ; ExceptionFrame
push ebx ; TrapFrame
call _KeContextFromKframes@12 ; KeContextFromKframes(x,x,x)
mov eax, [esi]
cmp eax, STATUS_BREAKPOINT
jz short IsBreakPoint
cmp eax, KI_EXCEPTION_ACCESS_VIOLATION
jnz short OtherExceptionProc
mov dword ptr [esi], STATUS_ACCESS_VIOLATION
cmp [ebp+PreviousMode], 1
jnz short OtherExceptionProc
lea eax, [ebp+Context]
push eax ; Context
push esi ; ExceptionRecord
call _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)
test al, al
jnz loc_425AFD
cmp byte ptr ds:0FFDF0280h, 1
jnz short OtherExceptionProc
cmp dword ptr [esi+14h], 8
jnz short OtherExceptionProc
test _KeNumberProcessors+0Fh, 40h
jnz short loc_425A30
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov eax, [eax+_KTHREAD.ApcState.Process]
test [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 2
jnz short loc_425A30
test _KeNumberProcessors+0Fh, 80h
jnz short OtherExceptionProc
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov eax, [eax+_KTHREAD.ApcState.Process]
test [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 1
jnz short OtherExceptionProc
這裡就開始將TrapFrame
備份到Context
,首先判斷是否是int3
斷點,如果是的話就會跳轉到如下程式碼:
IsBreakPoint: ; CODE XREF: KiDispatchException(x,x,x,x,x)+89↑j
dec [ebp+Context._Eip]
OtherExceptionProc: ; CODE XREF: KiDispatchException(x,x,x,x,x)+90↑j
; KiDispatchException(x,x,x,x,x)+9C↑j ...
xor edi, edi
DebugRoutine: ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
該程式碼就會修正EIP
,繼續下面的流程。如果不是斷點,就判斷一系列的異常,並進行相應的處理,如果都不是就跳轉到所謂的OtherExceptionProc
進行處理:
DebugRoutine: ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
cmp [ebp+PreviousMode], 0
jnz short UserMode
cmp [ebp+FirstChance], 1
jnz short SecondChance
mov eax, _KiDebugRoutine
cmp eax, edi ; edi = 0
jz short NoDebugRoutine
push edi ; SecondChance
push edi ; PreviousMode
lea ecx, [ebp+Context]
push ecx ; ContextRecord
push esi ; ExceptionRecord
push [ebp+var_ExceptionFrame] ; ExceptionFrame
push ebx ; TrapFrame
call eax ; _KiDebugRoutine
test al, al
jnz loc_425AFD
NoDebugRoutine: ; CODE XREF: KiDispatchException(x,x,x,x,x)+114↑j
lea eax, [ebp+Context]
push eax ; Context
push esi ; ExceptionRecord
call _RtlDispatchException@8 ; RtlDispatchException(x,x)
cmp al, 1
jz short loc_425AFD
這裡同理,我們沒有偵錯程式,就會呼叫RtlDispatchException
這個函式。如果失敗,就會繼續執行下面的程式碼:
jz short loc_425AFD
SecondChance: ; CODE XREF: KiDispatchException(x,x,x,x,x)+10B↑j
mov eax, _KiDebugRoutine
cmp eax, edi
jz NoDebugRoutine_0
push 1 ; SecondChance
push edi ; PreviousMode
lea ecx, [ebp+Context]
push ecx ; ContextRecord
push esi ; ExceptionRecord
push [ebp+var_ExceptionFrame] ; ExceptionFrame
push ebx ; TrapFrame
call eax ; _KiDebugRoutine
test al, al
jnz short loc_425AFD
jmp NoDebugRoutine_0
就繼續判斷有沒有偵錯程式,但我這裡沒有,就會跳到下面的彙編程式碼:
NoDebugRoutine_0: ; CODE XREF: KiDispatchException(x,x,x,x,x)+149↑j
; KiDispatchException(x,x,x,x,x)+167↑j
push edi ; BugCheckParameter4
push ebx ; BugCheckParameter3
push [esi+_EXCEPTION_RECORD32.ExceptionAddress] ; BugCheckParameter2
push [esi+_EXCEPTION_RECORD32.ExceptionCode] ; BugCheckParameter1
push KERNEL_MODE_EXCEPTION_NOT_HANDLED ; BugCheckCode
call _KeBugCheckEx@20 ; KeBugCheckEx(x,x,x,x,x)
上面的程式碼,就是所謂的藍屏。有關核心異常處理就介紹到這裡,具體的細節將會到總結與提升篇進行介紹。
使用者異常處理
對於使用者異常處理,我們先看看KiDispatchException
的處理流程:
KeContextFromKframes
將TrapFrame
備份到Context
,為返回3環做準備。- 判斷先前模式,0是核心呼叫反之是使用者層呼叫。
- 是否是第一次機會。
- 是否有核心偵錯程式。
- 傳送給3環除錯。
有了核心異常的介紹,我就不贅述了,直接定位到關鍵程式碼:
inc [eax+_KPRCB.KeExceptionDispatchCount] ; 異常派發數加1
mov [ebp+Context.ContextFlags], 10017h
cmp [ebp+PreviousMode], 1
jz short IsUserMode
cmp _KdDebuggerEnabled, 0
jz short KernelModeAndNoDBG
這裡我們是使用者的異常,所以會跳轉:
IsUserMode: ; CODE XREF: KiDispatchException(x,x,x,x,x)+4C↑j
mov [ebp+Context.ContextFlags], 1001Fh
cmp _KeI386XMMIPresent, 0
jz short KernelModeAndNoDBG
mov [ebp+Context.ContextFlags], 1003Fh
KernelModeAndNoDBG: ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
; KiDispatchException(x,x,x,x,x)+68↑j
lea eax, [ebp+Context]
這裡就是賦值,不是我們關注的重點,我們繼續:
KernelModeAndNoDBG: ; CODE XREF: KiDispatchException(x,x,x,x,x)+55↑j
; KiDispatchException(x,x,x,x,x)+68↑j
lea eax, [ebp+Context]
push eax ; ContextRecord
push ecx ; ExceptionFrame
push ebx ; TrapFrame
call _KeContextFromKframes@12 ; KeContextFromKframes(x,x,x)
mov eax, [esi]
cmp eax, STATUS_BREAKPOINT
jz short IsBreakPoint
cmp eax, KI_EXCEPTION_ACCESS_VIOLATION
jnz short OtherExceptionProc
mov dword ptr [esi], STATUS_ACCESS_VIOLATION
cmp [ebp+PreviousMode], 1
jnz short OtherExceptionProc
lea eax, [ebp+Context]
push eax ; Context
push esi ; ExceptionRecord
call _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)
test al, al
jnz loc_425AFD
cmp byte ptr ds:0FFDF0280h, 1
jnz short OtherExceptionProc
cmp dword ptr [esi+14h], 8
jnz short OtherExceptionProc
test _KeNumberProcessors+0Fh, 40h
jnz short loc_425A30
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov eax, [eax+_KTHREAD.ApcState.Process]
test [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 2
jnz short loc_425A30
test _KeNumberProcessors+0Fh, 80h
jnz short OtherExceptionProc
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov eax, [eax+_KTHREAD.ApcState.Process]
test [eax+_EPROCESS.Pcb.___u27.Flags._bf_0], 1
jnz short OtherExceptionProc
由於我們是3環的,就會呼叫到KiCheckForAtlThunk
這個函式作用如下:
This marks the beginning of an x86 ATL thunk. Control will branch here if the ATL thunk was built in memory marked no execute. If that is the case, then the thunk code will be emulated and control returned to the thread.
看描述並不是我們關注的重點,繼續走到了我所標註的DebugRoutine
:
DebugRoutine: ; CODE XREF: KiDispatchException(x,x,x,x,x)+F7↑j
cmp [ebp+PreviousMode], 0
jnz short UserMode_0
cmp [ebp+FirstChance], 1
jnz short SecondChance
mov eax, _KiDebugRoutine
cmp eax, edi ; edi = 0
jz short NoDebugRoutine
push edi ; SecondChance
push edi ; PreviousMode
lea ecx, [ebp+Context]
push ecx ; ContextRecord
push esi ; ExceptionRecord
push [ebp+var_ExceptionFrame] ; ExceptionFrame
push ebx ; TrapFrame
call eax ; _KiDebugRoutine
test al, al
jnz loc_425AFD
由於我們是使用者態,就直接跳轉到UserMode_0
:
UserMode_0: ; CODE XREF: KiDispatchException(x,x,x,x,x)+105↑j
cmp [ebp+FirstChance], 1
jnz SecondChance_0
cmp _KiDebugRoutine, edi
jz short NoDebugRoutine_1
mov eax, large fs:_KPCR.PrcbData.CurrentThread
mov eax, [eax+_KTHREAD.ApcState.Process]
cmp [eax+_EPROCESS.DebugPort], edi
jz short NoDebugPort
push 1
lea eax, [ebp+Context]
push eax
push esi
call _KdIsThisAKdTrap@12 ; KdIsThisAKdTrap(x,x,x)
test al, al
jz short NoDebugRoutine_1
NoDebugPort: ; CODE XREF: KiDispatchException(x,x,x,x,x)+18D↑j
push edi ; SecondChance
push dword ptr [ebp+PreviousMode] ; PreviousMode
lea eax, [ebp+Context]
push eax ; ContextRecord
push esi ; ExceptionRecord
push [ebp+var_ExceptionFrame] ; ExceptionFrame
push ebx ; TrapFrame
call _KiDebugRoutine
test al, al
jz short NoDebugRoutine_1
由於我們不是除錯模式,會跳到NoDebugRoutine_1
:
NoDebugRoutine_1: ; CODE XREF: KiDispatchException(x,x,x,x,x)+17C↑j
; KiDispatchException(x,x,x,x,x)+1A0↑j ...
push edi ; SecondChance
push 1 ; DebugException
push esi ; ExceptionRecord
call _DbgkForwardException@12 ; DbgkForwardException(x,x,x)
test al, al
jnz short EndProc
mov [ebp+var_SecondChance], edi
DbgkForwardException
就是向3環派發處理異常了,如果沒有偵錯程式的話,就會繼續往下走,最終會到如下程式碼:
mov [ebx+_KTRAP_FRAME.SegFs], eax
and [ebx+_KTRAP_FRAME.SegGs], 0
mov eax, _KeUserExceptionDispatcher
mov [ebx+_KTRAP_FRAME._Eip], eax
or [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh
這裡就開始修改KTRAP_FRAME
開始回到3環進行處理,剩餘的具體細節的講解同樣將會在總結與提升進行。
下一篇
異常篇—— VEH 與 SEH