内容
# windbg-mcp
[](https://github.com/glslang/windbg-mcp/actions/workflows/ci.yml)
[](LICENSE)
[](https://coderabbit.ai)
[](https://github.com/glslang/windbg-mcp/releases/latest)
[](https://github.com/glslang/windbg-mcp#requirements)
MCP 服务器,暴露 WinDbg/DbgEng 给 AI 代理(Claude Code,Claude Desktop,Cursor,...)通过标准输入输出。它驱动一个实时的调试引擎,用于用户模式、内核模式、崩溃转储和时间旅行调试(TTD)工作流。
低级别的引擎绑定位于 [`win-kexp`](https://github.com/glslang/win-kexp)(`src/dbgeng.rs`);该 crate 添加了一个专用的引擎线程和 `rmcp` 工具表面。
## 架构
- **`engine.rs`** — DbgEng 需要序列化、单线程访问(`WaitForEvent` 必须在会话所有者线程上运行),因此 `DebugEngine` 被创建在并限于一个 OS 工作线程。异步工具处理程序通过 `mpsc` 通道将闭包封送到它,带有 `oneshot` 回复和每个调用的超时。`catch_unwind` 守护进程将一个操作中的恐慌转换为调用失败,而不是死线程。
- **`server.rs`** — MCP 工具(见下文),使用 `rmcp` 的 `#[tool_router]`/`#[tool_handler]` 构建。
- **`ttd.rs`** — 定位 `TTD.exe` 并启动跟踪记录。
- **`main.rs`** — tokio + 标准输入输出传输。**日志输出到标准错误流**(标准输出是 JSON-RPC 通道)。
## 需求
- Windows x64(主机位数必须与目标匹配)。
- `dbgeng.dll` / `dbghelp.dll` — 现代 Windows 11 上存在于 `System32`(经 `10.0.26100` 验证)。这足以进行实时的用户模式/内核调试和崩溃转储分析。
- **对于崩溃转储 `!analyze`** (以及其他 `!` 扩展命令),引擎需要 WinDbg `winext\` 扩展程序与其二进制文件捆绑在一起 — System32 引擎不提供这些扩展,因此 `!analyze` 将返回 *"No export analyze found"*。请参见下面的 *捆绑 WinDbg 引擎*。
- **对于时间旅行调试(`.run`)重放**,System32 引擎 **不够用** — 它拒绝 `.run` 跟踪(`0x80070057`)。您需要 **WinDbg 引擎** (它捆绑了 TTD 重放组件)加载到二进制文件旁边 — 请参见下面的 *TTD 引擎*。
- `TTD.exe` (独立的时间旅行调试记录器)用于 `record_trace` — 随 WinDbg / TTD 存储包一起提供;将其放在 `PATH` 中。
- 一个可访问的符号服务器(例如 `srv*https://msdl.microsoft.com/download/symbols`)用于符号名称查询,如 `ttd_calls("ucrtbase!_stdio_common_vfprintf")`。离线,基于地址的查询和数据模型仍然有效;符号 **名称** 将无法解析。
- **管理员** 权限用于实时内核调试和 TTD 记录(不是用于重放)。
## 构建或下载
预构建的 Windows x64 二进制文件附在每个 [GitHub 发布](https://github.com/glslang/windbg-mcp/releases) 中,文件名为 `windbg-mcp-vX.Y.Z-windows-x64.zip`(带有 `SHA256SUMS.txt` 以验证下载 — 技能的 `setup.md` 片段为您完成了此操作)— 无需 Rust 工具链。要从源代码构建:
```pwsh
cargo build --release
```
`win-kexp` 自动作为 git 依赖项从 [`glslang/win-kexp`](https://github.com/glslang/win-kexp) 获取 — 无需兄弟目录。
### 捆绑 WinDbg 引擎
需要此操作有两个原因:TTD `.run` 重放(System32 引擎拒绝跟踪 `0x80070057`)和崩溃转储 `!analyze`(位于 `winext\` 扩展程序中,System32 不提供)。`DebugCreate` 绑定到加载程序找到的 `dbgeng.dll`,而应用程序目录在 `System32` 之前被搜索,因此复制的 **WinDbg** 引擎(重放 TTD 跟踪并提供扩展程序)获胜。一次性,从已安装的 WinDbg 存储包:
```pwsh
$wd = (Get-AppxPackage Microsoft.WinDbg).InstallLocation + "\amd64"
$dst = "C:\workspace\windbg-mcp\target\release"
Copy-Item "$wd\dbgeng.dll","$wd\dbghelp.dll","$wd\dbgcore.dll","$wd\dbgmodel.dll",`
"$wd\symsrv.dll","$wd\msdia140.dll" $dst -Force
Copy-Item "$wd\ttd" "$dst\ttd" -Recurse -Force # TTDReplay*.dll, TtdExt.dll, TTDAnalyze.dll, ...
Copy-Item "$wd\winext" "$dst\winext" -Recurse -Force # ext.dll (!analyze), kext.dll, … — 崩溃转储分类
```
- `ttd\` 子目录提供 `@$cursession.TTD` / `@$curprocess.TTD` 数据模型和 `!tt` 时间旅行命令。
- `winext\` 子目录提供 `ext.dll` (导出 `!analyze`)和其他 `!` 扩展程序。`open_dump` 为您运行 `.load ext`,但请注意 **未限定的 `!analyze` 不会解析** 在此最小引擎上 — 使用模块限定的 **`!ext.analyze -v`** 进行崩溃转储分类。没有 `winext\`,`!analyze` 返回 *"No export analyze found"*。
- **`msdia140.dll` 是 PDB 符号所必需的。** 没有它,`dbghelp` 无法解析任何 PDB(`dia error 0x8007007e`)并静默回落到 *导出* 符号 — 这使得 `module!name` 查找(以及 `ttd_calls("ucrtbase!__stdio_common_vfprintf")`)失败,即使正确的 PDB 在缓存中也是如此。`symsrv.dll` 是读取符号存储缓存所必需的。
(`cargo clean` 清除 `target\`,因此重新复制。)实时和转储调试在有或没有 TTD 引擎的情况下都有效;PDB 符号 **名称** 需要 `msdia140.dll` + 符号路径(`execute` → `.sympath srv*C:\ProgramData\Dbg\sym*https://msdl.microsoft.com/download/symbols`)。
## 与 MCP 客户端一起使用
将您的客户端指向构建的二进制文件,例如 Claude Code:
```jsonc
// .mcp.json (或 claude_desktop_config.json 下的 "mcpServers")
{
"mcpServers": {
"windbg": {
"command": "C:\\workspace\\windbg-mcp\\target\\release\\windbg-mcp.exe"
}
}
}
```
### 作为 Claude Code 插件
此存储库也是一个单一插件的 [Claude Code 市场](https://code.claude.com/docs/en/plugin-marketplaces):安装它会注册 `windbg` MCP 服务器 **和** 一个 `windbg-debugging` 技能,该技能知道如何驱动它(设置、崩溃转储、实时/内核和 TTD 操作手册)。
```text
/plugin marketplace add glslang/windbg-mcp
/plugin install windbg-mcp@windbg-mcp
```
插件提供源代码,不提供二进制文件,因此在安装后,您仍然需要将服务器二进制文件放在正确的位置 — 下载预构建的版本或从源代码构建 — 并且(对于 `.run` 重放和崩溃转储 `!analyze`)捆绑 WinDbg 引擎 — 技能的 `setup.md` 指南会指导您完成此操作,它与 [*构建或下载*](#build-or-download) 和 [*捆绑 WinDbg 引擎*](#bundling-the-windbg-engine) 部分相同。然后运行 `/reload-plugins` 以连接服务器。插件指向 `${CLAUDE_PLUGIN_ROOT}/target/release/windbg-mcp.exe`。
### 发布
插件在 [`.claude-plugin/plugin.json`](.claude-plugin/plugin.json) 中设置显式 `version`,因此用户仅在版本更改时才会收到更新 — 单纯的提交不会触发更新。要发布版本,请在 `plugin.json` 和 `Cargo.toml` 中增加 `version`,增加本自述文件顶部的版本徽章,在 [CHANGELOG.md](CHANGELOG.md) 中添加对应条目,并标记提交 `vX.Y.Z`。运行 `claude plugin validate . --strict` 在发布前验证。通过推送标签运行 [`release.yml`](.github/workflows/release.yml),它验证标签与两个清单版本和自述文件徽章的匹配,构建 `windbg-mcp.exe`,并将 zip + SHA256 校验和附加到 GitHub 发布。 zip 还获得了签名 [构建溯源证明](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations),将其与构建它的工作流运行绑定 — 使用以下命令验证:
```pwsh
gh attestation verify <zip> --repo glslang/windbg-mcp `
--signer-workflow glslang/windbg-mcp/.github/workflows/release.yml
```
(`--repo` 参数仅证明证明来自 *某些* 存储库中的工作流;`--signer-workflow` 参数将其固定到发布工作流。)
## 操作指南
- [`docs/crash-dump-walkthrough.md`](docs/crash-dump-walkthrough.md) — 对一个真实的内核 [minidump](docs/samples/052126-34312-01.dmp) 进行分类:一个 `0x9F DRIVER_POWER_STATE_FAILURE` 跟踪到 `nvlddmkm.sys` 通过 `!ext.analyze -v` 和手动设备栈遍历,带有真实的输出和部分 minidump(`0x80040205`)的特别之处。
- [`docs/ttd-walkthrough.md`](docs/ttd-walkthrough.md) — 使用 TTD 工具对 [`xusheng6/TTD_lab`](https://github.com/xusheng6/TTD_lab) 的 `helloworld` 示例进行交互式游览:打开 `.run`,调查事件/线程,导航前后,内存分析,基于符号的 `printf` 调用计数(带有真实输出和特别之处)。它将每个工具映射到实验室的练习。
- [`docs/flareauthenticator-ttd-walkthrough.md`](docs/flareauthenticator-ttd-walkthrough.md) — 一个完整的 **TTD → Z3 求解** 的 obfuscated Qt crackme(Flare-On 12 #8)。击败一个反分析环境保护措施,记录一个错误的猜测,并使用 `ttd_calls`/`ttd_memory`/反向导航剥离控制流扁平化 + 计算调用 + 加密字符串,直到确切的检查:一个每个按键的滚动哈希,最终简化为一个纯加权和。25 个权重来自重放,250 个 `g` 值来自调试器函数评估,Z3 找到一个令人满意的代码 — 它揭示了标志(代码故意不唯一)。可在 [`examples/flareauthenticator/`](examples/flareauthenticator/) 中运行的求解器;记录的终端会话在 [`docs/flareauthenticator.cast`](docs/flareauthenticator.cast) (`asciinema play`)— [渲染为 GIF](docs/flareauthenticator.gif) 在操作指南中。
- [`docs/driver-ioctl-walkthrough.md`](docs/driver-ioctl-walkthrough.md) — 枚举驱动程序的 IOCTL 接口并在实时 KDNET 内核上确定用户模式的可达性:`driver_object`/`uf` 恢复 `\Driver\mountmgr` 调度切换,`decode_ioctl` 访问层,解析内存中的设备 DACL,以及 `ioctl_trace` 扫描 — 结束时生成可达性报告(它对标准用户可以访问的代码进行编码,而 I/O 管理器阻止的代码)。
## 工具
| 分组 | 工具 |
|-------|-------|
| 会话 | `open_dump`, `open_trace`, `attach_kernel_local`, `attach_kernel`, `attach_process`, `launch`, `end_session` |
| 状态 | `registers`, `read_memory`, `backtrace`, `modules`, `threads`, `disassemble`, `dx` |
| 控制 | `go`, `step_over`, `step_into`, `set_breakpoint` |
| TTD 导航 | `step_back` (`t-`), `step_over_back` (`p-`), `reverse_go` (`g-`), `goto_position` (`!tt`) |
| TTD 分析 | `ttd_calls`, `ttd_memory`, `ttd_events`, `index_trace`, `record_trace` |
| 驱动程序 IOCTL | `decode_ioctl`, `driver_object`, `device_object`, `irp_stack`, `ioctl_trace`, `reachable_from_dispatch` |
| 原始 | `execute` — 运行任何调试器命令,返回完整的文本输出 |
正向 (`go`/`step_over`/`step_into`) 和反向 (`reverse_go`/`step_over_back`/`step_back`) 控制工具镜像调试器 UI 的 F9/F8/F7 和 Shift+F9/F8/F7,因此代理可以驱动跟踪两个方向,并在 `goto_position` 处跳转到任意位置。所有这些发出命令 **并将引擎推进到下一个停止点** (一个普通的 `Execute` 只设置运行状态 — 它不移动目标),这使得实时步进和 TTD 正向/反向导航实际上推进。
`ttd_calls`/`ttd_memory`/`ttd_events` 是 TTD 数据模型的便捷包装器:`ttd_calls` 和 `ttd_memory` 查询 `@$cursession.TTD.{Calls,Memory}` (每次调用函数/每次访问地址范围),而 `ttd_events` 查询 `@$curprocess.TTD.Events` (模块/线程/异常时间线)。对于其他操作,`dx` 评估任意数据模型/LINQ 表达式,例如 `@$cursession.TTD.Calls("ntdll!NtCreateFile").Where(c => c.ReturnValue != 0)`。
## 限制和注意事项
- **TTD 仅限用户模式** (微软限制):内核调试和 TTD 是不同的会话类型 — 您无法对内核目标进行时间旅行。
- `launch` 和 `attach_process` 在其初始/断点处停止目标,并带有实时进程/线程上下文。(绑定启用初始断点事件过滤器 — `sxe ibp` — 而裸 `DebugCreate` 主机将其关闭;如果它没有它,目标将自由运行。)注意:在 Windows 11 上 `notepad` 是商店应用,因此通过 `Start-Process notepad` 返回的 PID 可以击中 `0xD000010A`(该 PID 是一个瞬时启动器)— 附加到经典 Win32 进程。
- `read_memory` 仅接受数字/`0x`-十六进制地址;对于寄存器/符号表达式,请使用 `execute` 与 `db`/`dd`(例如 `db @rip`)。
- `reachable_from_dispatch` 是 **静态** 调用图遍历 `uf` 反汇编:它跟随直接调用和跨函数尾跳转,但 **不** 跟随函数指针或未解析的编译器跳转表。所以 `REACHABLE` 判决是健全的(存在一个具体的静态路径,并且报告路径),而 `NOT REACHABLE` 是在探索的边界内尽力而为。如果调度使用 `switch(IoControlCode)` 跳转表(常见),请传递特定的处理程序 VA 作为 `from` 以限制它,或使用断点 + `go` 确认动态操作。
- **崩溃转储分类使用 `!ext.analyze -v`** ,不是 `!analyze` — 捆绑定的引擎仅限于`!ext.analyze`解析模块限定的形式(见 *捆绑 WinDbg 引擎*)。在部分 minidump 上,未捕获的页面读取会引发 `An unexpected exception was raised (0x80040205)` 而不是干净的“内存读取错误”;请查询您需要的特定字段(例如 `dt nt!_DRIVER_OBJECT <addr> DriverName`),而不是转储整个结构。参见 [crash-dump walkthrough](docs/crash-dump-walkthrough.md)。
- 单步跟进仅在目标具有真实线程上下文时有效(在 `go`/步进或断点命中后)。直接在跟踪开始时从 `goto_position` 进行步进会返回 `0x80040205` — 首先运行到断点。
- 符号 **名称** (`module!func`) 需要 (a) 将 `msdia140.dll` 捆绑到二进制文件旁边,(b) 一个指向 PDB 的符号路径 (`execute` → `.sympath srv*C:\ProgramData\Dbg\sym*https://msdl.microsoft.com/download/symbols`),以及 (c) 在 **停止** 位置重新加载(在 `go`/断点之后,而不是直接从 `!tt` 开始),以便模块的 PDB 被匹配和加载。具备这些条件后,例如 `ttd_calls("ucrtbase!__stdio_common_vfprintf")` 将返回确切的调用次数。没有符号时,数据模型、导航和内存读取仍然有效 — 按地址查询。
- **一个调试会话,一个命令一次。** 单个引擎实例在一个专用线程上运行,并串行处理操作。顺序发出工具调用 **并等待每个结果**,然后发送下一个调用;服务器不会对并发的未完成请求进行排序,因此流水线它们(在返回结果之前触发几个调用)可能会运行一个命令,然后建立它的状态(调用在 `open_dump` 之前竞争并失败,带有 `0x80040205`),对于状态依赖的调试器命令来说毫无意义。标准的 MCP 客户端已经序列化调用→结果,因此这仅适用于自定义/批处理调用者。 以 `end_session` 结束会话,然后再打开另一个目标。
- TTD **重放** (`open_trace`) 需要 `TTDReplay.dll` 可发现但 **不** 需要提升;TTD **记录** (`record_trace`) 需要 `TTD.exe` **和** 管理员。`record_trace` 捕获记录器的启动输出到 `<out_dir>\ttd_record.log` 并观察它一会儿,因此快速失败(例如未提升运行 → `0x80070005 Access is denied`)被报告为错误而不是假的“记录已启动”。
- 控制流工具 (`go`/`step*`) 发出相应的调试器命令;对于长时间运行的 `go` 来说,针对实时目标的精确停止/等待语义受每个调用的超时限制。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器