欢迎光临
我们一直在努力

Windows内核事件通知机制

Windows内核事件通知机制

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

进程创建/退出通知

// 注册进程创建通知回调
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回调函数指针,定义如下:

/// 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内核编程》,回调例程中只是简单地输出进程的信息。

/*
* 进程创建或者结束
* 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- 策略禁用且不弹窗

在这里插入图片描述

#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;
}

线程创建/退出通知

// 注册线程创建通知回调
NTSTATUS PsSetCreateThreadNotifyRoutine(
_In_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);

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

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

模块加载通知

// 注册模块加载通知回调
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
);

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

示例代码

#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;
}

注册表操作通知

// 注册注册表操作通知回调
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 // 操作信息结构体
);

赞(0)
未经允许不得转载:171主机测评 » Windows内核事件通知机制
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址