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 // 操作信息结构体
);