Win64 驱动内核编程-15.回调监控注册表
回調(diào)監(jiān)控注冊(cè)表
????在?WIN32?平臺(tái)上,監(jiān)控注冊(cè)表的手段通常是?SSDT?HOOK。不過用?SSDT?HOOK
的方式監(jiān)控注冊(cè)表實(shí)在是太麻煩了,要?HOOK?一大堆函數(shù),還要處理一些?NT6?系統(tǒng)有而?NT5?系統(tǒng)沒有的函數(shù)。下面我就來介紹一種完勝?SSDT?HOOK?監(jiān)控注冊(cè)表的方法,效果跟?SSDT?HOOK?一樣好。這個(gè)方法就是使用微軟推薦的注冊(cè)表監(jiān)控函數(shù):CmRegisterCallbak。此函數(shù)其實(shí)在?XP?系統(tǒng)上就有了,不過那時(shí)功能不完善,只能簡(jiǎn)單的禁止或允許,無法獲得完整的注冊(cè)表修改信息(即做不到監(jiān)控);在?VISTA?以及之后的系統(tǒng)里,微軟對(duì)此函數(shù)做了相當(dāng)大的改進(jìn),使之能獲得完整的注冊(cè)表修改信息。本文最后實(shí)現(xiàn)的效果就是:把“注冊(cè)表編輯器”(regedit.exe)所有對(duì)注冊(cè)表添加、刪除、重命名的操作都通過?DbgView?打印
出來,并拒絕訪問(只針對(duì)?regedit.exe?是因?yàn)橄到y(tǒng)對(duì)注冊(cè)表的操作太頻繁了,這么做是為了方便大家實(shí)驗(yàn))。
?
函數(shù)原型:
NTSTATUS?CmRegisterCallback
(
_In_?PEX_CALLBACK_FUNCTION?Function,
_In_opt_?PVOID?Context,
_Out_?PLARGE_INTEGER?Cookie
);
這三個(gè)參數(shù)分別為:回調(diào)函數(shù)的地址,隨便設(shè)置的值(直接傳入?NULL?即可),回調(diào)的句柄。相反還有個(gè)函數(shù)用于銷毀回調(diào),它是?CmUnRegisterCallback,原型如下
NTSTATUS?CmUnRegisterCallback(?_In_?LARGE_INTEGER?Cookie);
CmUnRegisterCallback?函數(shù)唯一的的參數(shù)就是?cookie,也就是我所說的“回調(diào)的句柄”。創(chuàng)建和銷毀回調(diào)的代碼如下:
LARGE_INTEGER?CmHandle;
NTSTATUS?CmSt;
CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle);
if(NT_SUCCESS(CmSt))
????DbgPrint("CmRegisterCallback?SUCCESS!");
else
????DbgPrint("CmRegisterCallback?Failed!");
CmUnRegisterCallback(CmHandle);
接下來看看回調(diào)函數(shù)的原型:
NTSTATUS?RegistryCallback
(
_In_?PVOID?CallbackContext,
_In_opt_?PVOID?Argument1,?//操作類型(只是操作編號(hào),不是指針)
_In_opt_?PVOID?Argument2?//操作詳細(xì)信息的結(jié)構(gòu)體指針
)
CallbackContext?基本可以忽略,重要的就是下面的兩個(gè)參數(shù)?Argument1和?Argument2。1?Argument1??記錄的是操作類型(這個(gè)參數(shù)不是指針,只是操作類型的編號(hào)而已),2?Argument2??記錄的是有關(guān)操作信息的結(jié)構(gòu)體指針。接下來舉個(gè)例子。比如我們已經(jīng)注冊(cè)了一個(gè)注冊(cè)表回調(diào),當(dāng)有刪除注冊(cè)表項(xiàng)的操作發(fā)生時(shí),我們注冊(cè)的回調(diào)就會(huì)被調(diào)用,Argument1?的信息是?RegNtPreDeleteKey(pre?是“操作前”的意思),Argument2?的信息是一個(gè)指向
REG_DELETE_KEY_INFORMATION?結(jié)構(gòu)體的指針。當(dāng)操作完成后,我們的注冊(cè)表回調(diào)又會(huì)被調(diào)用一次,此時(shí)?Argument1?的信息是?RegNtPostDeleteKey(post?是“操作后”的意思),Argument2?的信息是一個(gè)指向REG_POST_OPERATION_INFORMATION?結(jié)構(gòu)體的指針。在所有的結(jié)構(gòu)體里,有一項(xiàng)是肯定有的,就是?Object,它是你操作了那個(gè)項(xiàng)或者根項(xiàng)的對(duì)象指針(相對(duì)于新建項(xiàng)而言,就是根項(xiàng)的對(duì)象指針;相對(duì)于新建/設(shè)置/刪除/重命名鍵值和刪除項(xiàng)而言,就是項(xiàng)的對(duì)象指針)。
????需要注意的是,此函數(shù)如果返回?STATUS_SUCCESS,注冊(cè)表操作就會(huì)繼續(xù)執(zhí)
行,如果返回?STATUS_ACCESS_DENIED,注冊(cè)表操作就不會(huì)執(zhí)行執(zhí)行了。這樣子
就達(dá)到了“監(jiān)控”的效果。最終效果如下:
最后附上測(cè)試代碼:
?
注冊(cè)回調(diào): CmSt=CmRegisterCallback(RegistryCallback,NULL,&CmHandle); 注銷回調(diào): if(NT_SUCCESS(CmSt)) CmUnRegisterCallback(CmHandle);回調(diào)函數(shù)處理,同時(shí)禁止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: //出現(xiàn)兩次是因?yàn)橐淮问荗penKey,一次是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; } //『注冊(cè)表編輯器』里的“重命名鍵值”是沒有直接函數(shù)的,是先SetValueKey再DeleteValueKey default: break; } if(registryPath.Buffer != NULL) ExFreePoolWithTag(registryPath.Buffer, REGISTRY_POOL_TAG); return CallbackStatus; }《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀
總結(jié)
以上是生活随笔為你收集整理的Win64 驱动内核编程-15.回调监控注册表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 11.PHP与MySQL
- 下一篇: Win64 驱动内核编程-16.WFP网