toneillcodes/UnwindRaven
GitHub: toneillcodes/UnwindRaven
UnwindRaven 是一个 Windows x64 攻击性研究框架,通过构建数据驱动的合成调用栈来绕过基于栈遍历的 EDR 检测与调试器分析。
Stars: 62 | Forks: 5
# UnwindRaven
ctx.Rip =
```
然后在恢复挂起的线程之前对其调用 `SetThreadContext`。
### VEH 清理
一旦 payload 完成并且合成返回链遍历到了真实栈的末尾,就会触发访问违例。安装的 VEH(位于 `src/veh.c` 的 `VehCallback`)检查 `EXCEPTION_ACCESS_VIOLATION` 并将 `RIP` 重定向到 `RtlExitUserThread(0)`,确保线程干净地退出而不会导致宿主进程崩溃。为了避免可检测的导入,`RtlExitUserThread` 是在运行时通过手动遍历 `ntdll.dll` 的导出表(`GetLocalProcAddressManual`)来解析的。
## Blueprint 收集器
`BlueprintCallstack`(`tools/blueprint-callstack.c`)是配套工具,用于**从展现出所需表观调用链的合法 Windows 进程中捕获真实的调用栈 blueprint**。
### 工作原理
1. 使用 `PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` 权限打开目标进程。
2. 挂起目标线程并捕获其 `CONTEXT`。
3. 调用 `StackWalk64` + `SymFromAddr` 枚举栈帧。
4. 对于每个栈帧,执行**手动导出表遍历**(`GetExportRva`)以验证该符号确实被导出 —— 未导出的栈帧将被静默跳过,因为它们无法跨进程边界重构(没有稳定的 RVA)。
5. 将模块路径规范化为标准的 `%SystemRoot%\System32\…` 格式。
6. 以 blueprint 格式为每个导出栈帧输出一行。
### 用法
```
# 从一个进程的任意 thread 进行 Harvest(自动选择第一个 thread)
BlueprintCallstack.exe --pid [--out blueprint.txt]
# 从特定的 thread 进行 Harvest
BlueprintCallstack.exe --thread [--out blueprint.txt]
```
### Blueprint 格式
输出的每一行(以及 UnwindRaven 使用的输入)都遵循这种竖线分隔的模式:
```
||<0xOffset>|
```
| 字段 | 描述 |
|---|---|
| `module_path` | DLL 的标准绝对路径(System32 规范化) |
| `exported_function` | 精确的导出符号名称 |
| `0xOffset` | 距离导出起始处的字节偏移量(十六进制) |
| `needLoad` | `1` = UnwindRaven 必须 `LoadLibrary` 此模块;`0` = 已经存在 |
**Blueprint 片段示例:**
```
C:\Windows\System32\ntdll.dll|NtWaitForSingleObject|0x14|0
C:\Windows\System32\kernel32.dll|WaitForSingleObjectEx|0x8E|0
C:\Windows\System32\kernelbase.dll|WaitForSingleObjectEx|0x4C|0
C:\Windows\System32\kernel32.dll|BaseThreadInitThunk|0x14|0
C:\Windows\System32\ntdll.dll|RtlUserThreadStart|0x21|0
```
## 构建说明
### 前置条件
| 需求 | 版本 / 备注 |
|---|---|
| Visual Studio | 2019 或 2022(Desktop C++ workload) |
| CMake | 3.20 或更高版本 |
| Windows SDK | 10.0.19041.0 或更高版本 |
| Windows Driver Kit (WDK) | 需要 WDK 提供的 `dbghelp.dll`(x64 Debuggers 路径) |
| 符号缓存目录 | `C:\Symbols`(首次运行时自动创建) |
### 克隆
```
git clone https://github.com/toneillcodes/UnwindRaven.git
cd UnwindRaven
```
### 配置与构建
仓库附带了一个便捷脚本:
```
.\build.ps1
```
或者使用 CMake 和 Visual Studio generator 手动执行:
```
cmake -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
```
二进制文件放在 `bin\` 目录中:
```
bin\
UnwindRaven.exe
UnwindRavenHellow.exe
BlueprintCallstack.exe
dbghelp.dll ← copied from WDK at post-build
```
### 关键编译器标志
| 标志 | 用途 |
|---|---|
| `/Oy-` | **禁用省略帧指针** —— 准确遍历栈所需 |
| `/Zi` / `/Z7` | PDB 中的完整调试信息 / 内嵌调试信息 |
| `/FAs` `/FAcs` | 发出带注释的汇编列表以供分析 |
| `/DEBUG` | 链接时调试信息 |
## 目录结构
```
UnwindRaven/
│
├── cmake/ # CMake helper modules
│
├── examples/
│ ├── UnwindRaven.c # Main example: blueprint load → stack spoof → payload run
│ └── UnwindRaven-Hellow.c # Minimal "hello world" loader example
│
├── include/ # Public headers
│ ├── unwindraven_core.h
│ ├── stack_blueprint.h
│ └── synthetic_stack.h
│
├── src/ # Core static library sources
│ ├── common.c # EnableDebugPrivilege, module/image helpers
│ ├── stack_blueprint.c # Blueprint file parsing (LoadBlueprintFile)
│ ├── synthetic_stack.c # Frame resolution, UNWIND_INFO parsing, stack construction
│ ├── pe_loader.c # Manual PE mapper: sections, relocations, IAT, permissions
│ ├── veh.c # VEH installation and AV → RtlExitUserThread redirect
│ └── unwindraven_core.c # High-level orchestration (MapPayloadImage, StartSpoofedLoaderThread)
│
├── tools/
│ └── blueprint-callstack.c # Blueprint Harvester utility
│
├── CMakeLists.txt
├── build.ps1 # PowerShell build helper
└── LICENSE # GPL-3.0
```
## 示例用法
### 1 — 捕获 Blueprint
针对任何展现出你想要模仿的调用链的进程运行 `BlueprintCallstack`。等待用户输入的 `notepad.exe` 是一个很好的例子:
```
# 查找 Notepad 的 PID
$pid = (Get-Process notepad).Id
# Harvest 并保存
.\bin\BlueprintCallstack.exe --pid $pid --out .\blueprints\notepad_wait.txt
```
示例输出:
```
[+] Harvesting blueprint from PID 4812, TID 7640
C:\Windows\System32\win32u.dll|NtUserGetMessage|0x14|0
C:\Windows\System32\user32.dll|GetMessageW|0x4A|0
C:\Windows\System32\kernel32.dll|BaseThreadInitThunk|0x14|0
C:\Windows\System32\ntdll.dll|RtlUserThreadStart|0x21|0
[+] Total frames walked: 7
[+] Exported frames listed: 4
```
### 2 — 运行 UnwindRaven
```
.\bin\UnwindRaven.exe --load-blueprint .\blueprints\notepad_wait.txt .\payload\my_payload.dll
```
```
[!] Blueprint loaded (4 entries).
Press ENTER here to continue and build synthetic stack...
[+] Frame 0: user32.dll!GetMessageW+0x4A (stack size: 0x38)
[+] Frame 1: kernel32.dll!BaseThreadInitThunk+0x14 (stack size: 0x28)
[+] Frame 2: ntdll.dll!RtlUserThreadStart+0x21 (stack size: 0x10)
[!] Payload mapped successfully at 0x00007FF9A3C40000.
Press ENTER to continue and start the spoofed loader thread...
[+] Spoofed loader thread started.
```
### 3 — 验证假栈
将 WinDbg 或 x64dbg 附加到宿主进程,并使用 `k` 检查新创建线程的栈。显示的栈帧将与 blueprint 精确匹配,真实分配的任何痕迹都将不可见。
## 限制
- **仅限 x64。** 合成栈引擎和 UNWIND\_INFO 解析器专门针对 `IMAGE_FILE_MACHINE_AMD64`。不支持 ARM64 或 x86(32 位)。
- **仅限导出栈帧。** Blueprint 条目必须引用导出的符号。非导出函数没有稳定的跨进程 RVA,无法重构。
- **最多 64 个栈帧。** `MAX_STACK_FRAMES` 定义为 64。包含更多条目的 blueprint 将被截断。
- **最大合成栈大小:0x3000 字节。** 累计 `UNWIND_INFO` 栈帧大小超过此限制的 blueprint 将在构建时失败。
- **需要 WDK 的 `dbghelp.dll`。** 构建系统在构建后从 WDK x64 Debuggers 路径复制 `dbghelp.dll`。Windows SDK 版本的 `dbghelp.dll` 不足以支持收集器中的 `StackWalk64`。
- **不支持链式解退。** 使用链式 `UNWIND_INFO` 记录(常见于深度优化的系统 DLL 中)的函数可能会被低估栈大小。
- **ASLR / 模块可用性。** 如果模块布局或导出集不同,在一台机器上捕获的 blueprint 可能无法直接移植到另一机器上。始终在目标机器上重新收集或在使用前验证模块的存在。
- **防病毒 / EDR 检测。** 虽然调用栈伪造可以击败栈检查启发式方法,但其他遥测源(内核 ETW 回调、父进程关系、手动映射 DLL 中的 IAT 异常)仍然处于活跃状态。UnwindRaven 仅解决栈向量。
## 许可证
UnwindRaven 在 **GNU General Public License v3.0** 下发布。
全文请见 [`LICENSE`](LICENSE)。
标签:云资产清单, 内核回调绕过, 安全意识培训, 客户端加密, 端点可见性, 调用栈欺骗, 逆向工程