文章总结: 本文展示了一个已签名rootkit如何利用PsSetCreateProcessNotifyRoutineEx注册回调,在进程创建前阻止EDR进程启动,从而禁用系统安全防护。该攻击凸显了内核级防护的重要性,建议加强驱动签名策略与安全启动机制。 综合评分: 90 文章分类: 红队,恶意软件,渗透测试
当猎手沦为猎物:用自定义回调禁用 EDR
原创
Saad Ahla Saad Ahla
赛博生存指南
2026年8月10日 21:54 中国台湾
在小说阅读器读本章
去阅读
原文:https://www.alteredsecurity.com/post/when-the-hunter-becomes-the-hunted-using-custom-callbacks-to-disable-edrs 作者:Saad Ahla(Altered Security 安全研究员) 发布时间:2024-06-20 翻译模型:GLM-5.2
引言
在不断演进的网络安全格局中,攻击者与防御者之间的较量从未停歇。安全机制——尤其是内核层的那些——被设计用来对抗复杂威胁、提供坚固防护。然而,随着攻击者持续发明绕过这些防御的新方法,那些猎手——我们赖以信任的 EDR(Endpoint Detection and Response,终端检测与响应)系统——自身也可能沦为猎物。本文将展示一个令人不寒而栗的演示:一个已签名的 rootkit 如何利用 PsSetCreateProcessNotifyRoutine 函数来瘫痪 EDR 进程。通过注册一个自定义回调,这个 rootkit 从侧翼悄然击穿安全防御——它阻止关键 EDR 进程启动,让系统暴露在无法被检测的恶意活动面前。让我们一起探索这种高级威胁战术,并由此强调:必须强化内核级防护,才能维持安全基础设施的完整性与有效性。
EDR 是如何检测进程创建的?
当一个进程(比如 PID 为 1234 的 malware.exe)被创建时,CreateProcessA/W 与 NtCreateProcess 等函数会被执行,触发一次进入 Windows 内核的系统调用。随后内核遍历 nt!PspCallProcessNotifyRoutines 数组,调用其中已注册的进程创建通知例程——该数组保存着指向各个回调函数的指针。这些例程包含由各类驱动注册的回调。
在下图中,标红的部分如 WdFilter.sys 与 mssecflt.sys 代表的是 EDR 系统的组成驱动,分别属于 Windows Defender 与 Microsoft Defender for Endpoint(MDE)。这些 EDR 专用驱动会注册回调来监控并响应进程创建事件。当一个进程创建事件发生时,这些回调被触发,使 EDR 系统能够检查该进程的细节——例如命令行参数、可执行映像、内存使用情况以及其他相关信息。此外,EDR 系统还可能向进程内存中注入一个 DLL 来 hook 某些 API,以此增强其监控与管控该进程的能力,达到安全目的。
图:EDR 如何检测并监控进程创建
借助这些回调,EDR 系统能够实时有效地检测并响应潜在威胁。这套机制允许它们进行深入检查并采取适当行动——例如阻断恶意进程、向安全管理员告警,或收集取证数据以供进一步分析。确保这些回调例程的完整性,对于维持稳健的安全措施、防止恶意行为者绕过检测机制至关重要。
PspCreateProcessNotifyRoutine 数组
EDR(终端检测与响应)系统、杀毒软件(AV)以及 Sysmon(系统监视器)会在回调数组中注册回调例程,以便在进程或线程被创建、或映像被加载时获得通知。这些回调提供额外信息,让这些安全工具能在运行时检测恶意软件。打补丁(patch)或禁用这些回调,会让 EDR、AV 和 Sysmon「致盲」,使其无法获取关于恶意活动的关键信息。
我使用内核调试器 WinDbg 逆向了内核,检查了一个名为 PspCreateProcessNotifyRoutine 的特定回调数组。这个数组存储着由各类驱动注册的所有进程创建通知回调。安全软件与 EDR 系统使用 PsSetCreateProcessNotifyRoutine、PsSetCreateProcessNotifyRoutineEx 以及 PsSetCreateProcessNotifyRoutineEx2 等函数,把它们的进程创建回调注册进这个数组。每个函数都允许驱动把自己的特定回调添加到数组中,使其能够有效地监控进程创建事件。
图:PspCreateProcessNotifyRoutine 数组
要获取通知例程的实际地址,需要对数组中的值与 0xFFFFFFFFFFFFFFF8 做按位与(AND)运算。这个运算会正确对齐地址,其结果给出回调例程的实际地址。
图:Windows Defender 的进程创建回调例程地址
可以看到, Windows Defender filtering 驱动正在注册一个进程创建通知回调,用于监控或记录进程创建事件,或用于实现反恶意软件防护。图中 WinDbg 输出里高亮的 WdFilter!MpCreateProcessNotifyRoutineEx 函数就是证据。该函数属于 Windows Defender filtering 驱动,它在检测与防范恶意软件方面扮演关键角色——通过监控进程创建并记录相关信息。
如果我能 patch 或从进程回调数组中移除这一条目,就能有效地禁用运行时检测与防范机制。这将使系统暴露在恶意软件面前,因为 EDR 与 AV 软件将不再收到新进程被创建的通知。不过,如何 patch 或移除这些条目的具体细节及其影响,已超出本文范畴。本文的重点在于展示: 如何为「阻止 EDR 进程启动」注册进程创建内核回调 。
注册进程创建通知以阻断 EDR 进程创建
函数 PsSetCreateProcessNotifyRoutineEx 注册了一个自定义回调例程 blockEDR,每当进程被创建或终止时,Windows 内核都会调用它。blockEDR 函数遵循 PCREATE_PROCESS_NOTIFY_ROUTINE_EX 原型,使其能够接收详细的进程创建与删除事件。这些事件包含进程标识符(PID)、映像文件名、命令行,以及关于被创建进程的其他相关信息,详见 PS_CREATE_NOTIFY_INFO 结构体。
下面是一个演示注册 blockEDR 回调的示例代码片段:
NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(blockEDR, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrintEx(0, 0, "[ProcessBlocker] Failed to set process notify routine. Status: 0x%08X\n", status);
returnstatus;
}
在这段代码中,PsSetCreateProcessNotifyRoutineEx 函数调用试图注册 blockEDR 回调。FALSE 参数表示该回调正在被注册,而非被移除。若注册成功,status 会反映一个成功状态码;若注册失败,status 将包含一个错误码,并打印一条表明失败的调试信息。
为阻止 EDR(终端检测与响应)进程启动,blockEDR 函数会检查 CreateInfo 结构体——它包含被创建进程的细节,包括其映像文件名。它把映像文件名与一份预定义的 EDR 进程名列表(edrNames)进行比对。一旦匹配命中,就把 CreateInfo->CreationStatus 设置为 STATUS_ACCESS_DENIED,从而阻断该进程的创建。这有效地阻止了指定 EDR 进程的启动。
下面是 blockEDR 函数的一个示例实现:
// Process creation notify routine
voidblockEDR(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo){
UNREFERENCED_PARAMETER(Process);
UNREFERENCED_PARAMETER(ProcessId);
if (CreateInfo) {
for (int i = 0; i < sizeof(edrNames) / sizeof(edrNames[0]); ++i) {
if (wcsstr(CreateInfo->ImageFileName->Buffer, edrNames[i]) != NULL) {
DbgPrintEx(0, 0, "[ProcessBlocker] Blocking %ws process creation\n", edrNames[i]);
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
break;
}
}
}
}
操作前后的进程创建内核通知回调数组
加载 prevent.sys 之前
在这个已签名的(「这不是本文主题,Altered Security 随后会讲如何拿到证书」)rootkit prevent.sys 被加载之前,PspCreateProcessNotifyRoutine 数组包含一列由各安全驱动注册的回调。在图中可以看到来自 WdFilter.sys(Windows Defender)、mssecflt.sys(Microsoft Security)等驱动的条目。这些驱动是系统 EDR 基础设施的一部分,它们监控进程创建事件以检测并缓解潜在威胁。通过注册自己的回调,这些驱动可以检查新创建进程的细节、分析其行为,并采取适当行动来防止恶意活动。用 Process Hacker 搜索关键词 defender,可以看到 MsSense.exe 等关键 EDR 进程及相关服务和其他进程都在正常运行——表明安全态势良好。
图:加载 prevent.sys 之前
加载 prevent.sys 之后
当 rootkit prevent.sys 在启动时(boot time)被加载后,情形发生了戏剧性变化。如第二张图所示,prevent.sys 把它的 blockEDR 回调注册进了 PspCreateProcessNotifyRoutine 数组。这个新条目允许 rootkit 拦截进程创建事件,并按自己的逻辑阻止特定 EDR 进程启动。其影响在 Process Hacker 输出中一览无余——再次搜索关键词 defender,会发现 MsSense.exe 等关键 EDR 进程及相关服务和其他进程已经不再运行。通过阻断这些进程的创建, blockEDR 函数有效地禁用了系统安全基础设施的关键组件。这个演示凸显了 rootkit 的潜在危险性,以及对内核级结构施加严格防护的必要性——以确保安全机制在面对复杂威胁时仍然有效。
图:加载 prevent.sys 之后
上述使用 PsSetCreateProcessNotifyRoutine 函数注册一个在启动时阻止 EDR 进程启动的回调例程的演示,揭示了 Windows 安全架构中一个重大威胁。借助 prevent.sys 这类已签名 rootkit,攻击者可以注册一个自定义回调例程(blockEDR)来拦截进程创建事件,并阻断指定 EDR 进程的创建。这有效地禁用了系统的实时检测与响应能力——因为关键安全进程被阻止启动。下面这张来自 Microsoft Defender for Endpoint(MDE)的图清楚地表明,没有任何事件或告警被检测到,证实该 rootkit 已成功逃避检测。活动安全进程的缺失与 MDE 界面中告警的缺席,共同强调了保护内核级结构、确保安全机制完整性的迫切需求,以防这类复杂攻击。这一场景凸显了稳健安全实践的迫切必要性——包括严格的代码签名策略、安全启动(Secure Boot)机制,以及保持警惕的监控——以抵御旨在瓦解系统防御的高级威胁。
图:MDE 侧无任何告警
演示
译者注:原文此处嵌入了一段演示视频,文字版未包含该视频内容。视频展示的就是上文「加载 prevent.sys 前后」的对比效果——加载后 EDR 关键进程不再启动、MDE 无告警。
致谢
感谢阅读本文。
发布者:Altered Security 安全研究员。
译者补充:几点解读与延伸
这篇文章的标题点睛——「当猎手沦为猎物」。它说的不是攻击者用什么全新技术,而是 用 EDR 自己赖以工作的同一套内核机制,反过来把 EDR 闷死在襁褓里 。下面几点值得展开。
1. 以彼之道:同一个 API,一正一反。 EDR / AV / Sysmon 监控进程创建,靠的正是 PsSetCreateProcessNotifyRoutineEx 注册回调;攻击者注册的 blockEDR 用的也是同一个 API。区别在于回调的「时机」与「动作」:PsSetCreateProcessNotifyRoutineEx 提供的是 创建前(pre-creation) 通知——此时进程尚未真正落地,回调可以通过把 CreateInfo->CreationStatus 置为 STATUS_ACCESS_DENIED 让创建直接失败。而旧版 PsSetCreateProcessNotifyRoutine 是创建后通知,无法阻断。这正是本文选用 Ex 版本的原因。
2. 0xFFFFFFFFFFFFFFF8 是什么。PspCreateProcessNotifyRoutine 数组里存的并非裸函数指针,而是 EX_CALLBACK_ROUTINE_BLOCK 结构,且低 3 位被借用来编码标志位(如「该槽是否在用」)。与 0xFFFFFFFFFFFFFFF8(即 ~0x7)按位与,就是清掉这 3 个标志位、对齐出真实的回调函数地址——这是内核回调逆向的标准一步。本仓库「深入解析 Windows 遥测」一篇对此机制有更系统的讲解,可对照阅读。
3. 整个手法最大的硬门槛:一个已签名内核驱动。 文章反复强调 prevent.sys 是「已签名的」,并明确说「如何拿到证书下篇再讲」——这不是客套,而是关键前提。现代 Windows 正是靠多层机制把这道门焊死的:驱动签名强制(DSE)、内核模式代码签名(KMCS)、Secure Boot、以及 VBS/HVCI(基于虚拟化的代码完整性)。没有合法签名,这个 rootkit 根本加载不进内核,后面注册回调也就无从谈起。换言之, 这篇 PoC 攻击的不是某个漏洞,而是「一旦你拿到了签名驱动能进内核」之后能做什么 。
4. 还有一类「直接 patch 回调」的手法,本文没展开。 作者特意说「patch 或移除这些条目的细节超出本文范围」——那是另一条技术路线:不去注册新回调,而是 直接篡改已有 EDR 回调(如把 WdFilter!MpCreateProcessNotifyRoutineEx 的入口 patch 成立即返回) ,让 EDR 的回调「存在但失能」。两者都依赖先进入内核,但「注册新回调」相对干净(不易触发 PatchGuard),「patch 既有回调」更激进、但易被内核完整性校验抓到。
5. 防御侧:被动与主动。 被动层面,PatchGuard(KPP)会周期性校验内核代码与关键结构、HVCI 阻止内核代码被篡改、Microsoft 易受攻击驱动黑名单(Vulnerable Driver Blocklist)则拦掉一批被滥用的合法签名驱动。主动层面,可以监控 PspCreateProcessNotifyRoutine 数组是否出现非预期的回调新增、监控可疑驱动加载、以及关注 EDR 传感器进程本身的存活。本文结尾点名「严格的代码签名策略 + Secure Boot + 持续监控」正是这个意思。
6. 关于「MDE 无告警」的一个现实提醒。 文章用 MDE 界面无告警来佐证「成功逃避检测」——这在 PoC 演示里成立,但真实环境未必如此干净:MDE 有传感器健康监测(sensor health monitoring),当 MsSense.exe 等传感器进程长期消失,MDE 后台与 Defender 门户通常会标记该终端为「传感器降级 / 离线」,反而可能触发另一类告警或被 SOC 注意到。所以这套手法更准确的定位是「让端点本地检测失效」,而非「完全隐身」。
一句话总结: 这是一篇用最小代码量演示「内核回调机制可被双向利用」的文章——防御者用它看见一切,拿到内核的攻击者用它让防御者睁眼瞎。技术本身不新,但它把「内核态权限」的价值和「驱动签名链」的重要性讲得很直观。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:赛博生存指南 Saad Ahla Saad Ahla《当猎手沦为猎物:用自定义回调禁用 EDR》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论