1. 程式人生 > >第八章——Windows下異常處理-異常處理基本概念

第八章——Windows下異常處理-異常處理基本概念

前言: 中斷和異常的區別,中斷是由外部硬體裝置或非同步事件產生的。異常是由內部事件產生,可以分為故障,陷阱和終止三類。 由CPU引發的異常成為硬體異常,例如訪問一個無效的記憶體地址。由作業系統或應用程式引發的異常成為軟體異常。 我們也可以主動丟擲一個異常,通過RaiseException()函式 void WINAPI RaiseException(   _In_       DWORD     dwExceptionCode,                //標識所引發異常的程式碼   _In_       DWORD     dwExceptionFlags,                //異常是否繼續執行的標識   _In_       DWORD     nNumberOfArguments,        //附加資訊   _In_ const ULONG_PTR *lpArguments                  //附加資訊 );   異常處理基本過程 1.IDT     在windows啟動後,在保護模式下,有中斷或者異常發生時,CPU會通過中斷描述符表來查詢處理函式(IDT表)     IDT表共有256項,在32位下每個IDT項的長度為8位元組,在64位下長度為64位元組,作業系統會在啟動階段初始化這個表     IDT的位置和長度由CPU和IDTR暫存器藐視,IDTR暫存器有48位,其中高32位是基地址。低16位是表的長度       IDT的每一項都是一個門結構,其中有三種門:
  • 任務門:用於CPU任務切換(TSS)
  • 中斷門:用於描述中斷處理程式入口
  • 陷阱門:主要用於描述異常處理程式入口
  2.異常處理的準備工作 一、當有中斷或異常發生時,CPU會根據中斷型別號轉而執行對應的中斷處理程式。CPU會在IDT中查詢對應的函式來處理,各個異常處理函式不僅僅處理異常還需要將異常資訊封裝,以便對後續處理(_EXCEPTION_RECORD結構體記錄封裝的異常資訊) 這個結構體主要描述了異常的程式碼,異常標誌,還有異常發生的地址,沒有描述異常發生時,具體的異常環境。具體的異常環境在另一個結構體_KTRAP_FRAME(陷阱幀)中,這裡麵包括了每個暫存器的狀態,這個結構體通常在核心中,而我們編寫偵錯程式的時候,通常用的是context結構體 二、上述總而言之是對異常資訊的封裝,封裝完成後,系統會呼叫nt!KiDispatchException來處理異常,所以分析KiDispatchException函式就可以瞭解異常是如何被處理的       函式原型:                 KiDispatchException (                     IN PEXCEPTION_RECORD ExceptionRecord,            //異常結構資訊(就是上面_EXCEPTION_RECORD結構體內容)                     IN PKEXCEPTION_FRAME ExceptionFrame,                                   IN PKTRAP_FRAME TrapFrame,                                //傳送異常的陷阱幀(核心:_KTRAP_FRAME,三環:context)                     IN KPROCESSOR_MODE PreviousMode,                 //傳送異常時CPU模式是核心還是使用者                     IN BOOLEAN FirstChance                                        //是否第一次發生異常                     )                      3.核心態的異常處理過程     PreviousMode欄位是KernelMode時候,表示核心模式下產生異常,具體處理步驟:     ①系統會先檢測是否有核心偵錯程式,如果沒有,就跳過這一步,如果有,就把異常處理的許可權交給核心偵錯程式,並且註明是第一次來執行的這個異常(FirstChance),核心偵錯程式如果處理了該異常就繼續回到原來異常地方繼續執行,如果沒有處理則發生中斷,將控制權交給使用者,使用者決定是否繼續處理     ②如果不存在核心偵錯程式,或者第一次的異常沒有被處理,系統就會呼叫RtDispatchException,這裡會根據使用者註冊的SEH異常處理結構來處理(注意,核心態下只有SEH)     ③上述過後,如果異常處理了,程式繼續執行,如果第一次沒有處理,則進行第二次異常處理,系統會再將控制權交給核心偵錯程式     ④如果不能存在核心偵錯程式,或者第二次處理失敗了,這時系統就會呼叫KeBugCheckEx產生一個錯誤碼為"KERNEL_MODE_EXCEPTION_NOT_HANDLED"藍屏錯誤   4.使用者態的異常處理過程     PreviousMode欄位是UserMode時候,表示使用者模式下產生異常,此時KiDispatchException函式仍然會檢測核心偵錯程式是否存在,如果核心偵錯程式存在,系統還是會將控制權交給核心偵錯程式進行處理,核心偵錯程式對使用者態的程式是可以除錯,並且還不依賴程序的除錯視窗。但是在大多數情況下,核心偵錯程式是不除錯使用者態程式,所以KiDispatchException函式還是會和核心偵錯程式一樣,分兩次在使用者態下處理異常資訊,具體處理步驟:     ①如果存在異常的程式被除錯,系統會將異常資訊傳送給正常除錯的使用者態偵錯程式,給偵錯程式一次機會,如果沒有被除錯,跳過此步     ②如果不存在使用者偵錯程式,或者偵錯程式未處理該異常,那麼棧上放置EXCEPTION_RECORD和CONTEXT,並將控制權返回使用者態的KiDispatchException函式,這一步涉及SEH,VEH頂級異常處理,如果偵錯程式存在,頂級異常處理函式就會被跳過,否則就會被頂級處理函式接管     ③如果RtlDispatchException函式在呼叫使用者態的異常處理過程中未處理該異常,那麼異常處理過程會再次返回kisdispathchexception,進行第二次異常分發     ④,如果第二次還沒有處理,則 kisdispathchexception會嘗試將異常分發給程序的異常埠進行處理,該埠由csrss.exe進行監聽,如果監聽到錯誤,則會顯示一個應用程式錯誤,如果偵錯程式還不能附加其上,則會呼叫exitprocess結束程序