Windows内核事件通知机制

Windows内核事件通知机制

Windows内核中提供了一组接口,让我们可以针对某一事件注册通知例程,当这个事件发生的时候,我们注册的通知例程就会执行。

进程创建/退出通知

cpp 复制代码
// 注册进程创建通知回调
NTSTATUS PsSetCreateProcessNotifyRoutine(
    _In_ PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine, // 通知例程指针
    _In_ BOOLEAN Remove // 为false则注册例程,为true则移除通知例程
);

// 回调函数原型
typedef VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE)(
    _In_ HANDLE ParentId, // 父进程ID
    _In_ HANDLE ProcessId,  // 被创建的进程的ID
    _In_ BOOLEAN Create // 这个参数为TRUE表示当前是创建进程的通知,为FALSE表示当前是进程结束的通知
);

// 扩展版本(Windows Vista+)
NTSTATUS PsSetCreateProcessNotifyRoutineEx(
    _In_ PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
    _In_ BOOLEAN Remove
);

通过调用PsSetCreateProcessNotifyRoutine为进程创建和退出注册NotifyRoutine回调例程。

**回调例程是在目标进程的第一个线程运行前被调用的。**根据微软文档:

"For a new process, the CreateProcessNotifyEx routine is called after the initial thread is created, but before the thread begins running."

所以我们可以在回调中阻止进程的创建。

PsSetCreateProcessNotifyRoutineEx

该函数的第一个参数是类型为PCREATE_PROCESS_NOTIFY_ROUTINE_EX回调函数指针,定义如下:

cpp 复制代码
/// PCREATE_PROCESS_NOTIFY_ROUTINE_EX
/// <summary>
///  进程创建的通知回调
/// </summary>
/// <param name="pEprocess">进程对象指针</param>
/// <param name="hProcessId">进程ID</param>
/// <param name="pCreateInfo">进程相关信息</param>
void CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId,
	PPS_CREATE_NOTIFY_INFO pCreateInfo);

下面看看示例代码:

简单示例代码

这份代码来自谭文老师的《Windows内核编程》,回调例程中只是简单地输出进程的信息。

cpp 复制代码
/*
* 进程创建或者结束
* PsSetCreateProcessNotifyRoutine:
*	param1: 回调函数指针
*	param2: 创建还是注销回调
* 
* 回调是哪条线程执行的:
*	创建:哪条线程创建的进程,就在哪条线程的上下文中执行回调
*	销毁:哪条线程是进程最后一个退出的,就在哪条线程中执行回调
*/

#include <ntddk.h>
#include <windef.h>

void DriverUnload(PDRIVER_OBJECT pDriverObj);

/// <summary>
///  进程创建的通知回调
/// </summary>
/// <param name="pEprocess">进程对象指针</param>
/// <param name="hProcessId">进程ID</param>
/// <param name="pCreateInfo">进程相关信息</param>
void CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId,
	PPS_CREATE_NOTIFY_INFO pCreateInfo);

static bool sg_bSuccReg = false;

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
	UNREFERENCED_PARAMETER(pRegPath);

	NTSTATUS status = STATUS_SUCCESS;
	do
	{
		pDriverObj->DriverUnload = DriverUnload;
		
		// 创建通知回调
		status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, false);
		if (!NT_SUCCESS(status))
		{
			sg_bSuccReg = false;
			break;
		}

		sg_bSuccReg = true;
	} while (false);

	return status;
}

void DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	UNREFERENCED_PARAMETER(pDriverObj);

	if (sg_bSuccReg)
	{
		// 传递true表明注销回调
		PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, true);
		sg_bSuccReg = false;
	}
}

void CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId,
	PPS_CREATE_NOTIFY_INFO pCreateInfo)
{
	HANDLE hCurrentThreadID = PsGetCurrentThreadId();
	if (pCreateInfo == nullptr) // 进程结束为nullptr,进程创建时为非nullptr
	{
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"[Destroy] ThreadID: 0x%X, ProcessID: 0x%X\n",
			(DWORD)hCurrentThreadID, (DWORD)hProcessId);
		return;
	}

	// 进程创建
	// HANDLE hParentProcessID = pCreateInfo->CreatingThreadId.UniqueProcess;
	// HANDLE hParentThreadID = pCreateInfo->CreatingThreadId.UniqueThread;
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
		"[Create] ThreadID: 0x%X, ProcessID: 0x%X, ProcessName: %wZ\n",
		(DWORD)hCurrentThreadID, (DWORD)hProcessId,
		pCreateInfo->ImageFileName);

	return;
}

通过回调例程阻止进程创建

要阻止进程创建,我们可以把回调例程中CreateInfo参数中的CreationStatus设置为STATUS_ACCESS_DENIED,并返回,这样就阻止进程的创建了。下面列举了可以使用的错误状态码:

  • STATUS_ACCESS_DENIED- 拒绝访问
  • STATUS_UNSUCCESSFUL- 操作不成功
  • STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY- 策略禁用且不弹窗
cpp 复制代码
#include <ntddk.h>
#include <windef.h>

// 这个函数貌似没有文档化。该函数的作用是获取EPROCESS中ImageFileName
EXTERN_C UCHAR* PsGetProcessImageFileName(PEPROCESS Process);

void DriverUnload(PDRIVER_OBJECT pDriverObj);

void CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId,
	PPS_CREATE_NOTIFY_INFO pCreateInfo);

static bool sg_bSuccReg = false;

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
	UNREFERENCED_PARAMETER(pRegPath);

	NTSTATUS status = STATUS_SUCCESS;
	do
	{
		pDriverObj->DriverUnload = DriverUnload;

		// 创建通知回调
		status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, false);
		if (!NT_SUCCESS(status))
		{
			sg_bSuccReg = false;
			break;
		}

		sg_bSuccReg = true;
	} while (false);

	return status;
}

void DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	UNREFERENCED_PARAMETER(pDriverObj);

	if (sg_bSuccReg)
	{
		// 传递true表明注销回调
		PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, true);
		sg_bSuccReg = false;
	}
}

void CreateProcessNotifyEx(PEPROCESS pEprocess, HANDLE hProcessId,
	PPS_CREATE_NOTIFY_INFO pCreateInfo)
{
	HANDLE hCurrentThreadID = PsGetCurrentThreadId();
	if (pCreateInfo == nullptr) // 进程结束为nullptr,进程创建时为非nullptr
	{
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"[Destroy] ThreadID: 0x%X, ProcessID: 0x%X\n",
			(DWORD)hCurrentThreadID, (DWORD)hProcessId);
		return;
	}

	// 进程创建,我们阻止notepad运行
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
		"[Create] ThreadID: 0x%X, ProcessID: 0x%X, ProcessName: %wZ\n",
		(DWORD)hCurrentThreadID, (DWORD)hProcessId,
		pCreateInfo->ImageFileName);

	// 我们要获取的是EPROCESS中的ImageFileName
	// pCreateInfo->ImageFileName是完整的路径
	PUCHAR processName = PsGetProcessImageFileName(pEprocess);
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "ImageFileName: %s\n", processName);
	if (processName != nullptr && _stricmp((PCHAR)processName, "notepad.exe") == 0)
	{
		// 通过设置这个标志,阻止notepad.exe运行
		pCreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
	}

	return;
}

线程创建/退出通知

cpp 复制代码
// 注册线程创建通知回调
NTSTATUS PsSetCreateThreadNotifyRoutine(
    _In_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);

// 回调函数原型
typedef VOID (*PCREATE_THREAD_NOTIFY_ROUTINE)(
    _In_ HANDLE ProcessId,
    _In_ HANDLE ThreadId,
    _In_ BOOLEAN Create
);

回调例程是在线程运行前被调用的。

模块加载通知

cpp 复制代码
// 注册模块加载通知回调
NTSTATUS PsSetLoadImageNotifyRoutine(
    _In_ PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);

// 回调函数原型
typedef VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE)(
    _In_opt_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,
    _In_ PIMAGE_INFO ImageInfo
);

// 注销回调
NTSTATUS PsRemoveLoadImageNotifyRoutine(
  [in] PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);

这里的回调例程是在模块被加载完成后执行的。

示例代码

cpp 复制代码
#include <ntddk.h>
#include <windef.h>

VOID DriverUnload(PDRIVER_OBJECT pDriverObj);
VOID LoadImageNotifyRoutine(PUNICODE_STRING fullImageName, HANDLE ProcessId,
	PIMAGE_INFO ImageInfo);

static bool sg_bSuccReg = false;

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
	NTSTATUS status = STATUS_SUCCESS;
	do
	{
		pDriverObj->DriverUnload = DriverUnload;
		status = PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine);
		if (!NT_SUCCESS(status))
		{
			break;
		}

		sg_bSuccReg = true;
	} while (false);

	return status;
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
	if (sg_bSuccReg)
	{
		PsRemoveLoadImageNotifyRoutine(LoadImageNotifyRoutine);
		sg_bSuccReg = false;
	}

	return;
}

// 这是单向通知,没有卸载事件通知回调
VOID LoadImageNotifyRoutine(PUNICODE_STRING fullImageName, HANDLE ProcessId,
	PIMAGE_INFO ImageInfo)
{
	if (!fullImageName || !ImageInfo)
	{
		return;
	}

	if (ImageInfo->ExtendedInfoPresent)
	{
		PIMAGE_INFO_EX pInfo = CONTAINING_RECORD(ImageInfo, IMAGE_INFO_EX, ImageInfo);
		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
			"ModLoad Name: %wZ, ProcessID: 0x%X, FileObj: 0x%X, ImageBase: 0x%X",
			fullImageName, (DWORD)ProcessId, pInfo->FileObject,
			pInfo->ImageInfo.ImageBase);
	}

	return;
}

注册表操作通知

cpp 复制代码
// 注册注册表操作通知回调
NTSTATUS CmRegisterCallback(
    _In_ PEX_CALLBACK_FUNCTION Function,
    _In_opt_ PVOID Context,
    _Out_ PLARGE_INTEGER Cookie
);

// 扩展版本
NTSTATUS CmRegisterCallbackEx(
    _In_ PEX_CALLBACK_FUNCTION Function,
    _In_ PCUNICODE_STRING Altitude,
    _In_ PVOID Driver,
    _In_opt_ PVOID Context,
    _Out_ PLARGE_INTEGER Cookie,
    _Reserved_ PVOID Reserved
);

// 回调函数原型
NTSTATUS RegistryCallback(
    _In_opt_ PVOID CallbackContext,
    _In_opt_ PVOID Argument1,  // REG_NOTIFY_CLASS
    _In_opt_ PVOID Argument2   // 操作信息结构体
);
相关推荐
caimouse2 小时前
Reactos 第 5 章 进程与线程 — 5.5 Windows 的可执行程序映像
windows
caimouse2 小时前
Reactos 第 5 章 进程与线程 — 5.2 Windows 进程的用户空间
windows·架构
莫逸风2 小时前
【AgentScope】6.文件系统(Filesystem)详解
开发语言·windows·springai·agentscope·agnet
超级无敌zhq2 小时前
内网权限维持实战:打造持久化后门与隐蔽通道
网络·windows·安全·网络安全
shandianchengzi3 小时前
【记录】VSCode|Windows 下 VS Code 配置 Git Bash 为默认终端完整教程
windows·git·vscode·bash
caimouse3 小时前
Reactos 第 5 章 进程与线程 — 5.6 Windows 的进程创建和映像装入
windows
泡^泡3 小时前
Python数据类型与运算符
开发语言·windows·python
caimouse14 小时前
Reactos 第 4 章 对象管理 — 4.5 几个常用的内核函数
c语言·windows·架构
caimouse15 小时前
Reactos 第 4 章 对象管理 — 4.3 句柄和句柄表(Handle & Handle Table)
c语言·windows·架构