對於CPU級的異常,CPU會通過IDT表尋找異常的處理函數,也就是KiTrapXX例程,會調用CommonDispatchException准備參數,然后調用內核分發函數KiDispatchException進行異常分發。
下面的圖是內核異常分發總管KiDispatchException處理的流程。
內核態異常的分發過程:
1.如果PreviousMode為KernelMode(0),那么對於第一輪處理機會,KiDispatchException會試圖先通知內核調試器來處理該異常。
2.內核變量KiDebugRoutine用來標識內核調試引擎交互的接口函數。當內核調試引擎啟用時,KiDebugRoutine指向內核調試引擎KdpTrap,這個函數會進一步把異常信息封裝為數據包發送給內核調試器,當調試內核調試引擎沒有啟用時,KiDebugRoutine指向KdpStub函數,簡單處理后返回
3.如果KiDebugRoutine返回TRUE,也就是內核引擎處理了異常,那么KiDispatchException便停止繼續分發,准備返回。如果KiDebugRoutine返回FALSE,也就是沒有處理該異常,那么KiDispatchException會調用RtlDispatchException函數,試圖尋找已經注冊的結構化異常處理器(SEH)。會遍歷異常登記鏈表,依次執行每個異常處理器。如果某個處理器除了了,RtlDispatchException返回TRUE,否則返回FALSE
4.RtlDispatchException返回FALSE,KiDispatchException會試圖給內核調試器第二次機會,如果KiDebugRoutine仍然返回FALSE,那么KiDispatchException會認為這是無人處理的異常,會調用KeBugCheckEx
用戶態異常的分發過程:
1.如果前一模式是用戶模式,即PreviousMode參數等於UserMode(1),對於第一次處理機會,KiDispatchException會試圖將異常分發給用戶態的調試器,如果DebugPort不為空,將異常發送給調試子系統,調試子系統將異常發送給調試器,如果處理了異常分發結束。
2.如果調試器沒有處理該異常,KiDispatchException修改用戶態棧,返回用戶層之后執行KiUserExceptionDispatcher,此函數會調用RtlDispatchException來尋找異常處理器,首先遍歷VEH,然后遍歷SEH,。如果RtlDispatchException返回FALSE,並且當前進程在被調試,那么KiUserExceptionDispatcher會調用ZwRaiseException並將FirstChance設置為FALSE,進行第二輪分發。如果沒有被調試,結束進程。
3.ZwRaiseException會通過內核服務NtRaiseException把異常傳遞給KiDispatchException來進行分發。第二次,將異常傳遞給調試器,如果沒有處理將異常分配給ExceptionPort異常端口監聽者處理,如果返回FALSE,結束進程。