Win64 驱动内核编程-15.回调监控注册表
回調監控注冊表
????在?WIN32?平臺上,監控注冊表的手段通常是?SSDT?HOOK。不過用?SSDT?HOOK
的方式監控注冊表實在是太麻煩了,要?HOOK?一大堆函數,還要處理一些?NT6?系統有而?NT5?系統沒有的函數。下面我就來介紹一種完勝?SSDT?HOOK?監控注冊表的方法,效果跟?SSDT?HOOK?一樣好。這個方法就是使用微軟推薦的注冊表監控函數:CmRegisterCallbak。此函數其實在?XP?系統上就有了,不過那時功能不完善,只能簡單的禁止或允許,無法獲得完整的注冊表修改信息(即做不到監控);在?VISTA?以及之后的系統里,微軟對此函數做了相當大的改進,使之能獲得完整的注冊表修改信息。本文最后實現的效果就是:把“注冊表編輯器”(regedit.exe)所有對注冊表添加、刪除、重命名的操作都通過?DbgView?打印
出來,并拒絕訪問(只針對?regedit.exe?是因為系統對注冊表的操作太頻繁了,這么做是為了方便大家實驗)。
?
函數原型:
NTSTATUS?CmRegisterCallback
(
_In_?PEX_CALLBACK_FUNCTION?Function,
_In_opt_?PVOID?Context,
_Out_?PLARGE_INTEGER?Cookie
);
這三個參數分別為:回調函數的地址,隨便設置的值(直接傳入?NULL?即可),回調的句柄。相反還有個函數用于銷毀回調,它是?CmUnRegisterCallback,原型如下
NTSTATUS?CmUnRegisterCallback(?_In_?LARGE_INTEGER?Cookie);
CmUnRegisterCallback?函數唯一的的參數就是?cookie,也就是我所說的“回調的句柄”。創建和銷毀回調的代碼如下:
LARGE_INTEGER?CmHandle;
NTSTATUS?CmSt;
CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle);
if(NT_SUCCESS(CmSt))
????DbgPrint("CmRegisterCallback?SUCCESS!");
else
????DbgPrint("CmRegisterCallback?Failed!");
CmUnRegisterCallback(CmHandle);
接下來看看回調函數的原型:
NTSTATUS?RegistryCallback
(
_In_?PVOID?CallbackContext,
_In_opt_?PVOID?Argument1,?//操作類型(只是操作編號,不是指針)
_In_opt_?PVOID?Argument2?//操作詳細信息的結構體指針
)
CallbackContext?基本可以忽略,重要的就是下面的兩個參數?Argument1和?Argument2。1?Argument1??記錄的是操作類型(這個參數不是指針,只是操作類型的編號而已),2?Argument2??記錄的是有關操作信息的結構體指針。接下來舉個例子。比如我們已經注冊了一個注冊表回調,當有刪除注冊表項的操作發生時,我們注冊的回調就會被調用,Argument1?的信息是?RegNtPreDeleteKey(pre?是“操作前”的意思),Argument2?的信息是一個指向
REG_DELETE_KEY_INFORMATION?結構體的指針。當操作完成后,我們的注冊表回調又會被調用一次,此時?Argument1?的信息是?RegNtPostDeleteKey(post?是“操作后”的意思),Argument2?的信息是一個指向REG_POST_OPERATION_INFORMATION?結構體的指針。在所有的結構體里,有一項是肯定有的,就是?Object,它是你操作了那個項或者根項的對象指針(相對于新建項而言,就是根項的對象指針;相對于新建/設置/刪除/重命名鍵值和刪除項而言,就是項的對象指針)。
????需要注意的是,此函數如果返回?STATUS_SUCCESS,注冊表操作就會繼續執
行,如果返回?STATUS_ACCESS_DENIED,注冊表操作就不會執行執行了。這樣子
就達到了“監控”的效果。最終效果如下:
最后附上測試代碼:
?
注冊回調: CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle); 注銷回調: if(NT_SUCCESS(CmSt)) CmUnRegisterCallback(CmHandle);回調函數處理,同時禁止regedit一切操作: #include <ntddk.h>#define REGISTRY_POOL_TAG 'pRE'NTKERNELAPI NTSTATUS ObQueryNameString (IN PVOID Object,OUT POBJECT_NAME_INFORMATION ObjectNameInfo,IN ULONG Length,OUT PULONG ReturnLength );NTKERNELAPI NTSTATUS RtlUnicodeStringCopy (__out PUNICODE_STRING DestinationString,__in PUNICODE_STRING SourceString );NTKERNELAPI UCHAR* PsGetProcessImageFileName(PEPROCESS Process);LARGE_INTEGER CmHandle; NTSTATUS CmSt;BOOLEAN IsProcessName(char *string, PEPROCESS eprocess) { char xx[260]={0}; strcpy(xx,PsGetProcessImageFileName(eprocess)); if(!_stricmp(xx,string)) return TRUE; else return FALSE; }BOOLEAN GetRegistryObjectCompleteName(PUNICODE_STRING pRegistryPath, PUNICODE_STRING pPartialRegistryPath, PVOID pRegistryObject) { BOOLEAN foundCompleteName = FALSE; BOOLEAN partial = FALSE; if((!MmIsAddressValid(pRegistryObject)) || (pRegistryObject == NULL)) return FALSE; /* Check to see if the partial name is really the complete name */ if(pPartialRegistryPath != NULL) { if( (((pPartialRegistryPath->Buffer[0] == '\\') || (pPartialRegistryPath->Buffer[0] == '%')) ||((pPartialRegistryPath->Buffer[0] == 'T') && (pPartialRegistryPath->Buffer[1] == 'R') && (pPartialRegistryPath->Buffer[2] == 'Y') && (pPartialRegistryPath->Buffer[3] == '\\'))) ) { RtlCopyUnicodeString(pRegistryPath, pPartialRegistryPath); partial = TRUE; foundCompleteName = TRUE; } } if(!foundCompleteName) { /* Query the object manager in the kernel for the complete name */ NTSTATUS status; ULONG returnedLength; PUNICODE_STRING pObjectName = NULL; status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, 0, &returnedLength ); if(status == STATUS_INFO_LENGTH_MISMATCH) { pObjectName = ExAllocatePoolWithTag(NonPagedPool, returnedLength, REGISTRY_POOL_TAG); status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)pObjectName, returnedLength, &returnedLength ); if(NT_SUCCESS(status)) { RtlCopyUnicodeString(pRegistryPath, pObjectName); foundCompleteName = TRUE; } ExFreePoolWithTag(pObjectName, REGISTRY_POOL_TAG); } } return foundCompleteName; }NTSTATUS RegistryCallback (IN PVOID CallbackContext,IN PVOID Argument1,IN PVOID Argument2 ) { long type; NTSTATUS CallbackStatus=STATUS_SUCCESS; UNICODE_STRING registryPath; registryPath.Length = 0; registryPath.MaximumLength = 2048 * sizeof(WCHAR); registryPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, registryPath.MaximumLength, REGISTRY_POOL_TAG); if(registryPath.Buffer == NULL) return STATUS_SUCCESS; type = (REG_NOTIFY_CLASS)Argument1; switch(type) { case RegNtPreCreateKeyEx: //出現兩次是因為一次是OpenKey,一次是createKey { if(IsProcessName("regedit.exe",PsGetCurrentProcess())) { GetRegistryObjectCompleteName(?istryPath,NULL,((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject); DbgPrint("[RegNtPreCreateKeyEx]KeyPath: %wZ",?istryPath); //新鍵的路徑 DbgPrint("[RegNtPreCreateKeyEx]KeyName: %wZ", ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);//新鍵的名稱 CallbackStatus=STATUS_ACCESS_DENIED; } break; } case RegNtPreDeleteKey: { if(IsProcessName("regedit.exe",PsGetCurrentProcess())) { GetRegistryObjectCompleteName(?istryPath,NULL,((PREG_DELETE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[RegNtPreDeleteKey]%wZ",?istryPath); //新鍵的路徑 CallbackStatus=STATUS_ACCESS_DENIED; } break; } case RegNtPreSetValueKey: { if(IsProcessName("regedit.exe",PsGetCurrentProcess())) { GetRegistryObjectCompleteName(?istryPath,NULL,((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[RegNtPreSetValueKey]KeyPath: %wZ",?istryPath); DbgPrint("[RegNtPreSetValueKey]ValName: %wZ",((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName); CallbackStatus=STATUS_ACCESS_DENIED; } break; } case RegNtPreDeleteValueKey: { if(IsProcessName("regedit.exe",PsGetCurrentProcess())) { GetRegistryObjectCompleteName(?istryPath,NULL,((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[RegNtPreDeleteValueKey]KeyPath: %wZ",?istryPath); DbgPrint("[RegNtPreDeleteValueKey]ValName: %wZ",((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName); CallbackStatus=STATUS_ACCESS_DENIED; } break; } case RegNtPreRenameKey: { if(IsProcessName("regedit.exe",PsGetCurrentProcess())) { GetRegistryObjectCompleteName(?istryPath,NULL,((PREG_RENAME_KEY_INFORMATION)Argument2)->Object); DbgPrint("[RegNtPreRenameKey]KeyPath: %wZ",?istryPath); DbgPrint("[RegNtPreRenameKey]NewName: %wZ",((PREG_RENAME_KEY_INFORMATION)Argument2)->NewName); CallbackStatus=STATUS_ACCESS_DENIED; } break; } //『注冊表編輯器』里的“重命名鍵值”是沒有直接函數的,是先SetValueKey再DeleteValueKey default: break; } if(registryPath.Buffer != NULL) ExFreePoolWithTag(registryPath.Buffer, REGISTRY_POOL_TAG); return CallbackStatus; }《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的Win64 驱动内核编程-15.回调监控注册表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 11.PHP与MySQL
- 下一篇: Win64 驱动内核编程-16.WFP网