* 通過 NTSTATUS 獲得相應的字串
使用函數RtlNtStatusToDosError可以獲得與NTSTATUS相對應的Windows錯誤碼。
微軟網站上說用API函數 FormatMessage 可獲得相應的字串,但是在調用前必須先用 LoadLibrary 載入 "NTDLL.DLL"。
文章連結 - http://support.microsoft.com/kb/259693
* 記憶體操作
盡量不要使用C運行時提供的記憶體操作函數,應該使用 RtlCopyMemory;RtlFillMemory;RtlZeroMemory 以便於程式移植。
* 字串操作
盡量不要使用C運行時提供的字串操作函數,應該使用 RtlString{xxx};RtlUnicodeString{xxx} 系列安全字串操作函數,以便於程式移植。
在包含安全字串操作的標頭檔 ntstrsafe.h 之前必須定義宏 NTSTRSAFE_LIB,以下是 ntstrsafe.h 裡的原文:
//
// The following steps are *REQUIRED* if ntstrsafe.h is used for drivers on:
// Windows 2000
// Windows Millennium Edition
// Windows 98 Second Edition
// Windows 98
//
// 1. #define NTSTRSAFE_LIB before including this header file.
// 2. Add ntstrsafe.lib to the TARGET_LIBS line in SOURCES
//
// Drivers running on XP and later can skip these steps to create a smaller
// driver.
//
* 卷上的檔案系統路徑
使用者模式下的檔案系統路徑必須在前面加上"\??\"才能被核心模式的程式識別,例如"\??\C:\file"。
* 操作檔案
用 Zw{xxx}File 系列函數可以實作類別似使用者模式的檔案操作。
* 獲得系統時間
用 KeQuerySystemTime 獲得從 1601-01-01 以來的時間計數,單位為100納秒,時區為格林威治。
用 ExSystemTimeToLocalTime 將格林威治時間值轉換成本地時區時間值。
用 RtlTimeToTimeFields 將時間值轉換成 TIME_FIELDS 結構。
* 測試當前作業系統的版本資訊
使用 PsGetVersion 獲得作業系統版本。
最低相容到Windows XP的程式應該使用 RtlGetVersion 獲得作業系統版本。
使用 RtlIsNtDdiVersionAvailable 獲得裝置驅動介面版本。
* 檔案系統驅動處理相對路徑
處理IRP_MJ_CREATE時,如果IRP的FileObject使用的相對路徑,可以通過FileObject的RelatedFileObject獲得相對路徑所在的目錄資訊。
* 鏈表操作
Windows DDK提供了SINGLE_LIST_ENTRY結構和LIST_ENTRY結構來支援單向和雙向的鏈表操作。
* 操作線程間的共用資源
在使用者模式下通常使用互斥對象來確保共用資源在同一時間下只能一個線程訪問,其它要訪問共用資源的線程則進入睡眠狀態。
在核心模式中雖然也有互斥對象,但是還提供了自旋鎖。
自旋鎖和互斥對象的功能類似,但是它不會讓等待的線程進入睡眠狀態,而是讓等待的線程一直在迴圈查詢自旋鎖的狀態。
所以當訪問共用資源的過程佔用時間很短時,應該使用自旋鎖,這樣可以降低系統開銷。
注意:獲得自旋鎖的所有權時,當前IRQL會變為DISPATCH_LEVEL,直到釋放所有權為止。
* FILE_SUPERSEDE 和 FILE_OVERWRITE_IF 的區別
ZwCreateFile 函數的 CreateDisposition 參數可以設定為 FILE_SUPERSEDE 或者 FILE_OVERWRITE_IF,似乎都是覆蓋老檔案,但還是有一些小區別的。
FILE_SUPERSEDE - 先將老檔案刪除,在建立一個檔案。
FILE_OVERWRITE_IF - 不刪除老檔案,只是清空它的內容,其它屬性保留。
* 線程操作
使用 PsCreateSystemThread 建立線程。
使用 PsGetCurrentThread 獲得當前線程控制代碼。
使用 KeDelayExecutionThread 讓當前線程睡眠一段指定的時間。
* 核心模式裡的幾種互斥對象
Mutex - 是標準的訊號對象,支援所有等待訊號相關的函數;可以在一個線程內遞迴擷取所有權而不被阻塞;操作速度慢;所有者能夠收到APC;所有者不能被換出記憶體。
Fast Mutex - 不是標準的訊號對象;不能被遞迴擷取所有權;操作速度快;取得所有權後IRQL會變為APC_LEVEL直到釋放所有權為止;從Windows 2000開始提供。
Guarded Mutexes - 不是標準的訊號對象;不能被遞迴擷取所有權;操作速度更快;所有者不能夠收到APC;從Windows Server 2003開始提供。
* 訪問註冊表
所有註冊表路徑必須以"\REGISTRY"開頭,可開啟的根註冊表鍵有"\REGISTRY\MACHINE"和"\REGISTRY\USERS",分別對應使用者模式下的"HKEY_LOCAL_MACHINE"和"MACHINE HKEY_USERS"。
用 Zw{xxx}Key 和 Zw{xxx}Value 系列函數可以實作類別似使用者模式的註冊表操作。
* 用DDK工具編譯時間顯示編譯器警告資訊
使用DDK的build編譯器時預設情況下不會顯示編譯的警告資訊,需要加上-w參數才行。
* 開啟檔案系統對象的特殊方式
檔案系統驅動接收到IRP請求IRP_MJ_CREATE時,如果IrpSp->Flags指定了SL_OPEN_TARGET_DIRECTORY,則表示並不是真的要開啟指定的檔案系統對象,而是要檢查對象是否可以刪除已經它所在的目錄是否可以進行建立操作。 通常這樣的請求會發生在重新命名檔案系統對象之前。
* 通過控制代碼獲得對象資訊
通過函數ObReferenceObjectByHandle可以獲得控制代碼引用的對象資訊。
* IRQL
IRQL(Interrupt ReQuest Level)表示代碼啟動並執行插斷要求等級,Windows定義了0-31個IRQL,除了16個硬體中斷,還虛擬了16個軟體中斷,IRQL越高代碼啟動並執行優先順序也就越高。
可以使用函數KeGetCurrentIrql、KeLowerIrql和KeRaiseIrql對IRQL進行相應的操作。
* 奇怪的IO控制碼
存放裝置的驅動程式有時候會收到0x004D0008這類IO控制碼,通過控制碼的組成方式能夠得出裝置類型為0x4D,但是在ntddk.h裡並沒有發現定義為0x4D的裝置類型。其實這是存放裝置載入管理器的裝置類型,開啟mountmgr.h查看,可以發現如下宏定義:
#define MOUNTDEVCONTROLTYPE ((ULONG) 'M')
這就是存放裝置載入管理器的裝置類型,字母'M'的ASCII碼就是0x4D,這個檔案裡還定義了存放裝置載入管理器的IO控制碼。
* 獲得一個核心對象的名稱
很多核心對象在建立時都指定了名稱,例如驅動程式對象、裝置對象和檔案對象等,可以通過調用函數ObQueryNameString獲得指定對象的完整名稱。
* 開啟檔案系統對象的特殊方式
檔案系統驅動接收到IRP請求IRP_MJ_CREATE時,如果IrpSp->Flags指定了SL_OPEN_TARGET_DIRECTORY,則表示並不是真的要開啟指定的檔案系統對象,而是要檢查對象是否可以刪除已經它所在的目錄是否可以進行建立操作。 通常這樣的請求會發生在重新命名檔案系統對象之前。
* 檔案系統驅動處理相對路徑
處理IRP_MJ_CREATE時,如果IRP的FileObject使用的相對路徑,可以通過FileObject的RelatedFileObject獲得相對路徑所在的目錄資訊。
* 提供正確的儲存空間資訊
處理IRP_MJ_QUERY_VOLUME_INFORMATION時,如果要查詢的資訊類型為FileFsSizeInformation或者FileFsFullSizeInformation,必須沒有提供正確的儲存空間參數,可能會造成資源管理員查看檔案屬性時崩潰。尤其是資訊結構的BytesPerSector成員,千萬不要設定為0。
* 支援檔案的記憶體映射
Windows的檔案記憶體映射機制能夠緩解檔案的IO次數,比如CreateFileMapping函數。要實現最基本的檔案對應支援,檔案系統驅動必須在處理IRP_MJ_CREATE時給FileObject的SectionObjectPointer成員賦值,並設定SectionObjectPointer的成員都為NULL值。FileObject的FsContext成員必須指向一塊已指派的並全部填充為0的記憶體區,這塊記憶體區的大小不能小於FSRTL_COMMON_FCB_HEADER類型的位元組數。在處理IRP_MJ_CLEANUP時,如果FileObject的SectionObjectPointer的DataSectionObject成員不為NULL,應該調用CcPurgeCacheSection函數清除緩衝管理器產生的資料。
* 正確處理IRP_MJ_CREATE
檔案系統驅動的IRP_MJ_CREATE不僅要處理開啟檔案或目錄,還要處理開啟卷。如果FileObject的FileName位元組數為0,則表示要開啟的是卷。