Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
mora-hwbp — 基于硬件断点(DR0-DR7)的无补丁用户模式挂钩与遥测插桩引擎(AMSI、WLDP 和 ETW PoC)。 | Kitploit
工具/GitHubGitHub/dovughs/mora-hwbp
防御工具IDS/IPS规避调试器学习与教育红队对抗性攻击
GitHubdovughs/mora-hwbp

mora-hwbp

基于硬件断点(DR0-DR7)的无补丁用户模式挂钩与遥测插桩引擎(AMSI、WLDP 和 ETW PoC)。

查看仓库
101天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Mora-HWBP — 通过硬件断点进行 AMSI / WLDP / ETW 遥测挂钩

一个安全研究概念验证(POC),演示了基于硬件断点(CPU 调试寄存器)的函数挂钩,作为传统内存代码修补的替代方案。

目的与范围

本仓库的发布严格用于针对 Windows 内部机制的防御性安全研究、红队/紫队教育、检测工程和学术研究。它演示了攻击者如何滥用处理器调试寄存器来中和用户态安全遥测——同样重要的是,防御者应当监测什么以检测此类技术。作者不对本代码的任何滥用负责。在未经明确授权的系统上使用此技术属违法行为,并且在大多数司法管辖区违反相关计算机欺诈和滥用法律。请勿在你不拥有或没有明确书面许可测试的任何环境中部署此代码。


目录

  1. 概述
  2. 背景 — 为什么使用硬件断点?
  3. 目标安全组件
  4. 架构
  5. 技术深度剖析
    • 5.1 x64 上的硬件断点
    • 5.2 调试寄存器布局(DR0–DR7)
    • 5.3 向量化异常处理程序(VEH)
    • 5.4 各组件的拦截逻辑
    • 5.5 线程管理与钩子持久性
  6. 导出的 API
  7. 构建说明
  8. 注入与使用示例
  9. 检测与缓解(蓝队)
  10. 已知限制
  11. 参考资料

概述

mora_hwbp.c 实现了一个 DLL,一旦被加载/注入到目标进程(例如 PowerShell 主机)中,就会仅通过 CPU 硬件断点来挂钩四个用户态函数,这些断点存储在该进程每个线程的架构调试寄存器(DR0–DR7)中:

进程内的向量化异常处理程序(VEH) 接收调试寄存器引发的 EXCEPTION_SINGLE_STEP(0x80000004)异常,通过重写异常上下文模拟原函数的成功返回路径,然后恢复执行——全程不修改任何一字节的可执行内存。

这使得该技术从攻击和防御两个角度来看都特别有趣:

  • 从攻击角度看,它可以绕过 EDR/HIPS 针对被修改的 .text 节(经典的内联 detours、EAT/IAT 补丁或 Etwp* 桩)的完整性检查。
  • 从防御角度看,硬件断点会留下非常独特的取证痕迹(调试寄存器内容、单步异常密度、VEH 注册、GetThreadContext/SetThreadContext 系统调用模式),可用于检测。

背景 — 为什么使用硬件断点?

传统的用户态挂钩方式——内联 detours(覆盖 5–14 字节)、导入地址表(IAT)挂钩和导出地址表(EAT)挂钩——有一个共同的弱点:它们会修改完整性扫描器和 ETW 可以观察到的内存。

现代 AV/EDR 产品实现了:

  • 内存扫描 / AMSI 扫描,针对 PowerShell 和 .NET CLR 缓冲区;
  • 基于 ETW 的遥测(Microsoft-Windows-PowerShell、.NET ETW、威胁情报提供程序);
  • 内核回调和用户态完整性检查,用于检测 pageguard/guard-page 技巧、VirtualProtect 向 PAGE_EXECUTE_READWRITE 的转换以及节哈希不匹配。

硬件断点可以绕开所有这些:

  1. 它们是 CPU 寄存器,而不是内存——.text 中没有任何可扫描的内容。
  2. 它们通过 Windows API SetThreadContext 以每个线程为基础设置,不会触发完整性扫描器所使用的经典“内存被修改”信号。
  3. 拦截点完全由处理器的异常分发处理,在任何用户态目标函数执行之前,会先经过进程的 VEH 链。

本 POC 探索了该技术针对 AMSI(反恶意软件扫描接口,Antimalware Scan Interface)、WLDP(Windows 锁定策略,Windows Lockdown Policy) 和 ETW(Windows 事件跟踪,Event Tracing for Windows) 的有效性和可检测性——这三者是现代 Windows 安全栈中最广泛依赖的用户态安全原语。


目标安全组件

AMSI — 反恶意软件扫描接口

AMSI 是 Windows 平台集成点,允许应用程序(PowerShell、Office、VBScript、.NET 主机等)向已注册的反恶意软件提供程序请求内容扫描。主要关注两个入口点:

  • AmsiScanBuffer(HANDLE hamsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HANDLE hamsiSession, AMSI_RESULT *pResult)
  • AmsiScanString(HANDLE hamsiContext, LPCWSTR string, LPCWSTR contentName, HANDLE hamsiSession, AMSI_RESULT *pResult)

通过将返回的 AMSI_RESULT 强制为 AMSI_RESULT_CLEAN (0),脚本引擎会认为内容已经过检查且未发现恶意,因此执行会不受阻碍地继续。

WLDP — Windows 锁定策略

WLDP 为 Windows Defender 应用程序控制(WDAC / Device Guard)实现策略评估。WldpIsClassInApprovedList 回答给定的 COM 类(由 GUID 标识)在当前策略下是否被允许。AMSI 在内部咨询 WLDP,以决定某些脚本/内容类是否“受信任”(在批准的列表中)。如果该函数报告该类已获批准,AMSI 可能会跳过对该内容类型的额外审查。

该 DLL 将 isApproved 输出参数(RDX)设置为 TRUE 并返回 S_OK,使被评估的类看起来受信任。

ETW — Windows 事件跟踪

ntdll.dll 中的 EtwEventWrite 是系统上几乎所有 ETW 事件发射的核心用户态接收器。抑制它会对安全监控产生广泛的副作用:

  • PowerShell 管道和脚本块日志记录事件
  • .NET 程序集加载事件(Microsoft-Windows-DotNETRuntime)
  • AMSI 扫描结果遥测
  • 被 EDR 代理消费的威胁情报提供程序事件

该 DLL 只是返回 ERROR_SUCCESS (0),而不执行真正的函数。


架构```

┌──────────────────────────────────────────────────────────────────────────┐ │ Target Process (e.g. powershell.exe) │ │ │ │ ┌──────────────────────────┐ ┌──────────────────────────────────┐│ │ │ mora_hwbp.dll │ │ CPU / Windows ││ │ │ │ │ ││ │ │ DllMain / InstallHook │ │ Thread A Thread B ││ │ │ │ │ │ ┌────────┐ ┌────────┐ ││ │ │ ▼ │ │ │ DR0..3 │ │ DR0..3 │ ││ │ │ Resolve exports │ │ └────────┘ └────────┘ ││ │ │ (amsi/wldp/ntdll) │ │ ││ │ │ │ │ │ #DB (single-step) ││ │ │ ▼ │ │ exception ──► Windows Dispatch ││ │ │ AddVectoredException │ │ │ ││ │ │ Handler(VEH) │ │ ▼ ││ │ │ │ │ │ ┌─────────────────────────────┐ ││ │ │ ▼ │ │ │ VectoredHandler (ours) │ ││ │ │ SetHwbpOnThread(ALL) │ │ │ • match #DB address │ ││ │ │ │ │ │ │ • rewrite context (RIP/RSP)│ ││ │ │ ▼ │ │ │ • spoof return value (RAX) │ ││ │ │ MonitorThread ◄──┐ │ │ │ • continue execution │ ││ │ │ (re-hook every │ │ │ └─────────────────────────────┘ ││ │ │ 500ms) └──────┘ │ ││ │ └──────────────────────────┘ └──────────────────────────────────┘│ └──────────────────────────────────────────────────────────────────────────┘

root@kitploit:~
**高级流程:**

1. DLL 被加载到目标进程中(通过任意注入技术——参见[用法](#injection--usage-example))。
2. 在 `DLL_PROCESS_ATTACH`(或通过导出的 `InstallHook`)时,使用 `GetProcAddress` 解析目标导出函数(可选地通过 `LoadLibraryW` 强制加载模块)。
3. 注册一个**向量化异常处理程序(Vectored Exception Handler)**,使其成为进程中的**第一个**处理程序(`AddVectoredExceptionHandler(1, ...)`)。
4. **当前线程**立即被挂钩,然后通过 `CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)` 枚举进程中**所有现有线程**并进行挂钩。
5. **监控线程**每 500 毫秒唤醒一次,并重新将断点应用到每个线程——包括**新创建的线程**——从而保证挂钩的持久性,即使在线程挂钩之后新产生了线程,或者断点被外部清除。
6. 当任何线程上调用任何被挂钩的函数时,CPU 会引发 `#DB` 单步异常;Windows 将其分发给 VEH,VEH 模拟一次无害返回并继续执行。

### 示例调试输出

![在 Sysinternals DebugView 中捕获的 HWBP Engine 挂钩状态诊断](https://assets.kitploit.com/production/public/readmes/50594/5cf99fadc07272ff598cbb9486ff7803a3234eb6aeaa8202684c27e97bd7e161.png)

*图 1 — 使用 Sysinternals DebugView 捕获的示例诊断输出。每一行报告已解析的目标地址及其对应调试寄存器的实时命中计数器。*

---

## 技术深入剖析

### 5.1 x64 上的硬件断点

在 x86/x64 上,每个 CPU 提供**四个硬件调试地址寄存器**(`DR0`–`DR3`)和一个控制寄存器(`DR7`)。任何线程只要在 `DR0`–`DR3` 中设置了非零断点,当指令指针到达该地址(或数据访问符合所配置的条件)时就会触发故障。状态寄存器 `DR6` 记录是哪个断点被触发。

硬件断点是**上下文相关**的:它们存储在线程的 `CONTEXT` 结构中,并且仅适用于设置它们的那个线程。这就是为什么一个健壮的实现必须将断点设置到进程的**每个线程**上(并持续为新建线程重新应用)。

### 5.2 调试寄存器布局(DR0–DR7)

`DR7` 是一个控制断点启用及其行为的位字段:

| Bits   | Field  | Meaning                                             |
|--------|--------|-----------------------------------------------------|
| `0`    | `L0`   | 本地启用断点 0 (DR0)                 |
| `2`    | `L1`   | 本地启用断点 1 (DR1)                 |
| `4`    | `L2`   | 本地启用断点 2 (DR2)                 |
| `6`    | `L3`   | 本地启用断点 3 (DR3)                 |
| `8`    | `LE`   | 旧版本地启用(为兼容性保留)        |
| `9`    | `GE`   | 旧版全局启用(为兼容性保留)       |
| `16–17`| `R/W0` | BP0 的访问类型(`00` = 指令执行)  |
| `18–19`| `Len0` | BP0 的长度(`00` = 1 字节)                      |
| `20–21`| `R/W1` | BP1 的访问类型(`00` = 指令执行)  |
| `22–23`| `Len1` | BP1 的长度(`00` = 1 字节)                      |
| `24–25`| `R/W2` | BP2 的访问类型(`00` = 指令执行)  |
| `26–27`| `Len2` | BP2 的长度(`00` = 1 字节)                      |
| `28–29`| `R/W3` | BP3 的访问类型(`00` = 指令执行)  |
| `30–31`| `Len3` | BP3 的长度(`00` = 1 字节)                      |

四个断点均配置为**在单字节上执行(指令获取)**,这是函数入口挂钩的合适条件。

### 5.3 向量化异常处理程序(VEH)

当断点触发时,处理器会引发 `#DB` 异常。在 x64 Windows 上,`ntdll` 分发例程会在进入线程的结构化异常处理程序(SEH)链之前,将其路由到进程范围的 **VEH 链**。本项目中的处理程序:

1. **筛选** — 仅处理 `EXCEPTION_SINGLE_STEP`(`0x80000004`);其他所有异常都落入 `EXCEPTION_CONTINUE_SEARCH`。
2. **匹配** — 将 `ExceptionAddress` 与四个已知函数地址进行比较。
3. **重写上下文**:
   - `RIP = *(RSP)` → 通过弹出返回地址“返回”到原始调用者。
   - `RSP += 8` → 模拟 `ret`(x64 单指令展开)。
   - `RAX = 0` → 伪造 `S_OK` / `ERROR_SUCCESS`(成功返回码)。
   - `DR6 &= ~0xF` → 清除断点状态位,以便后续可以重新执行该指令而不会产生虚假状态。
4. **修改输出参数**(参见 [5.4](#54-per-component-interception-logic))。
5. **返回 `EXCEPTION_CONTINUE_EXECUTION`**,它告诉 Windows 使用修改后的上下文重启线程——即,执行在*调用者*处恢复,真正的目标函数**永远不会运行**。

每个拦截点还额外包裹在 SEH `__try/__except` 保护中,从而确保格式错误或意外的堆栈布局不会导致进程崩溃——这是针对恶意/加固目标的一种健壮性考量。

### 5.4 各组件拦截逻辑

**DR0 — `AmsiScanBuffer`**(x64,前 6 个参数位于 `RCX, RDX, R8, R9, [RSP+0x28], [RSP+0x30]`):```
HRESULT AmsiScanBuffer(HANDLE, PVOID, ULONG, LPCWSTR, HANDLE, AMSI_RESULT* pResult);
                                                        [RSP+0x28]  [RSP+0x30]
  • 将 AMSI_RESULT_CLEAN (0) 写入第 6 个参数(pResult,位于 [RSP+0x30])。
  • 在 RAX 中返回 S_OK (0)。

DR1 — AmsiScanString(x64,前 5 个参数位于 RCX, RDX, R8, R9, [RSP+0x28]):``` HRESULT AmsiScanString(HANDLE, LPCWSTR, LPCWSTR, HANDLE, AMSI_RESULT* pResult); [RSP+0x28]

root@kitploit:~
- 将 `AMSI_RESULT_CLEAN (0)` 写入第 5 个参数(`pResult`,位于 `[RSP+0x28]`)。
- 在 `RAX` 中返回 `S_OK (0)`。

**DR2 — `WldpIsClassInApprovedList`**(前 3 个参数位于 `RCX, RDX, R8`):```
HRESULT WldpIsClassInApprovedList(const GUID* classId, PBOOL isApproved, DWORD evalCriteria);
                                         RCX             RDX             R8
  • 向 *isApproved 写入 TRUE(通过 RDX)。
  • 在 RAX 中返回 S_OK (0)。
  • 后果:被评估的内容类被视为已获锁定策略“批准”,并且 AMSI 信任该判定。

DR3 — EtwEventWrite(前 4 个参数在 RCX, RDX, R8, R9 中):``` ULONG EtwEventWrite(HANDLE RegHandle, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData);

root@kitploit:~
- 在 `RAX` 中返回 `ERROR_SUCCESS (0)`,且不触碰任何输出参数。
- 后果:ETW 提供程序不会从被挂钩的进程收到**任何**事件,从而抑制脚本执行、模块加载、进程创建和 AMSI 遥测的日志记录。

### 5.5 线程管理与挂钩持久性

由于调试寄存器是每线程的,引擎必须持续维护这些挂钩:

1. **立即挂钩** — `DllMain`(或 `InstallHook`)通过 `SetHwbpOnThread(GetCurrentThread())` 挂钩调用线程。
2. **全线程扫描** — `HookAllThreads()` 通过 `TH32CS_SNAPTHREAD` 快照枚举进程中的每个线程,挂起每个外部线程(`SuspendThread`),应用断点(`SetHwbpOnThread`),恢复线程并关闭句柄。挂起操作可防止线程在 `GetThreadContext` 与 `SetThreadContext` 之间进行上下文切换中途出错而导致的竞态条件。
3. **持久性监视器** — `MonitorThreadProc` 以 `Sleep(500)` 循环,每 500 毫秒调用一次 `HookAllThreads()`。这会**重新启用任何已被移除的断点**(例如,由外部 `SetThreadContext` 调用、调试工具或线程销毁/创建导致),并**覆盖初始挂钩之后创建的线程**。
4. **同步** — `HookAllThreads` 在 `CRITICAL_SECTION`(`g_HookLock`)下运行,因此监视器线程和初始挂钩例程绝不会交错进行上下文切换。
5. **干净拆除** — `UninstallHook` 停止监视器,清除每个线程上的 `DR0–DR7`,并注销 VEH。

> **明确回答“挂钩持久性”问题:** 是的——如果任何线程上的断点被移除(由另一个代理、调试器或 EDR 移除),监视器线程会**在 500 毫秒内重新应用它们**。此外,DLL 加载后创建的任何线程都会在一个监视周期内被挂钩。要击败这个特定引擎,唯一可靠的方法是在同一时间窗口内终止监视器线程*并*清除 VEH*并*移除寄存器——或者从一开始就使用拒绝 `SetThreadContext` 的反调试手段。

---

## 导出 API

| 导出             | 签名                               | 行为                                                                    |
|-------------------|------------------------------------|-----------------------------------------------------------------------|
| `InstallHook`     | `BOOL WINAPI InstallHook(void)`    | 解析目标,注册 VEH,挂钩所有线程,启动监视器。                          |
| `UninstallHook`   | `BOOL WINAPI UninstallHook(void)`  | 停止监视器,清除所有线程上的断点,移除 VEH。                            |
| `GetStats`        | `void WINAPI GetStats(void)`       | 输出当前挂钩状态(通过 `OutputDebugStringA`)——地址、命中计数器。       |

命中计数器(`g_HaveAmsiBuf`、`g_HaveAmsiStr`、`g_HaveWldp`、`g_HaveEtw`)通过 `InterlockedIncrement` 维护,并在调试输出中公开;这对于在实验室环境中验证拦截是否确实发生非常有用。

请注意,`DllMain` 本身会在 `DLL_PROCESS_ATTACH` 时执行完整的挂钩序列,因此这些导出函数只是为运行时加载/卸载场景提供的可选便利。

---

## 构建说明

**要求:** Windows 10/11 x64、Visual Studio Build Tools(`icx.exe`)、SDK。

编译 DLL(x64):```bat
icx.exe /nologo /O3 /MT /EHsc "mora_hwbp.c" /link /DLL /out:"mora_hwbp.dll" /LIBPATH:"C:\Program Files (x86)\Intel\oneAPI\compiler\latest\lib"

标志说明:

FlagPurpose
/O3最大优化(函数代码,非必需)

结果是 mora_hwbp.dll,可将其加载到目标进程中。


注入与使用示例

必须将 DLL 加载到使用 AMSI/WLDP/ETW 的进程中——PowerShell 主机是典型的测试环境。可以使用任何标准 DLL 注入技术完成加载。一个最小化、自包含的演示,使用反射式/LoadLibrary 注入,可以通过一个小型 C 加载器来执行:```bat rem Run from an x64 developer prompt (example with a generic loader) loader.exe mora_hwbp.dll powershell.exe

root@kitploit:~
或者,要进行手动实验室检查,请使用你偏好的工具进行注入,然后在 PowerShell 中进行验证:```powershell
# 1. Inject mora_hwbp.dll into the PowerShell process (via external tool).
# 2. Verify that classic AMSI test vectors now return clean.
"AmsiTestSample:7e72c3ce-861b-4339-8740-0ac1484c1386"

仅限实验室验证。 使用调试器或 GetStats/OutputDebugString 观察所有四个断点在脚本内容执行时均报告命中。


检测与缓解(蓝队)

此 POC 具有双重用途:使其在攻击方面有效的相同特性,正是防御者应重点搜寻的。

入侵指标(IOCs)

建议的缓解措施

  1. 看门狗/自监控代理 — 在高价值进程上轮询 GetThreadContext(CONTEXT_DEBUG_REGISTERS),并审计任何在已批准的调试器配置文件之外具有非零 DR0–DR3 的线程。
  2. 内核 ETW 审计 — 启用 Microsoft-Windows-Kernel-Process + 线程跟踪,并对针对安全相关进程的 NtGetContextThread/NtSetContextThread 发出警报。
  3. EDR 用户态钩子完整性 — 由于硬件钩子绕过内存检查,请依赖行为检测(EtwEventWrite 之下的 ETW 使用方钩子、内核 ETW、AMSI 使用方重新检查),而不是仅依靠 .text 完整性。
  4. 保护监控器 — 在真正敌对的环境中,将对其他进程的按线程 SetThreadContext 视为明确的高严重性信号。
  5. 端点加固 — 在适用处启用 WDAC(此 POC 明确为获得课程批准而绕过它——不要将 WDAC 视为针对内存工具的独立防御)、Credential Guard 和 LSASS 保护。

已知限制

  • 仅限 x64 — 栈偏移改写假定 x64 调用约定(参数 RCX/RDX/R8/R9,然后是 [RSP+0x20…])。x86 变体需要 [EBP+…] 风格的参数重建。
  • 仅有四个槽位 — x64 架构正好提供四个断点寄存器;仅凭此方法,每个线程无法钩住超过四个函数。
  • 监控器竞争窗口 — 在 Sleep(500) 迭代之间存在一个(故意很小的)窗口;极快的线程创建加上激进的剥离,理论上可能领先监控器数百毫秒。
  • 反调试干扰 — 任何主动监控或清除调试寄存器的组件(真正的调试器、某些沙箱、特定 EDR)都会干扰此技术。
  • 基于 OutputDebugStringA 的状态 — 诊断依赖调试输出通道;在完全剥离/无头环境中,应附加调试器或重定向输出以便实验室观察。
  • 不是内存持久化原语 — 这是一种仅限运行时、进程内的技术。它本身不提供磁盘/注册表持久化、权限提升或跨进程横向移动。其全部目的是对一种拦截原语进行受控研究。

参考

  • Microsoft Learn — 反恶意软件扫描接口 (AMSI)
  • Microsoft Learn — Windows 锁定策略 (WLDP)
  • Microsoft Learn — Windows 事件跟踪 (ETW)
  • Microsoft Learn — CONTEXT 结构与调试寄存器
  • Intel® 64 和 IA-32 架构软件开发人员手册,第 3B 卷 — 调试寄存器(Dr0–Dr7、#DB 异常)

许可证与负责任披露

本项目采用 MIT 许可证授权 — 详见 LICENSE 文件。

本项目仅用于教育和防御性研究目的。如果您是安全厂商、蓝队成员或检测工程师,我们鼓励您使用本仓库的内容来改进针对基于硬件断点的逃逸技术的检测覆盖。如果您在真实环境中发现此技术被滥用,请通过您所在组织的负责任披露流程以及相关厂商/官方渠道进行报告。

风险自负。未经授权使用此技术可能违反适用法律。

下载工具
寄存器挂钩的函数模块用途
DR0AmsiScanBufferamsi.dll中和 AMSI 内容扫描
DR1AmsiScanStringamsi.dll中和 AMSI 字符串扫描
DR2WldpIsClassInApprovedListwldp.dll强制 WLDP 类批准(Device Guard / WDAC)
DR3EtwEventWritentdll.dll抑制 ETW 事件跟踪
/MT
静态 CRT 链接(无运行时 DLL 依赖)
/EHscC++/SEH 异常处理(__try 所需)
/DLL生成带导出表的 DLL
工件可观察特征
GetThreadContext / SetThreadContext 调用在其他进程/线程上的高频调试寄存器上下文切换(内核 ETW:Microsoft-Windows-Kernel-Process/Thread API)。
非零 DR0–DR3任何 CONTEXT_DEBUG_REGISTERS 包含已知调试器工作流之外用户态地址的线程。
DR7 本地启用位(L0–L3)且 R/W = 00在非调试器管理的线程上设置仅执行断点——一个强烈的异常信号。
EXCEPTION_SINGLE_STEP 数量源自进程 VEH 的高频率 #DB 异常(0x80000004)。
首次机会 VEH 注册在 #DB 风暴之前短时间内新增的 VEH(AddVectoredExceptionHandler)。
TH32CS_SNAPTHREAD + SuspendThread/ResumeThread重复的线程枚举+挂起模式(供 500 毫秒监控器使用)。
通过 LoadLibraryW 加载此前未加载的 wldp.dll/amsi.dll目标进程中的异常模块加载。
EtwEventWrite 始终未被调用缺少预期的 ETW 事件(脚本运行期间 PowerShell 操作日志静默)。