在笔者前一篇文章《驱动开发:内核枚举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] PVOID Context,
[out] PLARGE_INTEGER Cookie
);

自定义注册表回调函数MyLySharkCallback需要保留三个参数,CallbackContext回调上下文,Argument1是操作类型,Argument2定义详细结构体指针。

NTSTATUS MyLySharkCallback(_In_ PVOID CallbackContext, _In_opt_ PVOID Argument1, _In_opt_ PVOID Argument2)

在自定义回调函数内Argument1则可获取到操作类型,类型是一个REG_NOTIFY_CLASS枚举结构,微软对其的具体定义如下所示。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com 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得到注册表完整路径,并打印出来,这段代码实现如下。

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com #include <ntifs.h>
#include <windef.h> // 未导出函数声明 pEProcess -> PID
PUCHAR PsGetProcessImageFileName(PEPROCESS pEProcess); NTSTATUS ObQueryNameString(
_In_ PVOID Object,
_Out_writes_bytes_opt_(Length) POBJECT_NAME_INFORMATION ObjectNameInfo,
_In_ ULONG Length,
_Out_ PULONG ReturnLength
); // 注册表回调Cookie
LARGE_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;
}

运行驱动程序,则会输出当前系统中所有针对注册表的操作,如下图所示。

如上的代码只能实现注册表项的监视,而如果需要监控则需要在回调函数MyLySharkCallback判断,如果指定注册表项是需要保护的则直接返回status = STATUS_ACCESS_DENIED;从而达到保护注册表的目的,核心代码如下所示。

// 反注册表删除回调
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]\n", &ustrRegPath, ((PREG_DELETE_VALUE_KEY_INFORMATION)Argument2)->ValueName); // 如果要删除指定注册表项则拒绝
PWCH pszRegister = L"\\REGISTRY\\MACHINE\\SOFTWARE\\lyshark.com";
if (wcscmp(ustrRegPath.Buffer, pszRegister) == 0)
{
DbgPrint("[lyshark] 注册表项删除操作已被拦截! \n");
// 拒绝操作
status = STATUS_ACCESS_DENIED;
}
break;
}
default:
break;
} // 释放内存
if (NULL != ustrRegPath.Buffer)
{
ExFreePool(ustrRegPath.Buffer);
ustrRegPath.Buffer = NULL;
} return status;
}

运行驱动程序,然后我们尝试删除\\LyShark\HKEY_LOCAL_MACHINE\SOFTWARE\lyshark.com里面的子项,则会提示如下信息。

当然这里的RegNtPreDeleteValueKey是指的删除操作,如果将其替换成RegNtPreSetValueKey,那么只有当注册表被创建才会拦截,此时就会变成拦截创建。

// 拦截创建操作
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.com";
if (wcscmp(ustrRegPath.Buffer, pszRegister) == 0)
{
DbgPrint("[lyshark] 注册表项创建操作已被拦截! \n");
status = STATUS_ACCESS_DENIED;
}
break;
}
default:
break;
} // 释放内存
if (NULL != ustrRegPath.Buffer)
{
ExFreePool(ustrRegPath.Buffer);
ustrRegPath.Buffer = NULL;
} return status;
}

加载驱动保护,然后我们尝试在\\LyShark\HKEY_LOCAL_MACHINE\SOFTWARE\lyshark.com里面创建一个子项,则会提示创建失败。

驱动开发:内核监控Register注册表回调的更多相关文章

  1. 驱动开发:内核枚举Registry注册表回调

    在笔者上一篇文章<驱动开发:内核枚举LoadImage映像回调>中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与 ...

  2. Win64 驱动内核编程-32.枚举与删除注册表回调

    枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...

  3. Win64 驱动内核编程-6.内核里操作注册表

    内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...

  4. 遍历注册表回调函数(仿PCHunter CmpBack)

    遍历注册表回调函数(仿PCHunter CmpBack) typedef struct _CAPTURE_REGISTRY_MANAGER { PDEVICE_OBJECT deviceObject; ...

  5. 关于软件开发中兼容win7注册表的解决方案

    关于软件开发中兼容win7注册表的解决方案   编写人:CC阿爸 2014-3-14 l  近来在开发一winform程序时,发现在xp 系统访问注册表一切正常.可偏这个时候,微软又提醒大家.Xp今年 ...

  6. 入侵检测中需要监控的注册表路径研究(Windows Registry Security Check)

    1. Windows注册表简介 注册表(Registry,繁体中文版Windows称之为登录档)是Microsoft Windows中的一个重要的数据库,用于存储系统和应用程序的设置信息.早在Wind ...

  7. 【spring 注解驱动开发】spring组件注册

    尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...

  8. Webbrowser指定IE内核版本(更改注册表)

    如果电脑上安装了IE8或者之后版本的IE浏览器,Webbrowser控件会使用IE7兼容模式来显示网页内容.解决方法是在注册表中为你的进程指定引用IE的版本号. 比如我的程序叫做a.exe 对于32位 ...

  9. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

随机推荐

  1. LuoguP1516 青蛙的约会 (Exgcd)

    #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> ...

  2. 分库分表之ShardingSphere

    目录 分库分表诞生的前景 分库分表的方式(垂直拆分,水平复制) 1.垂直拆分 1.1 垂直分库 1.2 垂直分表 2.水平拆分 2.1 水平分库 2.2 水平分表 分库分库中间件 ShardingSp ...

  3. CF453C Little Pony and Summer Sun Celebration(构造、贪心(?))

    CF453C Little Pony and Summer Sun Celebration 题解 这道题要求输出任意解,并且路径长度不超过4n就行,所以给了我们乱搞构造的机会. 我这里给出一种构造思路 ...

  4. Javaweb___Jquery高级

    今日内容: 1. JQuery 高级 1. 动画 2. 遍历 3. 事件绑定 4. 案例 5. 插件 JQuery 高级 1. 动画 1. 三种方式显示和隐藏元素 1. 默认显示和隐藏方式 1. sh ...

  5. Cluster table 与性能

    用户数据行存储在文件系统中的堆文件中,而这些行以不确定的顺序存储.如果表最初以插入/复制的顺序加载,那么以后的插入.更新和删除将导致在堆文件中以不可预测的顺序添加行.创建索引创建一个指向堆行的辅助文件 ...

  6. Linux云主机安全入侵排查步骤

    导语 经常有用户报障系统被植入恶意程序,如挖矿软件.ddos攻击病毒.syn映射攻击病毒等,可以按照以下流程为用户排查入侵病毒类型: 一.定位病毒进程 对于用户反馈云主机性能卡顿,CPU和内存占用较高 ...

  7. 大家都能看得懂的源码之 ahooks useVirtualList 封装虚拟滚动列表

    本文是深入浅出 ahooks 源码系列文章的第十八篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时 ...

  8. Windows优先使用IPv4

    当前主流的Windows系统(从Windows 7之后)都会同时使用ipv6和ipv4,并且优先使用ipv6.当你ping另一个服务器的时候就能看到,优先使用的是ipv6进行通信.由于能够在DNS中解 ...

  9. 第六章:Django 综合篇 - 19:部署 Django

    补充说明:关于项目部署,历来是开发和运维人员的痛点.造成部署困难的主要原因之一是大家的Linux环境不同,这包括发行版.解释器.插件.运行库.配置.版本级别等等太多太多的细节.因此,一个成功的部署案例 ...

  10. centos7使用yum方式安装node_exporter

    官网下载地址:https://prometheus.io/download/ 选择对应的系统版本 官网提供的是压缩包,点击旁边的github地址 github页面显示的有yum安装和docker安装, ...