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

驱动,开发,内核,枚举,registry,注册表,回调 · 浏览次数 : 295

小编点评

```python # 注册回调函数结构体定义 class PCM_NOTIFY_ENTRY: ListEntryHead: PVOID Function: PVOID # 定义回调函数结构体 class PCM_NOTIFY_ENTRY: ListEntryHead: PVOID Function: PVOID # 定义驱动函数入口 def UnDriver(pDriver): # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, pDriver.Function) # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, UnDriver) # 遍历链表结构 for i in range(pDriver.GetNumCallbacks()): # 获取回调函数地址 pCallbackEntry = pDriver.GetCallbackEntry(i) pCallbackEntry.Function = UnDriver # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, pDriver.Function) # 驱动程序加载 Driver->DriverUnload = UnDriver ``` **排版说明:** ```python # 注册回调函数结构体定义 class PCM_NOTIFY_ENTRY: ListEntryHead: PVOID Function: PVOID # 定义回调函数结构体 class PCM_NOTIFY_ENTRY: ListEntryHead: PVOID Function: PVOID # 定义驱动函数入口 def UnDriver(pDriver): # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, pDriver.Function) # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, UnDriver) # 遍历链表结构 for i in range(pDriver.GetNumCallbacks()): # 获取回调函数地址 pCallbackEntry = pDriver.GetCallbackEntry(i) pCallbackEntry.Function = UnDriver # 注册回调函数 pDriver.RegisterCallbackEntry(PCM_NOTIFY_ENTRY, pDriver.Function) ```

正文

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

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

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

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

在笔者前一篇文章`《驱动开发:内核枚举Registry注册表回调》`中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监控函数,通过这两个函数可以在不劫持内核API的前提下实现对注册表增加,删除,创建等事件的有效监控,注册表监视通常会通过`CmRegisterCallback`创建监控事件并传入自己的回调函数,与该创建对应的是

驱动开发:内核枚举进程与线程ObCall回调

在笔者上一篇文章`《驱动开发:内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。

驱动开发:内核枚举ShadowSSDT基址

在笔者上一篇文章`《驱动开发:Win10枚举完整SSDT地址表》`实现了针对`SSDT`表的枚举功能,本章继续实现对`SSSDT`表的枚举,ShadowSSDT中文名`影子系统服务描述表`,SSSDT其主要的作用是管理系统中的图形化界面,其`Win32`子系统的内核实现是`Win32k.sys`驱动,属于GUI线程的一部分,其自身没有导出表,枚举`SSSDT`表其与`SSDT`原理基本一致。

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

在笔者之前的文章`《驱动开发:内核特征码搜索函数封装》`中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核`LoadImage`映像回调,在Win64环境下我们可以设置一个`LoadImage`映像加载通告回调,当有新驱动或者DLL被加载时,回调函数就会被调用从而执行我们自己的回调例程,映像回调也存储在数组里,枚举时从数组中读取值之后,需要进行位运算解密得到地址。

驱动开发:内核注册并监控对象回调

在笔者上一篇文章`《驱动开发:内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止

驱动开发:内核监控进程与线程回调

在前面的文章中`LyShark`一直在重复的实现对系统底层模块的枚举,今天我们将展开一个新的话题,内核监控,我们以`监控进程线程`创建为例,在`Win10`系统中监控进程与线程可以使用微软提供给我们的两个新函数来实现,此类函数的原理是创建一个回调事件,当有进程或线程被创建或者注销时,系统会通过回调机制将该进程相关信息优先返回给我们自己的函数待处理结束后再转向系统层。

驱动开发:内核测试模式过DSE签名

微软在`x64`系统中推出了`DSE`保护机制,DSE全称`(Driver Signature Enforcement)`,该保护机制的核心就是任何驱动程序或者是第三方驱动如果想要在正常模式下被加载则必须要经过微软的认证,当驱动程序被加载到内存时会验证签名的正确性,如果签名不正常则系统会拒绝运行驱动,这种机制也被称为驱动强制签名,该机制的作用是保护系统免受恶意软件的破坏,是提高系统安全性的一种手段

驱动开发:内核无痕隐藏自身分析

在笔者前面有一篇文章`《驱动开发:断链隐藏驱动程序自身》`通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的驱动隐藏的,总体来说作者的思路是最终寻找到`MiProcessLoaderEntry`的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。

驱动开发:内核监视LoadImage映像回调

在笔者上一篇文章`《驱动开发:内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要