6.8 Windows驱动开发:内核枚举Registry注册表回调

2023-12-02 09:20

本文主要是介绍6.8 Windows驱动开发:内核枚举Registry注册表回调,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在笔者上一篇文章《内核枚举LoadImage映像回调》LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与LoadImage消息不同Registry消息不需要解密只要找到CallbackListHead消息回调链表头并解析为_CM_NOTIFY_ENTRY结构即可实现枚举。

Registry注册表回调是Windows操作系统提供的一种机制,它允许开发者在注册表发生变化时拦截并修改注册表的操作。Registry注册表回调是通过操作系统提供的注册表回调机制来实现的。

当应用程序或系统服务对注册表进行读写操作时,操作系统会触发注册表回调事件,然后在注册表回调事件中调用注册的Registry注册表回调函数。开发者可以在Registry注册表回调函数中执行自定义的逻辑,例如记录日志,过滤敏感数据,或者阻止某些操作。

Registry注册表回调可以通过操作系统提供的注册表回调函数CmRegisterCallback和CmUnRegisterCallback来进行注册和注销。同时,Registry注册表回调函数需要遵守一定的约束条件,例如不能在回调函数中对注册表进行修改,不能调用一些内核API函数等。

Registry注册表回调在安全软件、系统监控和调试工具等领域有着广泛的应用。

我们来看一款闭源ARK工具是如何实现的:

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

当得到注册表链表入口0xfffff8063a065bc0直接将其解析为_CM_NOTIFY_ENTRY即可得到数据,如果要遍历下一个链表则只需要ListEntryHead.Flink向下移动指针即可。

// 注册表回调函数结构体定义
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映像回调》文章中已经介绍了定位方式此处跳过介绍,具体实现代码如下。

#include <ntifs.h>
#include <windef.h>// 指定内存区域的特征码扫描
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 链表地址
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>nt!CmUnRegisterCallback+0x6b:fffff806`3a4271ab 4533c0          xor     r8d,r8dfffff806`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,raxfffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],raxfffff806`3a4271c7 4885c0          test    rax,raxfffff806`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 \n");// 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]/*lyshark>nt!CmUnRegisterCallback+0x6b:fffff806`3a4271ab 4533c0          xor     r8d,r8dfffff806`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,raxfffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],raxfffff806`3a4271c7 4885c0          test    rax,raxfffff806`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] CallbackListHead => %p \n", pCallbackListHeadAddress);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

运行这段代码,并可得到注册表回调入口地址,输出效果如下所示:

得到了注册表回调入口地址,接着直接循环遍历输出这个链表即可得到所有的注册表回调。

#include <ntifs.h>
#include <windef.h>// 指定内存区域的特征码扫描
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 链表地址
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>nt!CmUnRegisterCallback+0x6b:fffff806`3a4271ab 4533c0          xor     r8d,r8dfffff806`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,raxfffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],raxfffff806`3a4271c7 4885c0          test    rax,raxfffff806`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 \n");// 查找 fffff806`3a4271b3 488d0d06eac3ff  lea     rcx,[nt!CallbackListHead (fffff806`3a065bc0)]/*lyshark>nt!CmUnRegisterCallback+0x6b:fffff806`3a4271ab 4533c0          xor     r8d,r8dfffff806`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,raxfffff806`3a4271c2 4889442440      mov     qword ptr [rsp+40h],raxfffff806`3a4271c7 4885c0          test    rax,raxfffff806`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] 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] 回调函数地址: 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;
}

最终运行这个驱动程序,输出如下效果:

目前系统中有两个回调函数,这一点在第一张图片中也可以得到,枚举是正确的。

这篇关于6.8 Windows驱动开发:内核枚举Registry注册表回调的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/444779

相关文章

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

如何基于Python开发一个微信自动化工具

《如何基于Python开发一个微信自动化工具》在当今数字化办公场景中,自动化工具已成为提升工作效率的利器,本文将深入剖析一个基于Python的微信自动化工具开发全过程,有需要的小伙伴可以了解下... 目录概述功能全景1. 核心功能模块2. 特色功能效果展示1. 主界面概览2. 定时任务配置3. 操作日志演示

Windows Server 2025 搭建NPS-Radius服务器的步骤

《WindowsServer2025搭建NPS-Radius服务器的步骤》本文主要介绍了通过微软的NPS角色实现一个Radius服务器,身份验证和证书使用微软ADCS、ADDS,具有一定的参考价... 目录简介示意图什么是 802.1X?核心作用802.1X的组成角色工作流程简述802.1X常见应用802.

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录

windows和Linux安装Jmeter与简单使用方式

《windows和Linux安装Jmeter与简单使用方式》:本文主要介绍windows和Linux安装Jmeter与简单使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录Windows和linux安装Jmeter与简单使用一、下载安装包二、JDK安装1.windows设

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

一文教你如何解决Python开发总是import出错的问题

《一文教你如何解决Python开发总是import出错的问题》经常朋友碰到Python开发的过程中import包报错的问题,所以本文将和大家介绍一下可编辑安装(EditableInstall)模式,可... 目录摘要1. 可编辑安装(Editable Install)模式到底在解决什么问题?2. 原理3.

SpringIOC容器Bean初始化和销毁回调方式

《SpringIOC容器Bean初始化和销毁回调方式》:本文主要介绍SpringIOC容器Bean初始化和销毁回调方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录前言1.@Bean指定初始化和销毁方法2.实现接口3.使用jsR250总结前言Spring Bea