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   // 操作信息结构体
);
相关推荐
广州灵眸科技有限公司7 小时前
xfce桌面触摸校准:基于灵眸科技EASY-EAl-Orin-Nano
数据库·windows·科技
王维同学13 小时前
[原创][Windows C++]LSA 认证、安全与通知包的注册表枚举
c++·windows·安全
YCOSA202513 小时前
雨晨 Windows 11 IOT 企业版 LTSC 24H2 轻装 2合1 26100.8968
windows·物联网
汤姆yu13 小时前
CodeGeeX 4完整安装与实操使用全指南
人工智能·windows·ai·智能体·视频模型
阿昭L15 小时前
Windows对象句柄回调 (Object Handle Callbacks)
windows内核
吐了啊取名字太难16 小时前
美颜系统AI修图本地跑并支持Mac、win、安卓、iOS不卡顿
android·人工智能·windows·数码相机·mac·ai编程
筱羽_筱羽18 小时前
VS2022 编译SOEM
windows·visualstudio·soem
love530love20 小时前
【排障实录】GPT Desktop (Codex) 开启 WSL 智能体模式后无法启动?手把手教你修复
人工智能·windows·gpt·agent
WangWei_CM20 小时前
Session Manager 的 BootExecute、SetupExecute 与 Execute 多字符串解析
windows
寒水馨21 小时前
Windows 11下载、安装openclaw-v2026.7.1(附安装包OpenClawCompanion-Setup-x64.exe)
windows·大模型·开源软件·ai助手·openclaw·个人ai·中文版