首页
社区
课程
招聘
[原创]回调钩子的发展,作用与破除
发表于: 2025-7-26 21:52 1589

[原创]回调钩子的发展,作用与破除

2025-7-26 21:52
1589

回调钩子的发展历史

微软为了windwos生态体系的快速发展,放开了驱动开发的权限,第三方驱动可以加载到内核并享有内核权限。在早期32位内核中,微软并未对这些驱动做限制,任何产品都能加载自己的驱动到内核,导致内核钩子满天飞,系统不稳定。



历史的发展总是曲折前进的, 在x64内核上增加了PG和签名校验

PatchGuard 补丁防护

描述:Patch Guard(简称PG)是Windows x64系统中用于保护内核代码完整性和安全性的保护机制,能够防止任何不受信任的代码或驱动程序修改内核代码,从而防止系统破坏和恶意软件的传播。Patch Guard在系统启动时进行验证,并在系统运行过程中定期执行检查以确保内核代码的完整性。如果发现任何不正确的修改,Patch Guard会使系统蓝屏并重启系统以确保安全性,蓝屏代码为0x109。

原理:读取内核中的数据,并与系统初始化时的值进行比较。


Windows 驱动签名机制

数字签名通过加密技术绑定驱动程序与开发者身份。当用户安装驱动时,Windows系统会验证签名中的证书链,确认其是否由受信任的第三方机构颁发。这一过程确保驱动程序的发布者身份真实可信,杜绝了恶意软件伪装成合法驱动的风险。


Windows NT/2000/XP(32 位时代)

  • 初始实现(1993~2001)
    • 首个公开版本出现在 Windows NT 4.0,通过 PsSetCreateProcessNotifyRoutine 注册回调,仅支持进程创建/终止通知。

回调钩子最早出现在32位内核,因为32位内核随便hook,导致回调使用率不高,64位PG和签名引入后加上微软提供了更完善的回调钩子,回调钩子使用率得以上升。

回调钩子的使用

作用:监控或拦截事件;例:在回调监控下,恶意软件再去伪装和隐藏进程都是没有意义的。

提供的回调有很多种,如进程回调,线程回调,模块加载回调,注册回调...

这里拿进程回调举例


NTSTATUS PsSetCreateProcessNotifyRoutine(
  [in] PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
  [in] BOOLEAN                        Remove
);
NTSTATUS PsSetLoadImageNotifyRoutineEx(
  [in] PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine,
  [in] ULONG_PTR                  Flags
);
  • NotifyRoutine:指向回调函数的指针。这个回调函数必须符合 PS_CREATE_PROCESS_NOTIFY_ROUTINE 类型,它定义了当创建新进程时应该调用的函数。
  • Remove:一个布尔值,如果设置为TRUE,则表示要移除之前注册的回调函数;如果设置为FALSE,则表示注册一个新的回调函数

EX版本(x64增加)相比没有EX版本,提供了更多的进程信息,并且可以拦截进程启动,非EX版本只能监控无法直接拦截,就像无能的丈夫。

#include <ntifs.h>

NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);

PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
	NTSTATUS st = STATUS_UNSUCCESSFUL;
	PEPROCESS ProcessObj = NULL;
	PCHAR string = NULL;
	st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
	if (NT_SUCCESS(st))
	{
		string = PsGetProcessImageFileName(ProcessObj);
		ObfDereferenceObject(ProcessObj);
	}
	return string;
}

VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
	char ProcName[16] = { 0 };
	if (CreateInfo != NULL)
	{
		strcpy(ProcName, PsGetProcessImageFileName(Process));
		DbgPrintEx(77,0,"父进程ID: %ld  --->父进程名: %s --->进程名: %s---->进程路径:%wZ", CreateInfo->ParentProcessId,
			GetProcessNameByProcessId(CreateInfo->ParentProcessId),
			PsGetProcessImageFileName(Process), CreateInfo->ImageFileName);
	}
	else
	{
		strcpy(ProcName, PsGetProcessImageFileName(Process));
		DbgPrintEx(77,0,"进程[ %s ] 离开了,程序被关闭了", ProcName);
	}
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	NTSTATUS status;
	status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);

	DbgPrintEx(77, 0, "MyCreateProcessNotifyEx[ %p ] \r\n", MyCreateProcessNotifyEx);
	Driver->DriverUnload = UnDriver;

	return status;
}


typedef struct _PS_CREATE_NOTIFY_INFO {
    _In_ SIZE_T Size;
    union {
        _In_ ULONG Flags;
        struct {
            _In_ ULONG FileOpenNameAvailable : 1;
            _In_ ULONG IsSubsystemProcess : 1;
            _In_ ULONG Reserved : 30;
        };
    };
    _In_ HANDLE ParentProcessId;
    _In_ CLIENT_ID CreatingThreadId;
    _Inout_ struct _FILE_OBJECT *FileObject;
    _In_ PCUNICODE_STRING ImageFileName;
    _In_opt_ PCUNICODE_STRING CommandLine;
    _Inout_ NTSTATUS CreationStatus;
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

其中CreationStatus返回失败可以阻止进程创建。




破解 PsSetCreateProcessNotifyRoutineEx 函数的使用限制


内核通过 MmVerifyCallbackFunction 验证此回调是否合法, 但此函数只是简单的验证了一下 DriverObject->DriverSection->Flags 的值是不是为 0x20:

使用此函数, 一定要设置 IMAGE_OPTIONAL_HEADER 中的 DllCharacterisitics 字段设置为:IMAGE_DLLCHARACTERISITICS_FORCE_INTEGRITY 属性,该属性是一个驱动强制签名属性。

右击项目,选择属性

选中配置属性中的链接器,点击命令行

在其它选项中输入: /INTEGRITYCHECK 表示设置; /INTEGRITYCHECK:NO 表示不设置




破除回调钩子

1.PspNotifyEnableMask 标志位

  • PspNotifyEnableMask的不同位控制不同类型回调的启用状态‌16
    • 第0位:模块加载回调
    • 第1位:进程回调(PsSetCreateProcessNotifyRoutine)
    • 第2位:进程回调(PsSetCreateProcessNotifyRoutineEx)
    • 第3位:线程回调



2.数组PspCreateProcessNotifyRoutine 清空

参考wrk得知回调储存在数组中

x64下最大数组数量




3.抢占注册

有事没事先注册64个

4.强行ret

改别人驱动代码,强行把回调函数ret

5.PsSetCreateProcessNotifyRoutine

获取回调函数地址,给他注销

PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);

注意在PspCreateProcessNotifyRoutine数组中直接储存的不是回调函数地址,而是结构体地址具体可以参考wrk

typedef struct _EX_CALLBACK_ROUTINE_BLOCK {
    EX_RUNDOWN_REF        RundownProtect;
    PEX_CALLBACK_FUNCTION Function;
    PVOID                 Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;

其中 PEX_CALLBACK_FUNCTION Function; 是回调函数的地址




传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-7-26 21:56 被只会逆一点点编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (5)
雪    币: 134
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
兄弟对安卓内核有看法么
2025-7-30 11:03
0
雪    币: 108
活跃值: (1524)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
哈哈就像无能的丈夫 这词想的我记住了
2025-7-30 11:12
0
雪    币: 236
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
加油
2025-10-12 20:09
0
雪    币: 727
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5

2025-12-4 16:37
0
雪    币: 727
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
我明明把 PspNotifyEnableMask 都改为0了 ,怎么进程回调还能工作...
2025-12-4 16:37
0
游客
登录 | 注册 方可回帖
返回