欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Win64 驱动内核编程-15.回调监控注册表

发布时间:2025/6/17 编程问答 8 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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和 Argument21 Argument1  记录的是操作类型(这个参数不是指针,只是操作类型的编号而已),2 Argument2  记录的是有关操作信息的结构体指针。接下来举个例子。比如我们已经注册了一个注册表回调,当有删除注册表项的操作发生时,我们注册的回调就会被调用,Argument1 的信息是 RegNtPreDeleteKeypre 是“操作前”的意思),Argument2 的信息是一个指向

REG_DELETE_KEY_INFORMATION 结构体的指针。当操作完成后,我们的注册表回调又会被调用一次,此时 Argument1 的信息是 RegNtPostDeleteKeypost 是“操作后”的意思),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.回调监控注册表的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。