常見的測試手段有以下幾種:
黑箱測試
白盒測試
內建自檢(BIST,Built-InSelf-Test),是指在軟體代碼內部構建一些測試功能,這些功能可以在某些情況下執行,或者被自動化的測試工具所調用以發現問題。
壓力測試(Stresstesting),用於測試目標程式在高負載和低資源情況下的工作情況。
Windows下的驗證機制能夠類比極端和苛刻的運行環境,以便讓錯誤更容易暴露出來。
Windows下的驗證機制包含驅動程式驗證器和應用程式驗證器
先來看驅動程式驗證器(Driver Verifier),它主要用於驗證各種裝置驅動程式和核心模組。驅動驗證器主要是在核心檔案(NTOSKERNL.exe)中一系列核心功能和全域變數,其名字中大多是包含Verifier字樣,或者是以Vi和Vf開頭。例如用於驗證記憶體池的nt!VerifierFreePool,用於驗證降低IRQL操作的nt!VerifierKeLowerIrql.
(簡單介紹一下IRQL)
IRQL ( Interrupt Request level) 插斷要求層級,用來劃分windows下插斷要求的優先順序。常用的IRQL層級有:
PASSIVE_LEVEL: IRQL最低層級,沒有被屏蔽的中斷,在這個層級上,線程執行使用者模式,可以訪問分頁記憶體
APC_LEVEL: 在這個層級上,只有APC層級的中斷被屏蔽,可以訪問分頁記憶體。當有APC發生時,處理器提升到APC層級,這樣,就屏蔽掉其它APC,為了和APC執行一些同步,驅動程式可以手動提升到這個層級。比如,如果提升到這個層級,APC就不能調用。在這個層級,APC被禁止了,導致禁止一些I/O完成APC,所以有一些API不能調用。
DISPATCH_LEVEL: 這個層級,DPC 和更低的中斷被屏蔽,不能訪問分頁記憶體,所有的被訪問的記憶體不能分頁。因為只能處理非分頁式記憶體,所以在這個層級,能夠訪問的Api大大減少。
DIRQL: 一般的,更進階的驅動在這個層級上不處理IRQL,但是幾乎所有的中斷被屏蔽,這實際上是IRQL的一個範圍,這是一個決定某個驅動有更高的優先順序的方法。
驅動驗證器的設計原理:
驅動驗證器的基本設計思想是在驅動程式調用裝置驅動介面(DDI)函數時,對驅動程式執行各種檢查,看是否符合系統定義的設計需求。
Windows實際上採用的是通過修改被驗證驅動程式的輸入地址表(Import Address Table, 簡稱IAT)來掛接(HOOK)驅動程式的DDI調用,也就是之間我的部落格中介紹的IAT Hook方法。實現的方法是系統將被驗證驅動程式IAT表中的DDI函數地址替換為驗證函式的地址。所以當這個驅動程式調用DDI函數時,會調用對應的驗證函式。
驗證函式的作用是:
1. 更新計數器,或者全域變數
2. 檢測調用參數,或者做其他檢查,如果發現異常情況,就會調用KeBugCheckEx函數,然後通過藍屏機制來報告驗證失敗
3. 沒有發現問題,就會調用原來的函數。
再看一下驗證函式的執行過程:
1. 初始化過程:記憶體管理器的初始化函數MmInitSystem來調用驅動驗證器的初始化函數,來初始化驅動驗證器,它的工作是建立並初始化一個用來存放被驅動驗證驅動程式資訊的鏈表,windows使用全域變數MiSuspectDriverList來記錄這個鏈表。在可疑驅動鏈表的每個節點都是一個MI_VERIFIER_DRIVER_ENTRY結構,用來記錄一個被驗證的驅動程式。
2. 掛接驗證函式過程
當系統載入一個核心模組時,會調用MiApplyDriverVerifier函數,這個函數的任務就是查詢要載入的模組是否在可疑驅動列表中,如果在,則表示這是一個被驗證的驅動,並調用MiEnableVerifier函數,對它的IAT表進行修改。
對於OS Loader(NTLDR)載入的模組,當驅動驗證器初始化時(階段0初始化),驅動驗證器的MiInitialzeVerifyComponents函數會遍曆所有已經載入的模組,並以此對其調用MiApplyDriverVerifier。對於以後載入的驅動程式,記憶體管理器的工作函數(MiLoadSystemImage)會調用MiApplyDriverVerifier以給驅動驗證器以檢查機會。在掛接驗證函式後,一旦調用該核心功能,它的驗證函式就會首先被執行。
3. 驗證過程
Windows2000引入的驅動驗證器包含了一些基本的驗證項目
自動檢查:主要是判斷是否在合適的IRQL層級使用記憶體,是否有不恰當的切換棧,是否釋放包含活動計時器的記憶體池,是否在合適的IRQL層級擷取和釋放自旋鎖,當驅動卸載後系統也會檢測是否已經釋放了所有資源。
特殊記憶體池,從特殊的記憶體池來為驅動程式分配記憶體。系統對這個記憶體池具有增強監視功能,能夠發現上溢(overrun),下溢(underrun)和釋放後又訪問等錯誤情況。
強制的IRQL檢查,強制讓驅動程式使用的分頁記憶體失效,並監視驅動程式是否在錯誤的IRQL層級訪問分頁記憶體或持有自旋鎖。
低資源類比,對驅動程式的記憶體配置請求或其他資源請求,隨即返回失敗。
記憶體池追蹤(Pool Tracking),記錄驅動程式分配的記憶體,釋放時看其是否全部釋放。
I/O驗證,從特殊記憶體池分配IRP(I/O Request Packet),並監視驅動程式I/O的處理。
Windows XP引入了一些新的驗證項目
死結探測,增強I/O驗證,SCSI驗證
Windows Server 2003
IRP記錄(IRP Logging)
Windows Vista引入了
Driver Hang Detection, Completion Routine, Cancellation Routine
安全檢查
強制I/O請求等待解決
零散檢查
4. 啟動驅動那個驗證
在運行對話方塊中輸入Verifier,點擊OK可以進入圖形介面。Windows XP版本為嚮導模式。也可以使用命令列模式。介面如下所示:
啟動驅動驗證後,需要重新啟動系統,設定才會生效,因為系統只有在啟動期間才會初始化可疑驅動鏈表。Windows Vista對驅動驗證器做了增強,可以不重啟便可以使驗證生效。Windows 7 沒有驗證過,也應當如此吧。
每個驅動程式的資訊會記錄到可疑鏈表中這個驅動程式對應的MI_VERIFIER_DRIVER_ENTRY結構中,此外,全域資訊會被記錄到全域變數MmVerifierData中,可以使用WinDBG觀察這些值。WinDBG的工具包中葉設計了專門的擴充命令,!Verifier,具體可以查看winDBG的協助文檔。
應用程式驗證器
應用程式驗證器與驅動程式驗證器類似,也是使用IAT HOOK的方式,主要驗證它是否符合Windows SDK所定義的設計規範。應用驗證器(APP Verifier)包含在應用驗證工具包中,可以從windows XP的安裝光碟片或者微軟網站得到這個工具包。具體以後會詳細描述。