ntkrnlmp/svmtrace
GitHub: ntkrnlmp/svmtrace
基于 AMD SVM 硬件虚拟化技术的实验性 AMD64 逐指令执行追踪器,提供 C++ API、CLI 和离线解码器以捕获和分析指令边界的完整 CPU 状态。
Stars: 0 | Forks: 0
# SvmTrace
[](https://github.com/ntkrnlmp/svmtrace/actions/workflows/ci.yml)





SvmTrace 是一个实验性的 AMD64 执行追踪器,能够逐条指令地观察目标。
它使用 AMD SVM 执行门来启动会话,设置 guest 的 Trap Flag,将产生的 debug 异常作为 VM exits 进行拦截,并重构包含丰富寄存器信息的 instruction-boundary 记录。
这并非声称其他追踪器无法进行单步执行或收集寄存器。不同寻常之处在于此处实现的完整组合,特别是在 AMD SVM Windows 研究路径上。源码使这种区别变得具体化:VM-exit 处理程序处理退出码 `0x41`,验证 `DR6.BS`,记录 guest 状态,并在恢复 guest 之前再次设置 Trap Flag。
## 目录
- [SvmTrace 的不同之处](#why-svmtrace-is-different)
- [单步指令的意义](#what-one-instruction-step-means)
- [快速开始](#quick-start)
- [具体合成演练](#concrete-synthetic-walkthrough)
- [C++ API](#c-api)
- [架构](#architecture)
- [捕获生命周期](#capture-lifecycle)
- [Trace 格式](#trace-format)
- [SvmTrace 与仅分支追踪](#svmtrace-and-branch-only-tracing)
- [构建和测试](#build-and-test)
- [驱动工作流](#driver-workflow)
- [验证](#validation)
- [项目状态](#project-status)
- [出处](#provenance)
## SvmTrace 的不同之处
大多数追踪可视化从边缘开始:发生了一个分支,一个调用到达了目的地,或者执行返回。SvmTrace 则从连续 instruction boundaries 处的 CPU 状态开始。
```
flowchart LR
subgraph Branch[Branch-oriented view]
B1[Source address] --> B2[Branch event]
B2 --> B3[Destination address]
end
subgraph SVM[SvmTrace view]
S1[Execute gate NPF] --> S2[Set guest TF]
S2 --> S3[Execute one instruction]
S3 --> S4[Intercept #DB VM exit]
S4 --> S5[Capture RIP, RFLAGS, CR3, GPRs]
S5 --> S6[Set TF and resume]
S6 --> S3
end
```
| 问题 | 仅分支事件流 | 当前 SvmTrace 记录模型 |
|---|---|---|
| 控制流去了哪里? | 控制流事件前后的源和目标 | 每个捕获指令边界处的 RIP |
| 指令改变了什么? | 需要从其他数据重构 | 连续的记录揭示了 GPR 和选定的 RFLAGS 变化 |
| 哪个地址空间处于活动状态? | 取决于生产者 | CR3 是重构状态的一部分 |
| 可以跟踪直线的代码吗? | 分支之间不需要事件 | 每次拦截到的单步 debug 退出都可以产生一条记录 |
| 状态如何存储? | 特定于生产者的数据包 | 每 CPU v5 流中的完整锚点加上紧凑增量 |
实时捕获路径为每个接受的原始槽盖上以下印记:
- instruction-boundary RIP;
- RFLAGS 和 CR3;
- RAX、RBX、RCX、RDX、RSI、RDI、RBP、RSP 以及 R8 到 R15;
- 原始状态的全局序列值和 TSC;
- 当 gate 后的第一个 debug 退出到达时的 session-start 标记。
v5 线上格式和 `TraceReader` 还对 16 个 YMM 寄存器和 `xstate_bv` 字段进行了建模。当前的 VM-exit 热路径故意将这些字段清零,因此来自此源代码版本的实时捕获提供了 GPR 丰富的状态,但没有实时的 YMM 值。这种区别在 `driver/svmtrace_driver.c` 中直接可见。
## 单步指令的意义
debug 异常在处理器执行指令**之后**发生。因此,捕获的 RIP 指向下一条指令,而寄存器包含刚完成的指令所产生的状态。
```
sequenceDiagram
participant Guest as Target thread
participant CPU as AMD CPU
participant VMM as SvmTrace VM-exit handler
participant Ring as Per-CPU raw ring
VMM->>CPU: Set guest RFLAGS.TF
VMM->>Guest: Resume
Guest->>CPU: Execute one instruction
CPU->>VMM: #DB VM exit, exit code 0x41
VMM->>VMM: Require DR6.BS and WeSetTf
VMM->>Ring: Store resulting RIP, RFLAGS, CR3, and GPRs
VMM->>CPU: Set guest RFLAGS.TF again
```
这是连续的单步观察,而不是交互式调试器命令。公共客户端可以武装、查询、停止和关闭捕获,但它不暴露步进、暂停或编辑寄存器的命令。
处理程序还检查 debug 退出是否属于 SvmTrace。它同时要求 DR6 中的单步状态位和显示 SvmTrace 设置了 Trap Flag 的每 CPU 标记。内核模式的 RIP 会清除 Trap Flag,而稍后当执行返回到被跟踪的用户模式代码时,后续跟踪的执行错误可以重新启用步进。
## 快速开始
默认构建包含 C++20 库、CLI 和合成解析器测试。它不会构建或加载驱动程序。
```
git clone https://github.com/ntkrnlmp/svmtrace.git
cd svmtrace
cmake -S . -B build -A x64
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
```
检查一个每 CPU v5 流:
```
build\Release\svmtrace.exe inspect C:\svmtrace_trace_cpu0.bin
```
将解码记录转储为 CSV:
```
build\Release\svmtrace.exe dump C:\svmtrace_trace_cpu0.bin
build\Release\svmtrace.exe dump C:\svmtrace_trace_cpu0.bin 100
```
可选的最后一个参数会在输出指定数量的解码行后停止输出。CLI 会打印以下 schema:
```
cpu,sequence,kind,rip,rflags,cr3,reason
```
解码器在读取帧时访问记录,因此调用者不需要将整个 trace 加载到内存中。
## 具体合成演练
假设目标入口页是执行门,RFLAGS 初始为 `0x202`,并且目标以以下指令开始:
```
0x0000000140001000 mov eax, 7
0x0000000140001005 add eax, 1
0x0000000140001008 test eax, eax
```
gate 页首先引起一个 nested page fault。SvmTrace 清除 gate 的 NX 位,启动一个 trace 会话,并设置 Trap Flag。接下来的两次拦截到的 debug 退出可以这样解释:
| 完成的指令 | `#DB` 报告的 RIP | 生成的 RAX | 生成的 RFLAGS | 记录类型 |
|---|---:|---:|---:|---|
| 位于 `0x140001000` 的 `mov eax, 7` | `0x140001005` | `0x7` | `0x202` | `session_start` |
| 位于 `0x140001005` 的 `add eax, 1` | `0x140001008` | `0x8` | `0x202` | `instruction` |
该合成序列的示例 CLI 输出:
```
cpu,sequence,kind,rip,rflags,cr3,reason
3,10,session_start,0x140001005,0x202,0x12345000,0
3,11,instruction,0x140001008,0x202,0x12345000,0
```
关键点在第一行可见:`RIP=0x140001005` 描述了 `mov` 之后的边界,尽管 CLI 的紧凑 CSV 视图没有打印每个 GPR,但通过 C++ 记录仍然可以获取 `RAX=7`。
RIP 0x140001005
RAX 7] -->|full 704-byte record| B[Anchor baseline] B --> C[Next state
RIP +3
RAX +1] C -->|RIP varint + GPR mask + RAX delta| D[Compact delta] D --> E[TraceReader reconstructs
RIP 0x140001008
RAX 8] ``` 编码器将每个原始状态与前一个状态进行比较。它在 16 位掩码后写入更改的 GPR,将附近的 RIP 和 GPR 更改表示为有符号变长整数,并定期再次发射完整的锚点。
## C++ API
`TraceReader` 为每个重构的记录调用一次访问者。返回 `false` 可提前停止。
```
#include
#include
#include
#include
int main() {
const svmtrace::TraceReader trace("svmtrace_trace_cpu0.bin");
std::optional previous;
const auto summary = trace.read([&](const svmtrace::Record& record) {
if (previous && previous->gpr[0] != record.state.gpr[0]) {
std::printf("seq=%" PRIu64 " rip=0x%" PRIx64
" rax: 0x%" PRIx64 " -> 0x%" PRIx64 "\n",
record.sequence,
record.state.rip,
previous->gpr[0],
record.state.gpr[0]);
}
previous = record.state;
return true;
});
std::printf("decoded %" PRIu64 " instruction records\n",
summary.instructions);
}
```
公共模型是类型化的,独立于内核驱动程序:
```
svmtrace::Record
.kind // instruction, session_start, or session_end
.cpu // logical processor that owns this stream
.sequence // reconstructed record order
.tsc // present on anchors and session-end records
.reason // gate id, exit reason, or session-end reason
.state.rip
.state.rflags
.state.cr3
.state.gpr[16]
.state.ymm[16][4] // represented by v5; current capture path writes zeros
.state.xstate_bv // represented by v5; current capture path writes zero
```
驱动控制是一个独立的 API:
```
#include
svmtrace::DriverClient driver;
driver.connect();
driver.set_target(pid);
driver.arm();
const auto status = driver.status();
driver.stop();
```
因此,分析应用程序可以使用 `TraceReader` 而无需连接到 `\\.\svmtrace` 设备。
## 架构
```
flowchart LR
A[Target entry page] -->|execute NPF| B[SVM VM-exit handler]
B -->|clear NX and set TF| C[Target instruction]
C -->|intercepted #DB| B
B -->|full raw state| D[Per-CPU ring]
D --> E[Per-CPU worker]
E --> F[Anchor and delta encoder]
F --> G[LZ4 or raw frames]
G --> H[(Per-CPU v5 file)]
H --> I[TraceReader]
I --> J[C++ visitor]
I --> K[inspect]
I --> L[dump]
```
| 层 | 源码 | 职责 |
|---|---|---|
| SVM 捕获 | `driver/svmtrace_driver.c`, `driver/vmexit.asm` | 配置 VMCB 和 NPT,处理执行错误,拦截 debug 退出,捕获 guest 状态 |
| 每 CPU 缓冲 | `driver/svmtrace_driver.c` | 将编码、压缩和文件 I/O 排除在 VM-exit 热路径之外 |
| v5 编码 | `driver/svmtrace_driver.c` | 将完整的原始槽转换为锚点和增量,然后写入分帧的每 CPU 文件 |
| 解码器 | `src/trace.cpp`, `src/lz4_block.cpp` | 验证帧,解码 LZ4 块,并重构类型化记录 |
| CLI 和客户端 | `cli/main.cpp`, `src/client.cpp` | 检查流并发出显式的驱动程序 IOCTL |
捕获和分析这两部分共享的是文件契约,而不是运行时依赖。这使得在未加载实验性驱动程序时,离线解码仍然可用。
## 捕获生命周期
独立的 gate 定义包含一个目标 gate。在 `arm` 期间,驱动程序读取选定进程的 PE `AddressOfEntryPoint`,将该页解析为 guest 物理地址,并标记相应的 nested page-table 条目为 NX。
```
sequenceDiagram
participant CLI as svmtrace CLI
participant Driver as SVM driver
participant NPT as Nested page table
participant CPU as Target CPU
participant Worker as Per-CPU worker
CLI->>Driver: set-target PID
Driver->>Driver: resolve image and PE entry point
CLI->>Driver: arm
Driver->>NPT: mark entry page NX
Driver->>CPU: request translation flush
CPU->>Driver: execute NPF at entry page
Driver->>NPT: clear NX on gate page
Driver->>CPU: set guest RFLAGS.TF
loop while the target session remains active
CPU->>Driver: intercepted single-step #DB
Driver->>Driver: verify DR6.BS and WeSetTf
Driver->>Worker: publish full state to per-CPU ring
Driver->>CPU: set RFLAGS.TF and resume
end
Worker->>Worker: encode anchors and deltas
Worker->>Worker: write raw or LZ4 frames
```
VM-exit 汇编代码切换到专用的 host 栈,并在 C 处理程序周围使用 VMSAVE 和 VMLOAD。C 热路径将状态复制到固定的原始槽中。稍后,一个 worker 会执行增量编码、LZ4 压缩和文件 I/O。
## Trace 格式
每个逻辑处理器写入 `C:\svmtrace_trace_cpu{N}.bin`。`TraceReader` 识别版本 5,这是一个分帧的小端流。
```
+-------------------------------+
| 16-byte file header |
+-------------------------------+
| decoded_size : u32 | frame header
| stored_size : u32 |
+-------------------------------+
| raw or LZ4 block payload |
+-------------------------------+
| next frame ... |
+-------------------------------+
```
### 文件头
| 偏移量 | 大小 | 字段 |
|---:|---:|---|
| `0x00` | 4 | Magic bytes `FTRC` |
| `0x04` | 4 | 格式版本 `5` |
| `0x08` | 4 | 逻辑 CPU 标识符 |
| `0x0c` | 4 | 标志,其中 bit 0 标记为分帧主体 |
解码后的帧最大为 64 KiB。当 `decoded_size == stored_size` 时,payload 是原始数据。否则,payload 是一个 LZ4 块,其解码大小必须与声明的大小完全一致。在记录可能跨越帧边界之前,worker 会启动一个新帧。
### 记录流
```
flowchart LR
A[Session start
full state] --> D1[Delta
changed fields] D1 --> D2[Delta
changed fields] D2 --> AN[Anchor
full state] AN --> D3[Delta
changed fields] D3 --> Z[Session end
RIP, TSC, reason] ``` | 记录 | 线上类型 | 解码器结果 | |---|---:|---| | 锚点 | 头部低位中的 `01` | 一条带有完整基线的 `instruction` 记录 | | 会话开始 | 头部低位中的 `10` | 一条带有完整基线和 gate id 的 `session_start` 记录 | | 增量 | 头部低位中的 `00` | 一条重构的 `instruction` 记录 | | 会话结束 | 头部低位中的 `11` | 一条带有最终 RIP 和原因的 `session_end` 记录 | 完整的锚点携带序列、TSC、退出原因、RIP、RFLAGS、CR3、16 个 GPR、v5 YMM 区域和 `xstate_bv`。编码器在会话开始时、重置后的第一个状态时,以及距离上一个锚点已过去 64 条记录时,发射一个完整的锚点。 增量头部位宣告记录是否包含更改的 GPR、RFLAGS、YMM 寄存器或完整的 RIP。否则,RIP 和 GPR 更改使用有符号变长整数。`TraceReader` 将每个增量应用于前一个状态,并递增序列值。 ### 防御性解码 解码器在将记录公开给访问者之前会检查数据: - 文件 magic bytes、版本和分帧主体标志; - 完整的帧头和 payload; - 解码和存储的帧大小; - 精确的 LZ4 输出大小、偏移量和匹配长度; - 完整的固定记录和变长整数; - 增量掩码选择的每个字段的可用性; - 增量必须跟在已初始化的锚点之后的要求。 ## SvmTrace 与仅分支追踪 SvmTrace 不是 branch-packet 解码器。它的源码实现了一条刻意不同的观察路径。 | 属性 | SvmTrace 实现 | 仅分支模型 | |---|---|---| | 会话开始后的触发器 | Trap Flag 在一条指令后产生 debug 异常 | 控制流事件产生数据包或事件 | | 直线指令 | 通过连续的 debug 退出可见 | 它们之间没有分支事件发生 | | 此代码库中的状态 | 接受的实时槽中的 RIP、RFLAGS、CR3 和 16 个 GPR | 状态必须来自其他来源或被推断出来 | | 存储 | 完整锚点加上已更改字段的增量 | 面向控制流的编码 | | 运行时影响 | 每个拦截到的步进都会进入 VM-exit 处理程序 | 取决于分支追踪机制 | | 预期用途 | 状态转换至关重要的狭窄研究会话 | 更广泛的重构控制流 | ``` flowchart TB Q{What must the trace answer?} Q -->|Which edge executed?| B[Branch-oriented trace] Q -->|What state followed each instruction?| S[SvmTrace] S --> R[Compare consecutive GPR and flag values] S --> C[Follow straight-line execution] S --> A[Associate state with CR3 and sequence] ``` 此处不进行性能比较。该代码库不包含可以支持性能比较的硬件捕获记录或基准测试工具。 ## 构建和测试 ### 用户模式组件 要求: - Windows 10 或 Windows 11 - Visual Studio 2022 及 Desktop C++ 工具 - CMake 3.22 或更高版本 ``` cmake -S . -B build -A x64 cmake --build build --config Release ctest --test-dir build -C Release --output-on-failure ``` 默认配置构建: | 目标 | 输出 | 目的|---|---|---| | `svmtrace` | 由 CMake 选择的静态库或导入库 | Reader、LZ4 解码器和驱动程序客户端 | | `svmtrace_cli` | `svmtrace.exe` | `inspect`、`dump` 和 `driver` 命令 | | `svmtrace_tests` | 测试可执行文件 | 合成的原始和 LZ4 v5 解析器检查 | ### 实验性驱动程序 可选的驱动程序构建需要匹配的 Windows Driver Kit: ``` cmake -S . -B build-driver -A x64 ` -DSVMTRACE_BUILD_DRIVER=ON ` -DSVMTRACE_WDK_VERSION=10.0.26100.0 cmake --build build-driver --config Release ``` 该代码库不提供驱动程序二进制文件或加载程序。请在隔离的机器上使用正常的 Windows 签名或测试签名驱动程序路径。 ## 驱动工作流 在安装并启动驱动程序后,从提权的 shell 中执行: ``` svmtrace driver set-target 1234 svmtrace driver arm svmtrace driver status svmtrace driver stop svmtrace driver shutdown ``` `status` 报告: ``` bootstrap: complete|pending watchdog: clear|triggered armed: yes|no active CPUs:
target PID:
target image: ..
NX pages:
NPF exits:
debug exits:
```
状态字段直接映射到 `DriverStatus` 和驱动程序的 `SVMTRACE_STATUS_RESPONSE`。
## 验证
签入的测试使用合成文件,这使得它们的证据精确且可重复,而不意味着 CI 执行了 AMD SVM 硬件测试。
| 检查 | 此代码库中的证据 |
|---|---|
| 库、CLI 和测试在 Windows 上编译 | `.github/workflows/ci.yml` 配置并构建默认的 CMake 目标 |
| 原始分帧 v5 解码 | `parser_round_trip(false)` 构建并读取合成的原始帧 |
| LZ4 分帧 v5 解码 | `parser_round_trip(true)` 将相同的测试装置包装在一个仅包含字面量的 LZ4 块中 |
| 锚点重构 | 测试断言 CPU id、初始 RIP 和初始 RAX |
| 增量重构 | 测试断言序列、RIP 增量、RAX 增量和 RFLAGS 更改 |
| 会话结束解码 | 测试断言记录类型和结束原因 |
| 无效 magic bytes 被拒绝 | `bad_magic_is_rejected()` 验证 `TraceError` |
| 实时 SVM 捕获 | 未由当前 CI 工作流执行 |
在本地运行相同的验证:
```
ctest --test-dir build -C Release --output-on-failure
```
本 README 中的图表和演练是对源码和合成记录语义的解释性渲染。没有将伪造的硬件屏幕截图或捕获结果作为验证展示。
## 项目状态
SvmTrace 是一个研究原型,而不是生产级的追踪工具。默认构建执行离线 reader 和 CLI。驱动程序编译是可选的,实时捕获需要在合适的 AMD 硬件上进行独立测试。
每个 CPU 写入一个独立的流。`TraceReader` 保留每 CPU 的顺序,但不合并流或标准化 TSC 值。当前的实时热路径将 v5 YMM 和 `xstate_bv` 字段置零。Ring 的背压会造成序列间隙,而单步 VM exits 会实质性地干扰目标执行。
在解释 trace 时,这些属性很重要。成功的构建和合成的解析器测试证明了签入的用户模式组件在 v5 文件契约上达成了一致。它们并不能证明在特定处理器、固件版本或 Windows 构建版本上的实时捕获是正确的。
## 出处
AMD SVM 驱动程序最初是私有研究工作区中一个未提交的原型,于 2026 年 7 月 21 日提取出来。源代码目录没有提交历史记录,因此无法做出更强的出处声明。特定于目标的二进制文件、trace、微码、加载程序代码和相关的未复制研究数据均未被复制。
`driver/sflz4.c` 和 `driver/sflz4.h` 改编自 Nigel Tao 的 SFLZ4 代码。该原型将该代码标识为 Apache License 2.0,但未记录确切的上游版本。此改编接受调用者拥有的哈希表,并使用 Windows 内核内存例程。原始格式文章可在 获取。`src/lz4_block.cpp` 中的 C++ 解码器是已记录的 LZ4 块格式的独立实现。
未添加项目范围的许可证。只有在确认原始驱动程序原型的作者身份和权利之后,才应选择许可证。
相同的状态如何变成锚点和增量
``` flowchart LR A[Session start stateRIP 0x140001005
RAX 7] -->|full 704-byte record| B[Anchor baseline] B --> C[Next state
RIP +3
RAX +1] C -->|RIP varint + GPR mask + RAX delta| D[Compact delta] D --> E[TraceReader reconstructs
RIP 0x140001008
RAX 8] ``` 编码器将每个原始状态与前一个状态进行比较。它在 16 位掩码后写入更改的 GPR,将附近的 RIP 和 GPR 更改表示为有符号变长整数,并定期再次发射完整的锚点。
捕获状态转换
``` stateDiagram-v2 [*] --> Idle Idle --> TargetSelected: set-target PID TargetSelected --> Armed: arm Armed --> Recording: entry-page execute NPF Recording --> Recording: accepted #DB step Recording --> Armed: target CR3 changes Recording --> TargetSelected: stop Armed --> TargetSelected: stop TargetSelected --> Idle: select another target Idle --> [*]: shutdown SVM ```full state] --> D1[Delta
changed fields] D1 --> D2[Delta
changed fields] D2 --> AN[Anchor
full state] AN --> D3[Delta
changed fields] D3 --> Z[Session end
RIP, TSC, reason] ``` | 记录 | 线上类型 | 解码器结果 | |---|---:|---| | 锚点 | 头部低位中的 `01` | 一条带有完整基线的 `instruction` 记录 | | 会话开始 | 头部低位中的 `10` | 一条带有完整基线和 gate id 的 `session_start` 记录 | | 增量 | 头部低位中的 `00` | 一条重构的 `instruction` 记录 | | 会话结束 | 头部低位中的 `11` | 一条带有最终 RIP 和原因的 `session_end` 记录 | 完整的锚点携带序列、TSC、退出原因、RIP、RFLAGS、CR3、16 个 GPR、v5 YMM 区域和 `xstate_bv`。编码器在会话开始时、重置后的第一个状态时,以及距离上一个锚点已过去 64 条记录时,发射一个完整的锚点。 增量头部位宣告记录是否包含更改的 GPR、RFLAGS、YMM 寄存器或完整的 RIP。否则,RIP 和 GPR 更改使用有符号变长整数。`TraceReader` 将每个增量应用于前一个状态,并递增序列值。 ### 防御性解码 解码器在将记录公开给访问者之前会检查数据: - 文件 magic bytes、版本和分帧主体标志; - 完整的帧头和 payload; - 解码和存储的帧大小; - 精确的 LZ4 输出大小、偏移量和匹配长度; - 完整的固定记录和变长整数; - 增量掩码选择的每个字段的可用性; - 增量必须跟在已初始化的锚点之后的要求。 ## SvmTrace 与仅分支追踪 SvmTrace 不是 branch-packet 解码器。它的源码实现了一条刻意不同的观察路径。 | 属性 | SvmTrace 实现 | 仅分支模型 | |---|---|---| | 会话开始后的触发器 | Trap Flag 在一条指令后产生 debug 异常 | 控制流事件产生数据包或事件 | | 直线指令 | 通过连续的 debug 退出可见 | 它们之间没有分支事件发生 | | 此代码库中的状态 | 接受的实时槽中的 RIP、RFLAGS、CR3 和 16 个 GPR | 状态必须来自其他来源或被推断出来 | | 存储 | 完整锚点加上已更改字段的增量 | 面向控制流的编码 | | 运行时影响 | 每个拦截到的步进都会进入 VM-exit 处理程序 | 取决于分支追踪机制 | | 预期用途 | 状态转换至关重要的狭窄研究会话 | 更广泛的重构控制流 | ``` flowchart TB Q{What must the trace answer?} Q -->|Which edge executed?| B[Branch-oriented trace] Q -->|What state followed each instruction?| S[SvmTrace] S --> R[Compare consecutive GPR and flag values] S --> C[Follow straight-line execution] S --> A[Associate state with CR3 and sequence] ``` 此处不进行性能比较。该代码库不包含可以支持性能比较的硬件捕获记录或基准测试工具。 ## 构建和测试 ### 用户模式组件 要求: - Windows 10 或 Windows 11 - Visual Studio 2022 及 Desktop C++ 工具 - CMake 3.22 或更高版本 ``` cmake -S . -B build -A x64 cmake --build build --config Release ctest --test-dir build -C Release --output-on-failure ``` 默认配置构建: | 目标 | 输出 | 目的|---|---|---| | `svmtrace` | 由 CMake 选择的静态库或导入库 | Reader、LZ4 解码器和驱动程序客户端 | | `svmtrace_cli` | `svmtrace.exe` | `inspect`、`dump` 和 `driver` 命令 | | `svmtrace_tests` | 测试可执行文件 | 合成的原始和 LZ4 v5 解析器检查 | ### 实验性驱动程序 可选的驱动程序构建需要匹配的 Windows Driver Kit: ``` cmake -S . -B build-driver -A x64 ` -DSVMTRACE_BUILD_DRIVER=ON ` -DSVMTRACE_WDK_VERSION=10.0.26100.0 cmake --build build-driver --config Release ``` 该代码库不提供驱动程序二进制文件或加载程序。请在隔离的机器上使用正常的 Windows 签名或测试签名驱动程序路径。 ## 驱动工作流 在安装并启动驱动程序后,从提权的 shell 中执行: ``` svmtrace driver set-target 1234 svmtrace driver arm svmtrace driver status svmtrace driver stop svmtrace driver shutdown ``` `status` 报告: ``` bootstrap: complete|pending watchdog: clear|triggered armed: yes|no active CPUs:
标签:AMD SVM, Bash脚本, C++20, 内核驱动开发, 客户端加密, 控制流追踪, 虚拟化技术