回调监控注册表

在 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和 Argument2。1 Argument1  记录的是操作类型(这个参数不是指针,只是操作类型的编号而已),2 Argument2  记录的是有关操作信息的结构体指针。接下来举个例子。比如我们已经注册了一个注册表回调,当有删除注册表项的操作发生时,我们注册的回调就会被调用,Argument1 的信息是 RegNtPreDeleteKey(pre 是“操作前”的意思),Argument2 的信息是一个指向

REG_DELETE_KEY_INFORMATION 结构体的指针。当操作完成后,我们的注册表回调又会被调用一次,此时 Argument1 的信息是 RegNtPostDeleteKey(post 是“操作后”的意思),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;
}

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

  1. Win64 驱动内核编程-14.回调监控文件

    回调监控文件 使用 ObRegisterCallbacks 实现保护进程,其实稍微 PATCH 下内核,这个函数还能实现文件操作监视.但可惜只能在 WIN7X64 上用.因为在 WIN7X64 上 P ...

  2. Win64 驱动内核编程-12.回调监控进线程创建和退出

    回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine   进程回调PsSetCreateThreadNotifyRoutine    线程回调分 ...

  3. Win64 驱动内核编程-11.回调监控进线程句柄操作

    无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...

  4. Win64 驱动内核编程-13.回调监控模块加载

    回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...

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

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

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

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

  7. Win64 驱动内核编程-18.SSDT

    SSDT 学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019 学习资料:WIN64内核编程基础 胡文亮 SSDT(系统服务描述表),刚开 ...

  8. Win64 驱动内核编程-17. MINIFILTER(文件保护)

     MINIFILTER(文件保护) 使用 HOOK 来监控文件操作的方法有很多,可以在 SSDT 上 HOOK 一堆和 FILE 有关的函数,也可以对 FSD 进行 IRP HOOK,不过这些方法既不 ...

  9. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

最新文章

  1. 【Android 逆向】Android 进程注入工具开发 ( 系统调用 | Android NDK 中的系统调用示例 )
  2. timertask run函数未执行_图执行模式下的 TensorFlow 2
  3. curd什么意思中文_每日一句英译英:She's a ten什么意思?
  4. 靶场练习第十天~vulnhub靶场之dc-2
  5. KindEditor在eclipse里的配置方法
  6. 【玩转Atlas200DK系列】为Atlas200DK配置wifi外挂模块
  7. leetcode题库1370-- 上升下降字符串
  8. 系统学Android从零开始,附超全教程文档
  9. 百度网盘视频加速播放
  10. 苹果开发者中心如何上传构建版本
  11. 反正切函数摘录自变频器
  12. 全网首发Modown主题8.31开心版
  13. Simple Torrent:一个支持边下边播、无版权限制和自动上传的BT离线下载程序
  14. 新司机的黑裙战斗机 篇二:入门—新司机的黑群晖指北——软件篇(上)
  15. pytorch 入门:GPU加速,卷积层,池化层
  16. 安卓电话补充业务 SS
  17. 微星GT77HX-13VI2023原厂Windows11重建F3一键恢复msirestore功能
  18. 打篮球与企业管理有相似之处吗?
  19. 97 条 Linux 运维工程师常用命令总结
  20. python 分词包_python调用hanlp分词包手记

热门文章

  1. final 修饰方法参数
  2. iOS开发者有价值的工具集
  3. SSH 安全性和配置入门
  4. 6大设计原则之依赖倒置原则
  5. silverlight 客户端之间的通讯
  6. Python爬虫(十二)_BeautifulSoup4 解析器
  7. 【leetcode 968. 监控二叉树】解题报告
  8. ROS学习笔记_创建工作空间(一)
  9. 原创关于python中的一些坑点
  10. struts.xml 文件添加DTD文件