今天花了近一天的時間都在調試ObReferenceObjectByName這個內核函數,結果卻無比狗血。故寫篇文章記錄一下這一天蛋疼的調試記錄。
其實說是調試,其實是一直編譯連接不通過。今天在研究楚狂人(其實是360的大牛wowocock寫的代碼,楚狂人進行了簡化處理)的鍵盤過濾驅動代碼。
有一段代碼是要獲得kbdclass這個內核驅動對象的對象地址,然後獲得整個設備鏈,將過濾設備綁定在所有設備之上。其中要用到ObReferenceObjectByName()這個內核函數,
代碼如下:
POBJECT_TYPE IoDriverObjectType;
NTSTATUS ObReferenceObjectByName(
IN PUNICODE_STRING ObjectPath,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID *ObjectPtr);
// 初始化一個字符串,就是Kdbclass驅動的名字。
RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME);
// 請參照前面打開設備對象的例子。只是這裏打開的是驅動對象。
status = ObReferenceObjectByName (
&uniNtNameString,
OBJ_CASE_INSENSITIVE,
NULL,
0,
IoDriverObjectType,
KernelMode,
NULL,
&KbdDriverObject
);
編譯調試到
status = ObReferenceObjectByName (
&uniNtNameString,
OBJ_CASE_INSENSITIVE,
NULL,
0,
IoDriverObjectType,
KernelMode,
NULL,
(PVOID*)&KbdDriverObject
);
這個地方怎麼都編譯不過了,由於是WDK編譯環境,所以不能提示是什麼地方出現的問題。由於ObReferenceObjectByName ()這個內核函數比較特殊,WDK幫助文檔裏沒有對它進行公開聲明,所以要自己聲明,當然這也在幫助文檔裏查不到它的任何信息,百思不得其解,google之。網上有人說要加extern “C”關鍵字,應爲WDK庫文件都是C語言編譯連接的文件,而VS編譯時則是自動按照C++的編譯規則進行編譯,而C++支持重載,故編譯後的目標文件中函數符與C語言庫文件不符合,故會出現連接錯誤。
看後大爲欣喜,改之,可是還是編譯不過,繼續搜索,發現有人說Windows7下這個函數和XP有些不同,POBJECT_TYPE ObjectType,參數要換成指針的指針,照樣子做了以後還是不行。
就這樣在網上查了大半天,還看了WRK的一些源代碼,都沒有解決。故想放棄使用ObReferenceObjectByName()這個函數,取而代之換ZwCreateFile()和ObReferenceObjectByHandle()這兩個內核函數間接完成獲得驅動對象地址的功能。
查看WDK幫助文檔發現ObReferenceObjectByHandle()這個函數和ObReferenceObjectByName()參數列表十分接近,但是在最後PVOID* Object參數傳入時進行了(PVOID*)強制轉換,起初沒有怎麼注意。這個方法果然行得通,程序成功編譯並且運行成功。這個時候我又注意到那個(PVOID*)強制轉換,於是我就是這不要這個強制轉換進行編譯,果然又不通了,我突然意識到內核程序對自動強制轉換的限制應該和普通應用程序不通,於是馬上換成ObReferenceObjectByName()這個函數進行測試,果然通了!
原來困擾了一天的問題竟然如此簡單,這又讓我突然想到昨天寫程序時 RtlStringCchPrintfW函數也遇到了類似的情況,還沒有去驗證這個假想是否正確。總之今天一天的調試經歷真是蛋疼中不乏學到了東西啊。