7.5 Windows驱动开发:监控Register注册表回调
在笔者前一篇文章《内核枚举registry注册表回调》中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监控函数,通过这两个函数可以在不劫持内核api的前提下实现对注册表增加,删除,创建等事件的有效监控,注册表监视通常会通过cmregistercallback创建监控事件并传入自己的回调函数,与该创建对应的是cmunregistercallback当注册表监控结束后可用于注销回调。
CmRegisterCallback和CmUnRegisterCallback是Windows操作系统提供的两个内核API函数,用于注册和取消注册注册表回调函数。
注册表回调函数是一种内核回调函数,它可以用于监视和拦截系统中的注册表操作,例如键值的创建、修改和删除等。当有相关操作发生时,操作系统会调用注册的注册表回调函数,并将操作相关的信息传递给回调函数。
CmRegisterCallback函数用于注册注册表回调函数,而CmUnRegisterCallback函数则用于取消注册已经注册的回调函数。开发者可以在注册表回调函数中执行自定义的逻辑,例如记录日志、过滤敏感数据、或者阻止某些操作。
需要注意的是,注册表回调函数的注册和取消注册必须在内核模式下进行,并且需要开发者有一定的内核开发经验。同时,注册表回调函数也需要遵守一些约束条件,例如不能阻塞或挂起进程或线程的创建或访问,不能调用一些内核API函数等。
内核监控Register注册表回调在安全软件、系统监控和调试工具等领域有着广泛的应用。开发者可以利用这个机制来监视和拦截系统中的注册表操作,以保护系统安全。
CmRegisterCallback 设置注册表回调CmUnRegisterCallback 注销注册表回调
默认情况下CmRegisterCallback需传入三个参数,参数一回调函数地址,参数二空余,参数三回调句柄,微软定义如下。
代码语言:javascript代码运行次数:0运行复制
// 参数1:回调函数地址// 参数2:无作用// 参数3:回调句柄NTSTATUS CmRegisterCallback( [in] PEX_CALLBACK_FUNCTION Function, [in, optional] PVOID Context, [out] PLARGE_INTEGER Cookie);
自定义注册表回调函数MyLySharkCallback需要保留三个参数,CallbackContext回调上下文,Argument1是操作类型,Argument2定义详细结构体指针。
代码语言:javascript代码运行次数:0运行复制
NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2)
在自定义回调函数内Argument1则可获取到操作类型,类型是一个REG_NOTIFY_CLASS枚举结构,微软对其的具体定义如下所示。
代码语言:javascript代码运行次数:0运行复制
typedef enum _REG_NOTIFY_CLASS { RegNtDeleteKey, RegNtPreDeleteKey = RegNtDeleteKey, RegNtSetValueKey, RegNtPreSetValueKey = RegNtSetValueKey, RegNtDeleteValueKey, RegNtPreDeleteValueKey = RegNtDeleteValueKey, RegNtSetInformationKey, RegNtPreSetInformationKey = RegNtSetInformationKey, RegNtRenameKey, RegNtPreRenameKey = RegNtRenameKey, RegNtEnumerateKey, RegNtPreEnumerateKey = RegNtEnumerateKey, RegNtEnumerateValueKey, RegNtPreEnumerateValueKey = RegNtEnumerateValueKey, RegNtQueryKey, RegNtPreQueryKey = RegNtQueryKey, RegNtQueryValueKey, RegNtPreQueryValueKey = RegNtQueryValueKey, RegNtQueryMultipleValueKey, RegNtPreQueryMultipleValueKey = RegNtQueryMultipleValueKey, RegNtPreCreateKey, RegNtPostCreateKey, RegNtPreOpenKey, RegNtPostOpenKey, RegNtKeyHandleClose, RegNtPreKeyHandleClose = RegNtKeyHandleClose, // // .Net only // RegNtPostDeleteKey, RegNtPostSetValueKey, RegNtPostDeleteValueKey, RegNtPostSetInformationKey, RegNtPostRenameKey, RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPostQueryKey, RegNtPostQueryValueKey, RegNtPostQueryMultipleValueKey, RegNtPostKeyHandleClose, RegNtPreCreateKeyEx, RegNtPostCreateKeyEx, RegNtPreOpenKeyEx, RegNtPostOpenKeyEx, // // new to Windows Vista // RegNtPreFlushKey, RegNtPostFlushKey, RegNtPreLoadKey, RegNtPostLoadKey, RegNtPreUnLoadKey, RegNtPostUnLoadKey, RegNtPreQueryKeySecurity, RegNtPostQueryKeySecurity, RegNtPreSetKeySecurity, RegNtPostSetKeySecurity, // // per-object context cleanup // RegNtCallbackObjectContextCleanup, // // new in Vista SP2 // RegNtPreRestoreKey, RegNtPostRestoreKey, RegNtPreSaveKey, RegNtPostSaveKey, RegNtPreReplaceKey, RegNtPostReplaceKey, MaxRegNtNotifyClass //should always be the last enum} REG_NOTIFY_CLASS;
其中对于注册表最常用的监控项为以下几种类型,当然为了实现监控则我们必须要使用之前,如果使用之后则只能起到监视而无法做到监控的目的。
RegNtPreCreateKey 创建注册表之前RegNtPreOpenKey 打开注册表之前RegNtPreDeleteKey 删除注册表之前RegNtPreDeleteValueKey 删除键值之前RegNtPreSetValueKey 修改注册表之前
如果需要实现监视则,首先CmRegisterCallback注册一个自定义回调,当有消息时则触发MyLySharkCallback其内部获取到lOperateType操作类型,并通过switch选择不同的处理例程,每个处理例程都通过GetFullPath得到注册表完整路径,并打印出来,这段代码实现如下。
代码语言:javascript代码运行次数:0运行复制
#include <ntifs.h>#include <windef.h>// 未导出函数声明 pEProcess -> PIDPUCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);NTSTATUS ObQueryNameString( _In_ PVOID Object, _Out_writes_bytes_opt_(Length) POBJECT_NAME_INFORMATION ObjectNameInfo, _In_ ULONG Length, _Out_ PULONG ReturnLength );// 注册表回调CookieLARGE_INTEGER g_liRegCookie;// 获取注册表完整路径BOOLEAN GetFullPath(PUNICODE_STRING pRegistryPath, PVOID pRegistryObject){ // 判断数据地址是否有效 if ((FALSE == MmIsAddressValid(pRegistryObject)) || (NULL == pRegistryObject)) { return FALSE; } // 申请内存 ULONG ulSize = 512; PVOID lpObjectNameInfo = ExAllocatePool(NonPagedPool, ulSize); if (NULL == lpObjectNameInfo) { return FALSE; } // 获取注册表路径 ULONG ulRetLen = 0; NTSTATUS status = ObQueryNameString(pRegistryObject, (POBJECT_NAME_INFORMATION)lpObjectNameInfo, ulSize, &ulRetLen); if (!NT_SUCCESS(status)) { ExFreePool(lpObjectNameInfo); return FALSE; } // 复制 RtlCopyUnicodeString(pRegistryPath, (PUNICODE_STRING)lpObjectNameInfo); // 释放内存 ExFreePool(lpObjectNameInfo); return TRUE;}// 注册表回调函数NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrRegPath; // 获取操作类型 LONG lOperateType = (REG_NOTIFY_CLASS)Argument1; // 申请内存 ustrRegPath.Length = 0; ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR); ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength); if (NULL == ustrRegPath.Buffer) { return status; } RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength); // 判断操作 switch (lOperateType) { // 创建注册表之前 case RegNtPreCreateKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject); DbgPrint("[创建注册表][%wZ][%wZ]
", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName); break; } // 打开注册表之前 case RegNtPreOpenKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject); DbgPrint("[打开注册表][%wZ][%wZ]
", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName); break; } // 删除键之前 case RegNtPreDeleteKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[删除键][%wZ]
", &ustrRegPath); break; } // 删除键值之前 case RegNtPreDeleteValueKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[删除键值][%wZ][%wZ]
", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName); // 获取当前进程, 即操作注册表的进程 PEPROCESS pEProcess = PsGetCurrentProcess(); if (NULL != pEProcess) { UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess); if (NULL != lpszProcessName) { DbgPrint("进程 [%s] 删除了键值对
", lpszProcessName); } } break; } // 修改键值之前 case RegNtPreSetValueKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[修改键值][%wZ][%wZ]
", &ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->ValueName); break; } default: break; } // 释放内存 if (NULL != ustrRegPath.Buffer) { ExFreePool(ustrRegPath.Buffer); ustrRegPath.Buffer = NULL; } return status;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("Uninstall Driver Is OK
")); // 注销当前注册表回调 if (0 DriverUnload = UnDriver; return STATUS_SUCCESS;}</windef.h></ntifs.h>
运行驱动程序,则会输出当前系统中所有针对注册表的操作,如下图所示。
如上的代码只能实现注册表项的监视,而如果需要监控则需要在回调函数MyLySharkCallback判断,如果指定注册表项是需要保护的则直接返回status = STATUS_ACCESS_DENIED;从而达到保护注册表的目的,核心代码如下所示。
代码语言:javascript代码运行次数:0运行复制
// 反注册表删除回调NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrRegPath; // 获取操作类型 LONG lOperateType = (REG_NOTIFY_CLASS)Argument1; ustrRegPath.Length = 0; ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR); ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength); if (NULL == ustrRegPath.Buffer) { return status; } RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength); // 判断操作 switch (lOperateType) { // 删除键值之前 case RegNtPreDeleteValueKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object); DbgPrint("[删除键值][%wZ][%wZ]
", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName); // 如果要删除指定注册表项则拒绝 PWCH pszRegister = L"\REGISTRY\MACHINE\SOFTWARE\lyshark"; if (wcscmp(ustrRegPath.Buffer, pszRegister) == 0) { DbgPrint("[lyshark] 注册表项删除操作已被拦截!
"); // 拒绝操作 status = STATUS_ACCESS_DENIED; } break; } default: break; } // 释放内存 if (NULL != ustrRegPath.Buffer) { ExFreePool(ustrRegPath.Buffer); ustrRegPath.Buffer = NULL; } return status;}
运行驱动程序,然后我们尝试删除\LySharkHKEY_LOCAL_MACHINESOFTWARElyshark里面的子项,则会提示如下信息。
当然这里的RegNtPreDeleteValueKey是指的删除操作,如果将其替换成RegNtPreSetValueKey,那么只有当注册表被创建才会拦截,此时就会变成拦截创建。
代码语言:javascript代码运行次数:0运行复制
// 拦截创建操作NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2){ NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrRegPath; // 获取操作类型 LONG lOperateType = (REG_NOTIFY_CLASS)Argument1; // 申请内存 ustrRegPath.Length = 0; ustrRegPath.MaximumLength = 1024 * sizeof(WCHAR); ustrRegPath.Buffer = ExAllocatePool(NonPagedPool, ustrRegPath.MaximumLength); if (NULL == ustrRegPath.Buffer) { return status; } RtlZeroMemory(ustrRegPath.Buffer, ustrRegPath.MaximumLength); // 判断操作 switch (lOperateType) { // 修改键值之前 case RegNtPreSetValueKey: { // 获取注册表路径 GetFullPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object); // 拦截创建 PWCH pszRegister = L"\REGISTRY\MACHINE\SOFTWARE\lyshark"; if (wcscmp(ustrRegPath.Buffer, pszRegister) == 0) { DbgPrint("[lyshark] 注册表项创建操作已被拦截!
"); status = STATUS_ACCESS_DENIED; } break; } default: break; } // 释放内存 if (NULL != ustrRegPath.Buffer) { ExFreePool(ustrRegPath.Buffer); ustrRegPath.Buffer = NULL; } return status;}
加载驱动保护,然后我们尝试在\LySharkHKEY_LOCAL_MACHINESOFTWARElyshark里面创建一个子项,则会提示创建失败。
以上就是7.5 Windows驱动开发:监控Register注册表回调的详细内容,更多请关注启程网【www.vszh.cn】。
如非特殊说明,本站内容均来自于网友自主分享,如有任何问题均请联系我们进行处理!
猜您喜欢
2026最新!Win10_11电脑怎么搞定帝国时代2安装?一键免注册表配置教学
2026-06-04“帝国时代2安装”一直是困扰老玩家的终极难题。由于这是一款诞生于上世纪末的游戏,传统的安装光盘或散装文件在Win10/11系统上经常会遇到“注册表缺失”、“DirectDirectDraw初始化失败”等致命报错。为了彻底跨越这道技术门槛,强烈推荐:玩帝国时代2上KK对战平台。KK官方对战平台采用“云端环境配置”技术,实现了一键“免安装”直达游戏,不仅完美适配所有老电脑和新系统,还彻底解决了经典老B...
如何在 Win11 中开启网络数据包实时捕获监控 监控流氓软件偷跑流量
2026-06-04必须启用底层网卡级数据包捕获能力,可选Wireshark+Npcap全链路捕获、ETW+NetEventViewer内核级审计、PowerShell快速筛查或资源监视器交叉验证四条路径。 当你怀疑Windows11中有流氓软件在后台静默上传日志、同步缓存或推送广告,却无法通过任务管理器直观识别时,必须绕过系统API统计层,直接从网卡驱动捕获原始数据包——只有这样,才能看到每个TCP/UDP连接的真...
如何开启 Win11 系统 24H2 版本的 NPU AI 算力监控
2026-06-04必须确保系统版本≥26100、NPU硬件被识别、驱动含npuplatform.sys或qcomnpu.sys、性能计数器通道激活(注册NPULogger并设Start=1),并在任务管理器中启用NPU视图,缺一不可。 要在Windows1124H2(Build26100及以上)中开启NPUAI算力监控,必须确保系统已识别物理NPU硬件、驱动注册完成、性能计数器通道激活,并在任务管理器中显式启用对应...
如何修复 Windows 11 由于更新导致的蓝牙图标消失 注册表深度清理蓝牙记录
2026-06-04Windows11更新后蓝牙图标消失主因是注册表策略项被重置:需将NoTrayItemsDisplay设为0、新建并设EnableBluetoothTaskbarIcon为1,删除HKEY_CURRENT_USER\Software\Microsoft\Bluetooth下的Devices和TrayIcon子项,再注销重登即可恢复。 Windows11系统更新后任务栏右下角蓝牙图标突然消失,设置中...
AIDA64怎么显示桌面副屏监控 AIDA64设置屏显方法【技巧】
2026-05-30AIDA64可通过内置RemoteSensorLCD服务将CPU温度、内存占用、显卡频率等实时数据原生投屏至副屏。需启用RemoteSensor、设端口8080、匹配副屏分辨率、绑定显示器编号,并用LCD项目添加图像/传感器/图表,支持快捷键开关与位置锁定。 想让AIDA64把CPU温度、内存占用、显卡频率这些实时数据直接投到副屏上,不靠第三方网页中转、不依赖手机浏览器刷新、也不用额外装驱动——只...
修复Windows 11由于更新导致的蓝牙无法删除设备 注册表强制清理蓝牙记录
2026-05-30Windows11蓝牙设备删不掉是因BTHPORT注册表残留密钥、服务挂起及BTHENUM隐藏实例导致;需先重启蓝牙相关服务,再依次清理Keys、Devices子项,卸载BTHENUM设备,并用pnputil强制移除硬件ID。 Windows11系统更新后,蓝牙设备卡在“正在删除”状态、点击删除无反应、或删完立刻重现,本质是BTHPORT注册表中残留了损坏的配对密钥分支,导致UI层删除指令无法触达...
修复 Windows 11 由于更新导致的蓝牙图标消失 注册表深度清理蓝牙连接记录教程
2026-05-30Windows11更新后任务栏蓝牙图标消失的主因是注册表键EnableBluetoothTaskbarIcon被重置为0或删除,需手动创建并设为1,再重启explorer.exe;同时建议清理蓝牙配对缓存及禁用FastPair以确保图标稳定显示。 Windows11系统更新后任务栏右下角蓝牙图标突然消失,设置中蓝牙开关不可见、快速设置里找不到蓝牙模块,且已确认设备管理器中“蓝牙”分类存在、驱动无黄...
彻底清理Windows 11注册表残留文件 提升系统启动及响应速度方案
2026-05-29应优先用磁盘清理工具清除Windows更新备份等外部依赖,再手动备份后精准删除三大Uninstall路径下的无效注册表项,接着验证并清理Run启动项,然后用DISM+SFC修复系统映像,最后用CCleaner可视化扫描修复深层冗余。 卸载软件后系统启动变慢、程序响应迟滞,或“应用和功能”列表中仍显示已移除的条目,说明注册表中残留了无效的安装记录、启动项或服务引用,必须逐层定位并清除这些冗余项才能恢...
