在笔者上一篇文章《驱动开发:内核枚举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注册表回调的更多相关文章

  1. 驱动开发:内核监控Register注册表回调

    在笔者前一篇文章<驱动开发:内核枚举Registry注册表回调>中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监 ...

  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. 驱动开发:内核枚举PspCidTable句柄表

    在上一篇文章<驱动开发:内核枚举DpcTimer定时器>中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows ...

  7. 驱动开发:内核枚举LoadImage映像回调

    在笔者之前的文章<驱动开发:内核特征码搜索函数封装>中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核LoadImage映像回调,在Win64环境下我们可以设置一个 ...

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

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

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

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

随机推荐

  1. Modbus转Profinet网关案例 | 三菱FR-A700系列变频器配置方法

    本案例是利用小疆智控Modbus转Profinet网关GW-PN5001把三菱FR-A700变频器接入到西门子1200PLC.实现Profinet转Modbus的通讯协议的互转. 用到设备有:三菱FR ...

  2. 技术分享 | innodb_buffer_pool_size为什么无法调低至1GB以内

    前言 innodb_buffer_pool_size可以调大,却不能调小至1GB以内,这是为什么? MySQL 版本:5.7.30 测试环境有台 MySQL 服务器反应很慢,检查系统后发现内存使用量已 ...

  3. Springboot Jpa: [mysql] java.sql.SQLException: Duplicate entry 'XXX' for key 'PRIMARY'

    前言 1.问题背景 偶尔会出现登录请求出错的情况,一旦失败就会短时间内再也登录不上,更换浏览器或者刷新可能会暂时解决这个问题. 项目运行日志如下: 2022-07-21 09:43:40.946 DE ...

  4. Luogu P1903 [国家集训队]数颜色 / 维护队列 (带修莫队)

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

  5. 大家都能看得懂的源码 - ahooks useSet 和 useMap

    本文是深入浅出 ahooks 源码系列文章的第十篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 今天我们来聊聊 ahooks 中对 Map 和 Set 类型进行状 ...

  6. shiro登录过程

    工作流程: 浏览器将用户名.密码.是否记住登录等信息发送给登录controller , new UsernamePasswordToken()获取token,将用户名.加密后的密码.rememberM ...

  7. Ubuntu20.04配置 ES7.17.0集群

    Ubuntu20.04配置 ES7.17.0集群 ES能做什么? elasticsearch简写es,es是一个高扩展.开源的全文检索和分析引擎,它可以准实时地快速存储.搜索.分析海量的数据. Ubu ...

  8. JOIOI王国 - 二分+贪心

    题面 题解 通过一句经典的话"最大值的最小值" 我判断它是二分题, 不难发现,整个图形中两个省的分界线是一条单调不递减或单调不递增的折线. 而且,越到后来它的最大值只会越来越大,最 ...

  9. Android的Handler线程切换原理

    Handler是我们在开发中经常会接触到的类,因为在Android中,子线程一般是不能更新UI的. 所以我们会使用Handler切换到主线程来更新UI,那Handler是如何做到实现不同线程之间的切换 ...

  10. DFS算法-求集合的所有子集

    目录 1. 题目来源 2. 普通方法 1. 思路 2. 代码 3. 运行结果 3. DFS算法 1. 概念 2. 解题思路 3. 代码 4. 运行结果 4. 对比 1. 题目来源 牛客网,集合的所有子 ...