驱动开发:内核枚举Registry注册表回调
在笔者上一篇文章《驱动开发:内核枚举LoadImage映像回调》中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NOTIFY_ENTRY结构即可实现枚举。
我们来看一款闭源ARK工具是如何实现的:

注册表系统回调的枚举需要通过特征码搜索来实现,首先我们可以定位到uf CmUnRegisterCallback内核函数上,在该内核函数下方存在一个CallbackListHead链表节点,取出这个链表地址。

当得到注册表链表入口0xfffff8063a065bc0直接将其解析为_CM_NOTIFY_ENTRY即可得到数据,如果要遍历下一个链表则只需要ListEntryHead.Flink向下移动指针即可。
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
// 注册表回调函数结构体定义
typedef struct _CM_NOTIFY_ENTRY
{
LIST_ENTRY ListEntryHead;
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
PVOID Context;
PVOID Function;
}CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
要想得到此处的链表地址,需要先通过MmGetSystemRoutineAddress()获取到CmUnRegisterCallback函数基址,然后在该函数起始位置向下搜索,找到这个链表节点,并将其后面的基地址取出来,在上一篇《驱动开发:内核枚举LoadImage映像回调》文章中已经介绍了定位方式此处跳过介绍,具体实现代码如下。
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntifs.h>
#include <windef.h>
// 指定内存区域的特征码扫描
// PowerBy: LyShark.com
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根据特征码获取 CallbackListHead 链表地址
// PowerBy: LyShark.com
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pCmUnRegisterCallback = NULL;
PVOID pCallbackListHead = NULL;
// 先获取 CmUnRegisterCallback 函数地址
RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pCmUnRegisterCallback)
{
return pCallbackListHead;
}
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
/*
lyshark.com>
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0 xor r8d,r8d
fffff806`3a4271ae 488d542438 lea rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff call nt!CmListGetNextElement (fffff806`3a255414)
fffff806`3a4271bf 488bf8 mov rdi,rax
fffff806`3a4271c2 4889442440 mov qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0 test rax,rax
fffff806`3a4271ca 0f84c7000000 je nt!CmUnRegisterCallback+0x157 (fffff806`3a427297) Branch
*/
pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return pCallbackListHead;
}
// 先获取偏移再计算地址
lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
return pCallbackListHead;
}
VOID UnDriver(PDRIVER_OBJECT Driver)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
PVOID pCallbackListHeadAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
LONG lSpecialOffset = 0;
DbgPrint("hello lyshark.com \n");
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
/*
lyshark.com>
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0 xor r8d,r8d
fffff806`3a4271ae 488d542438 lea rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff call nt!CmListGetNextElement (fffff806`3a255414)
fffff806`3a4271bf 488bf8 mov rdi,rax
fffff806`3a4271c2 4889442440 mov qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0 test rax,rax
fffff806`3a4271ca 0f84c7000000 je nt!CmUnRegisterCallback+0x157 (fffff806`3a427297) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
// 根据特征码获取地址
pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
DbgPrint("[LyShark.com] CallbackListHead => %p \n", pCallbackListHeadAddress);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
运行这段代码,并可得到注册表回调入口地址,输出效果如下所示:

得到了注册表回调入口地址,接着直接循环遍历输出这个链表即可得到所有的注册表回调。
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntifs.h>
#include <windef.h>
// 指定内存区域的特征码扫描
// PowerBy: LyShark.com
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根据特征码获取 CallbackListHead 链表地址
// PowerBy: LyShark.com
PVOID SearchCallbackListHead(PUCHAR pSpecialData, ULONG ulSpecialDataSize, LONG lSpecialOffset)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pCmUnRegisterCallback = NULL;
PVOID pCallbackListHead = NULL;
// 先获取 CmUnRegisterCallback 函数地址
RtlInitUnicodeString(&ustrFuncName, L"CmUnRegisterCallback");
pCmUnRegisterCallback = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pCmUnRegisterCallback)
{
return pCallbackListHead;
}
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
/*
lyshark.com>
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0 xor r8d,r8d
fffff806`3a4271ae 488d542438 lea rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff call nt!CmListGetNextElement (fffff806`3a255414)
fffff806`3a4271bf 488bf8 mov rdi,rax
fffff806`3a4271c2 4889442440 mov qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0 test rax,rax
fffff806`3a4271ca 0f84c7000000 je nt!CmUnRegisterCallback+0x157 (fffff806`3a427297) Branch
*/
pAddress = SearchMemory(pCmUnRegisterCallback, (PVOID)((PUCHAR)pCmUnRegisterCallback + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return pCallbackListHead;
}
// 先获取偏移再计算地址
lOffset = *(PLONG)((PUCHAR)pAddress + lSpecialOffset);
pCallbackListHead = (PVOID)((PUCHAR)pAddress + lSpecialOffset + sizeof(LONG) + lOffset);
return pCallbackListHead;
}
// 注册表回调函数结构体定义
typedef struct _CM_NOTIFY_ENTRY
{
LIST_ENTRY ListEntryHead;
ULONG UnKnown1;
ULONG UnKnown2;
LARGE_INTEGER Cookie;
PVOID Context;
PVOID Function;
}CM_NOTIFY_ENTRY, *PCM_NOTIFY_ENTRY;
VOID UnDriver(PDRIVER_OBJECT Driver)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
PVOID pCallbackListHeadAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
LONG lSpecialOffset = 0;
DbgPrint("hello lyshark.com \n");
// 查找 fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
/*
lyshark.com>
nt!CmUnRegisterCallback+0x6b:
fffff806`3a4271ab 4533c0 xor r8d,r8d
fffff806`3a4271ae 488d542438 lea rdx,[rsp+38h]
fffff806`3a4271b3 488d0d06eac3ff lea rcx,[nt!CallbackListHead (fffff806`3a065bc0)]
fffff806`3a4271ba e855e2e2ff call nt!CmListGetNextElement (fffff806`3a255414)
fffff806`3a4271bf 488bf8 mov rdi,rax
fffff806`3a4271c2 4889442440 mov qword ptr [rsp+40h],rax
fffff806`3a4271c7 4885c0 test rax,rax
fffff806`3a4271ca 0f84c7000000 je nt!CmUnRegisterCallback+0x157 (fffff806`3a427297) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
// 根据特征码获取地址
pCallbackListHeadAddress = SearchCallbackListHead(pSpecialData, ulSpecialDataSize, lSpecialOffset);
DbgPrint("[LyShark.com] CallbackListHead => %p \n", pCallbackListHeadAddress);
// 遍历链表结构
ULONG i = 0;
PCM_NOTIFY_ENTRY pNotifyEntry = NULL;
if (NULL == pCallbackListHeadAddress)
{
return FALSE;
}
// 开始遍历双向链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pCallbackListHeadAddress;
do
{
// 判断pNotifyEntry地址是否有效
if (FALSE == MmIsAddressValid(pNotifyEntry))
{
break;
}
// 判断回调函数地址是否有效
if (MmIsAddressValid(pNotifyEntry->Function))
{
DbgPrint("[LyShark.com] 回调函数地址: 0x%p | 回调函数Cookie: 0x%I64X \n", pNotifyEntry->Function, pNotifyEntry->Cookie.QuadPart);
}
// 获取下一链表
pNotifyEntry = (PCM_NOTIFY_ENTRY)pNotifyEntry->ListEntryHead.Flink;
} while (pCallbackListHeadAddress != (PVOID)pNotifyEntry);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
最终运行这个驱动程序,输出如下效果:

目前系统中有两个回调函数,这一点在第一张图片中也可以得到,枚举是正确的。
驱动开发:内核枚举Registry注册表回调的更多相关文章
- 驱动开发:内核监控Register注册表回调
在笔者前一篇文章<驱动开发:内核枚举Registry注册表回调>中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监 ...
- Win64 驱动内核编程-32.枚举与删除注册表回调
枚举与删除注册表回调 注册表回调是一个监控注册表读写的回调,它的效果非常明显,一个回调能实现在SSDT 上 HOOK 十几个 API 的效果.部分游戏保护还会在注册表回调上做功夫,监控 service ...
- Win64 驱动内核编程-6.内核里操作注册表
内核里操作注册表 RING0 操作注册表和 RING3 的区别也不大,同样是"获得句柄->执行操作->关闭句柄"的模式,同样也只能使用内核 API 不能使用 WIN32 ...
- 遍历注册表回调函数(仿PCHunter CmpBack)
遍历注册表回调函数(仿PCHunter CmpBack) typedef struct _CAPTURE_REGISTRY_MANAGER { PDEVICE_OBJECT deviceObject; ...
- 关于软件开发中兼容win7注册表的解决方案
关于软件开发中兼容win7注册表的解决方案 编写人:CC阿爸 2014-3-14 l 近来在开发一winform程序时,发现在xp 系统访问注册表一切正常.可偏这个时候,微软又提醒大家.Xp今年 ...
- 驱动开发:内核枚举PspCidTable句柄表
在上一篇文章<驱动开发:内核枚举DpcTimer定时器>中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows ...
- 驱动开发:内核枚举LoadImage映像回调
在笔者之前的文章<驱动开发:内核特征码搜索函数封装>中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核LoadImage映像回调,在Win64环境下我们可以设置一个 ...
- 【spring 注解驱动开发】spring组件注册
尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...
- Webbrowser指定IE内核版本(更改注册表)
如果电脑上安装了IE8或者之后版本的IE浏览器,Webbrowser控件会使用IE7兼容模式来显示网页内容.解决方法是在注册表中为你的进程指定引用IE的版本号. 比如我的程序叫做a.exe 对于32位 ...
随机推荐
- AOP实现切入
6.AOP实现切入 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程 通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术 AOP是OOP的延续,也 ...
- Python基础之dict和set的使用
dict Python内置了字典:dict的支持,dict全称dictionary,在其他语言种也称为map,使用键-值(key-value)存储,具有极快的查找速度. 举个例子,假设要根据同学的名字 ...
- PicGo+GitHub 图床搭建
前言 用GitHub搭建图床,在很久之前我就有了解,但由于市面上有挺多免费的图床,比如我之前一直在用的 路过图床,所以一直懒得动手搭建GitHub图床.一直到前两天我在完善博客的相册时,发现 路过图床 ...
- apk编辑器测评
hi你好,我今天要介绍的就是apk编辑器 这里我用的是apk编辑器专业版 APK编辑器 关于 APK 编辑器智友汉化组论坛:bbs.zhiyoo.com修改应用程序名称美化 UI: 更改背景图片删除广 ...
- 第五十二篇:webpack的loader(三) -url-loader (图片的loader)
好家伙, 1.什么是base64? 图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址. 这样做有什么意义呢?我们知道,我们所看到的网页上的每一个图片,都是需要 ...
- 线程池:ThreadPoolExecutor源码解读
目录 1 带着问题去阅读 1.1 线程池的线程复用原理 1.2 线程池如何管理线程 1.3 线程池配置的重要参数 1.4 shutdown()和shutdownNow()区别 1.5 线程池中的两个锁 ...
- KingbaseES 局部索引
一个列要不要建立btree索引,判断条件是其键值分布是否够离散,比如主键.唯一键,可以建立索引.如果这个列有大量重复的值,则建立索引没有意义. 在生产环境中常会碰到键值分布不均匀的列,如表t1有一个名 ...
- volatile 函数影响子查询提升
我们知道 volatile 函数会影响SQL的执行性能,比如:volatile 类型函数无法建函数索引.volatile 函数针对每条记录都要执行一次.本篇的例子主要讲述 volatile 类型的函数 ...
- 跟羽夏学 Ghidra ——初识
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- G&GH04 本地连接至远程
注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...