EvilBytecode/Ebyte-amsi-patchless-vehhwbp
GitHub: EvilBytecode/Ebyte-amsi-patchless-vehhwbp
利用硬件断点和VEH异常处理器实现无内存修改的AMSI绕过,在函数执行前拦截并强制返回安全结果。
Stars: 45 | Forks: 8
# Ebyte-amsi-patchless-vehhwbp
使用硬件断点和向量化异常处理器(Vectored Exception Handler)实现无补丁 AMSI 绕过,在 `AmsiScanBuffer` 和 `AmsiScanString` 执行前拦截它们。该绕过技术从原始栈帧读取第 5 个参数(AMSI 结果指针),强制返回干净结果,并在不修改内存中 AMSI 代码的情况下返回给调用者。
## 背景
### AMSI 架构
AMSI 通过 `amsi.dll` 导出的 `AmsiScanBuffer` 和 `AmsiScanString` 提供内容扫描功能。
### 硬件断点 (HWBP)
x64 调试寄存器 (DR0-DR7) 提供 CPU 级别的断点功能:
- **DR0-DR3**:断点地址(共 4 个可用)
- **DR6**:调试状态寄存器
- **DR7**:调试控制寄存器(启用断点,设置类型/长度)
对于执行断点(在 DR7 中配置),CPU 会在执行断点地址处的指令**之前**触发 `STATUS_SINGLE_STEP` (NTSTATUS 0x80000004)。
### 向量化异常处理器 (VEH)
通过 `AddVectoredExceptionHandler()` 注册的 VEH 处理器会在 SEH 处理器之前被调用。它们接收包含完整 CPU 上下文(RIP, RSP, RAX 等)的 `EXCEPTION_POINTERS`,并可以修改它以重定向控制流。
## 逆向工程分析
### AmsiScanBuffer 函数序言
入口点反汇编(在 x64dbg 中 Ctrl + G -> AmsiScanbuffer/AmsiScanString)`0x00007FFB30778160`:
```
00007FFB30778160 | 48:895C24 08 | mov qword ptr ss:[rsp+8],rbx
00007FFB30778165 | 48:896C24 10 | mov qword ptr ss:[rsp+10],rbp
00007FFB3077816A | 48:897424 18 | mov qword ptr ss:[rsp+18],rsi
00007FFB3077816F | 57 | push rdi
00007FFB30778170 | 41:56 | push r14
00007FFB30778172 | 41:57 | push r15
00007FFB30778174 | 48:83EC 70 | sub rsp,70
```
### x64 调用约定
在函数入口(序言之前),栈布局如下:
```
[rsp+0x00] = Return address
[rsp+0x08] = Shadow space
[rsp+0x10] = Shadow space
[rsp+0x18] = Shadow space
[rsp+0x20] = 5th parameter (AMSI_RESULT* result pointer)
```
寄存器参数:`RCX` (HAMSICONTEXT), `RDX` (buffer), `R8` (length), `R9` (name)。
**关键发现:** 在序言修改栈之前,结果指针位于 `[rsp+0x20]`。
## 技术实现
### 阶段 1:初始化
1. 解析 `amsi.dll` 并获取 `AmsiScanBuffer`/`AmsiScanString` 地址
2. 通过 `AddVectoredExceptionHandler(1, VehHandler)` 注册 VEH 处理器
3. 使用 `CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD)` 枚举所有线程
**局限性:** 除非实施额外的监控,否则初始化后创建的线程将不会设置断点。
### 阶段 2:硬件断点安装
对于每个现有线程:
1. 使用 `THREAD_GET_CONTEXT | THREAD_SET_CONTEXT` 打开线程
2. 使用 `CONTEXT_DEBUG_REGISTERS` 标志获取上下文
3. 设置 `DR0 = AmsiScanBuffer`, `DR1 = AmsiScanString`
4. 配置 `DR7` 以启用本地执行断点
5. 使用 `SetThreadContext()` 写回上下文
**注意:** 某些 EDR 和受保护进程可能会限制调试寄存器的写入。此技术可能不适用于所有 Windows 版本或进程保护环境。
### 阶段 3:基于异常的拦截
当 AMSI 函数被调用时:
1. 硬件断点触发 → CPU 触发 `STATUS_SINGLE_STEP` (0x80000004)
2. VEH 处理器接收包含完整上下文的异常
3. 验证异常地址与目标函数匹配
4. 从 `[rsp+0x20]`(第 5 个参数)读取结果指针
5. 清零 `AMSI_RESULT` 结构
6. 重定向控制流:
- `RIP = return address` (来自 `[rsp+0x00]`)
- `RSP += 8` (模拟 `RET`)
- `RAX = 0` (成功)
7. 返回 `EXCEPTION_CONTINUE_EXECUTION`
## 攻击流程
```
DLL Load → InitializeThread() → Thread Enumeration → HWBP Installation
↓
Application calls AmsiScanBuffer()
↓
HWBP triggers → STATUS_SINGLE_STEP → VEH Handler
↓
PoisonScanResult() + ModifyReturnFlow()
↓
Execution resumes at return address (RAX=0, result=clean)
```
## PoC
## 参考资料
- [Microsoft x64 调用约定](https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention)
- [硬件调试寄存器](https://wiki.osdev.org/CPU_Registers_x86-64#Debug_Registers)
- [向量化异常处理](https://docs.microsoft.com/en-us/windows/win32/debug/vectored-exception-handling)
- [AMSI 文档](https://docs.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal)
- [CS 无补丁 AMSI 绕过](https://www.crowdstrike.com/en-us/blog/crowdstrike-investigates-threat-of-patchless-amsi-bypass-attacks/)
## 免责声明
本研究仅用于教育和防御性安全目的。请仅在授权的安全测试环境中使用。
**作者:** Evilbytecode
**日期:** 2025 年 6 月
## 参考资料
- [Microsoft x64 调用约定](https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention)
- [硬件调试寄存器](https://wiki.osdev.org/CPU_Registers_x86-64#Debug_Registers)
- [向量化异常处理](https://docs.microsoft.com/en-us/windows/win32/debug/vectored-exception-handling)
- [AMSI 文档](https://docs.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal)
- [CS 无补丁 AMSI 绕过](https://www.crowdstrike.com/en-us/blog/crowdstrike-investigates-threat-of-patchless-amsi-bypass-attacks/)
## 免责声明
本研究仅用于教育和防御性安全目的。请仅在授权的安全测试环境中使用。
**作者:** Evilbytecode
**日期:** 2025 年 6 月标签:AmsiScanBuffer, AMSI绕过, Conpot, DNS 反向解析, EDR对抗, Patchless, TGT, UML, VEH异常处理, Windows安全, x64汇编, 二进制安全, 云资产清单, 免杀技术, 内存操作, 动态分析规避, 威胁检测, 攻防演练, 暴力破解检测, 硬件断点, 端点可见性, 逆向工程