進行WDM/IFS開發時,常需要開發一個GUI來同使用者互動,並藉此控制驅動程式的行為,或者向使用者彙報底層資訊。一個著名的例子是FileMon。這個程式功能全面,但其不足也很致命:通過輪詢法來互動ring0/ring3的資料,經常造成系統資源不足,或者乾脆掛起。
在使用者模態下的處理序間通訊模式,可以移植到ring0/ring3的通訊中,使得kernel進程中的流程與使用者態進程進行同步。這種可能性來自於核心對象管理技術——所有的Mutex/Semaphore都是核心對象(這也是多線程程式設計所依賴的基本服務)。由於ring0/ring3中的進程都能訪問這些“基本”核心對象,利用他們來做同步是理所當然的。這樣的同步模式和平常使用沒有多少區別,只是ring0程式在訪問Mutex/Semaphore是步驟不同。
下文摘自一篇本科畢業論文,這個片斷剖析了殺毒引擎中核心/使用者態通訊技術,十分有借鑒價值。
註:文中"Hooksys.sys"為NT Service的binary image, "guidll.dll"為使用者態GUI進程的組件
Hooksys.sys中使用命名的訊號量來喚醒ring3級線程。具體做法如下:首先在guidll.dll中調用CreateSemaphore建立一個命名訊號量Hookopen並設為無訊號狀態,同時調用CreateThread建立一個線程。線程代碼的入口處通過調用WaitForSingleObject在此訊號量上等待被ring0鉤子函數喚醒查毒。驅動程式這邊則在初始化過程中通過未公開的常式ObReferenceObjectByName(/BaseNamedObjects/Hookopen)得到命名訊號量對象Hookopen的指標,當它攔截到檔案開啟請求時調用KeReleaseSemaphore將Hookopen置為有訊號狀態喚醒ring3級等待檢查開啟檔案的線程。其實guidll.dll共建立了兩個命名訊號量,還有一個Hookclose用於喚醒ring3級等待檢查關閉檔案的線程。
guidll.dll中使用命名的事件來喚醒暫時掛起等待查毒完畢的ring0鉤子函數。具體做法如下:Hooksys.sys在其初始化過程中通過ZwCreateEvent函數建立一組命名事件對象(此處必須合理設定安全性描述元,否則ring3線程將無法使用事件控制代碼)並得到其控制代碼,同時通過ObReferenceObjectByHandle得到控制代碼引用的事件對象的指標。然後Hooksys.sys將這一組事件控制代碼和指標對以及事件名儲存在備用鏈表的每個元素中:ring3使用控制代碼,ring0使用指標。當鉤子函數攔截到檔案請求時它首先喚醒ring3查毒線程,然後馬上調用KeWaitForSingleObject在一個事件/BaseNamedObjects/Hookxxxx上等待查毒的完成。而被喚醒的ring3查毒線程通過OpenEventA函數由事件名字得到其控制代碼,在結束查毒後發出一個SetEvent調用將事件置為有訊號狀態從而喚醒ring0掛起的鉤子函數。當然,以上討論僅限於開啟檔案操作,鉤子函數在攔截到其它檔案請求時並不調用KeWaitForSingleObject等待查毒的完成,而是喚醒ring3查毒線程後直接返回;相應的ring3查毒線程也就不必在查毒完成後調用SetEvent進行遠程喚醒。
補充一些未公布系統函數
With a little bit of research (and some help from
alert readers), we know the prototype for
ObReferenceObjectByName looks like this:
extern "C"
NTSYSAPI
NTSTATUS NTAPI 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
);
原文見http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/objectrefbyname.txt