草庐IT

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

LyShark 2023-03-28 原文

在笔者上一篇文章《驱动开发:内核枚举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. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  5. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  6. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  7. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  8. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  9. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐