在笔者前一篇文章《驱动开发:内核枚举Registry注册表回调》
中实现了对注册表的枚举 , 本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监控函数 , 通过这两个函数可以在不劫持内核API的前提下实现对注册表增加,删除,创建等事件的有效监控 , 注册表监视通常会通过CmRegisterCallback
创建监控事件并传入自己的回调函数,与该创建对应的是CmUnRegisterCallback
当注册表监控结束后可用于注销回调 。
- CmRegisterCallback 设置注册表回调
- CmUnRegisterCallback 注销注册表回调
CmRegisterCallback
需传入三个参数,参数一回调函数地址,参数二空余,参数三回调句柄,微软定义如下 。// 署名权// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com// 参数1:回调函数地址// 参数2:无作用// 参数3:回调句柄NTSTATUS CmRegisterCallback([in]PEX_CALLBACK_FUNCTION Function,[in, optional] PVOIDContext,[out]PLARGE_INTEGERCookie);
自定义注册表回调函数MyLySharkCallback
需要保留三个参数,CallbackContext
回调上下文,Argument1
是操作类型,Argument2
定义详细结构体指针 。NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2)
在自定义回调函数内Argument1
则可获取到操作类型,类型是一个REG_NOTIFY_CLASS
枚举结构,微软对其的具体定义如下所示 。【驱动开发:内核监控Register注册表回调】
// 署名权// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.comtypedef 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
得到注册表完整路径,并打印出来,这段代码实现如下 。// 署名权// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#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]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);break; } // 打开注册表之前 case RegNtPreOpenKey: {// 获取注册表路径GetFullPath(&ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->RootObject);DbgPrint("[打开注册表][%wZ][%wZ]\n", &ustrRegPath, ((PREG_CREATE_KEY_INFORMATION)Argument2)->CompleteName);break; } // 删除键之前 case RegNtPreDeleteKey: {// 获取注册表路径GetFullPath(&ustrRegPath, ((PREG_DELETE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[删除键][%wZ] \n", &ustrRegPath);break; } // 删除键值之前 case RegNtPreDeleteValueKey: {// 获取注册表路径GetFullPath(&ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[删除键值][%wZ][%wZ] \n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName);// 获取当前进程, 即操作注册表的进程PEPROCESS pEProcess = PsGetCurrentProcess();if (NULL != pEProcess){UCHAR *lpszProcessName = PsGetProcessImageFileName(pEProcess);if (NULL != lpszProcessName){DbgPrint("进程 [%s] 删除了键值对 \n", lpszProcessName);}}break; } // 修改键值之前 case RegNtPreSetValueKey: {// 获取注册表路径GetFullPath(&ustrRegPath, ((PREG_SET_VALUE_KEY_INFORMATION)Argument2)->Object);DbgPrint("[修改键值][%wZ][%wZ] \n", &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 \n")); // 注销当前注册表回调 if (0 < g_liRegCookie.QuadPart) {CmUnRegisterCallback(g_liRegCookie); }}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint(("hello lyshark.com \n")); // 设置注册表回调 NTSTATUS status = CmRegisterCallback(MyLySharkCallback, NULL, &g_liRegCookie); if (!NT_SUCCESS(status)) {g_liRegCookie.QuadPart = 0;return status; } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 【pytest官方文档】解读-开发可pip安装的第三方插件
- 驱动开发:内核运用LoadImage屏蔽驱动
- 分享几个关于Camera的坑
- 如何开发简单的游戏(自学开发一个游戏app)
- 个人如何开发手机游戏(如何自己开发一款手机游戏)
- 二 京东云开发者| Redis数据结构-List、Hash、Set及Sorted Set的结构实现
- DevOps | 如何快速提升团队软件开发成熟度,快速提升研发效能?
- 三 【单片机入门】应用层软件开发的单片机学习之路-----UART串口通讯和c#交互
- 四十六 SpringCloud微服务实战——搭建企业级开发框架:【移动开发】整合uni-app搭建移动端快速开发框架-环境搭建
- 驱动开发:内核监视LoadImage映像回调