Skip to main content
Glama
README.md
# WinDbg / cdb 会话式调试 MCP

一个把系统自带的 **cdb.exe**(Debugging Tools for Windows 里的命令行调试器)暴露为
MCP 工具的服务端。**零三方调试库**——不依赖任何 Python 调试绑定,直接驱动你装 WinDbg
时就已经有的 cdb。

## 设计思想

调试本质上是一个**持久的、有状态的交互会话**,不是一堆孤立命令。因此本 MCP 采用
**token_key 会话生命周期**架构:

```
   open(target)      ──►  token_key        打开 dump / 启动 exe / attach 进程 / 连内核
   run(token_key, 命令)  ──►  output        在会话里执行任意 cdb 命令(上下文跨命令保留)
   interrupt(token_key)                   中断当前命令(阻塞的 g)
   close(token_key)                       关闭会话
   sessions()                             列出活跃会话
```

`run` 下的是**真正的 cdb/WinDbg 命令**(`!analyze`、`kv`、`r`、`~Ns`、`.frame N`、`dv`、
`bp`、`g`、`p`、`!heap`、`!process` ……),会话上下文(符号 / 当前线程 / 栈帧 / 断点 /
当前进程)在同一个 token_key 内跨命令保留——**和坐在 cdb 前操作完全一致**。WinDbg/cdb
的全部命令都可直接用,远比预先封装的几十个结构化工具完整。

## 前置

- **Windows**
- **Debugging Tools for Windows**(提供 `cdb.exe`;装 WinDbg 时一并就有)。本服务会自动在
  常见位置(Windows Kits、Store 版 WinDbg、PATH)查找;找不到时设环境变量
  `WINDBGMCP_CDB` 指向 `cdb.exe` 全路径。
- **Python 3.10+**
- **符号路径**(强烈建议):设环境变量
  `_NT_SYMBOL_PATH=srv*C:\Symbols*https://msdl.microsoft.com/download/symbols`,
  cdb 子进程会继承,否则符号解析多半失败。

## 安装

```bat
cd windbg_mcp
pip install -r requirements.txt      # 只装 mcp SDK
```

## 接入客户端

启动方式统一:`python <本目录>/server.py`。

**Claude Code**

```bat
claude mcp add windbg-mcp -- py -3.13 C:\path\to\windbg_mcp\server.py
```

**手写 `.mcp.json`**

```json
{
  "mcpServers": {
    "windbg-mcp": {
      "command": "py",
      "args": ["-3.13", "C:/path/to/windbg_mcp/server.py"],
      "env": {
        "_NT_SYMBOL_PATH": "srv*C:\\Symbols*https://msdl.microsoft.com/download/symbols"
      }
    }
  }
}
```

改完配置**重启客户端**并批准 `windbg-mcp`。

## 工具总览(44 个)

分两层:**会话生命周期(7)** + **基础命令封装(37)**。封装是便捷,`run` 是统一原始接口。

### 会话生命周期(7)
| 工具 | 作用 |
|---|---|
| `open(target, args?, kind?, initial_commands?)` | 打开会话 → `token_key` |
| `run(token_key, command, timeout_ms?)` | **统一原始接口**:执行任意 cdb 命令 |
| `interrupt(token_key)` | 中断当前命令(阻塞的 g 等) |
| `close(token_key)` | 关闭会话(终止被调试进程) |
| `detach(token_key)` | 分离会话(被调试进程继续运行,cdb qd) |
| `sessions()` | 列出活跃会话(自动清理死会话,含 kind/target) |
| `list_dumps(directory, pattern?, limit?)` | 列出目录下转储文件(不需会话) |

`open` 的 `kind`:`dump` / `launch` / `attach` / `kernel` / `remote` / `auto`(默认,按 target 形态判断)。

### 基础命令封装(37)
封装内部都走会话的 `run`,参数结构化、不用记 cdb 语法。**没有封装的命令仍用 `run` 直接发。**

| 分组 | 工具(对应 cdb 命令) |
|---|---|
| 执行 | `go`(g) · `step(into?)`(p/t) · `step_out`(gu) · `goto(expr)`(g expr) · `trace(count)`(t N) · `analyze`(!analyze -v) |
| 寄存器/内存 | `regs`(r) · `set_reg`(r name=val) · `read_mem`(db) · `write_mem`(eb) · `read_str`(da/du) · `read_ptr`(dps) · `poi`(dps L1) · `disasm`(u $ip) · `mem_info`(!vprot) · `mem_list`(!address) |
| 符号/模块 | `resolve`(?) · `find_symbols`(x) · `addr_to_symbol`(ln) · `modules`(lm) · `module_info`(lm vm) · `get_exports`(x mod!*) |
| 栈/线程/帧/局部 | `stack`(kv) · `threads`(~) · `select_thread`(~Ns) · `frame`(.frame N) · `locals`(dv) · `get_teb`(r $teb) · `get_peb`(r $peb) · `get_handles`(!handle 0 f) |
| 断点 | `bp`(bp) · `hw_bp`(ba) · `breakpoints`(bl) · `enable_bp`(be) · `disable_bp`(bd) · `remove_bp`(bc) |
| 状态捕获 | `capture_state`(手动快照:寄存器 + 调用栈 + 栈顶内存 + 当前 ip 反汇编) |

`regs` / `read_mem` / `resolve` 会把输出解析成结构化字段(`registers` / `hex`+`ascii` / `addr`);其余返回干净文本(提示符已去掉)。

## 典型工作流

### 崩溃 dump 分析(交互式,!analyze 只是起点)
```
1. open("C:/crashes/x.dmp")            -> t
2. analyze(t)                          起点:故障模块 / 异常 / 故障栈
3. threads(t)                          列出所有线程
4. select_thread(t, 3)                 切到故障线程 3
5. stack(t)                            该线程带参数的调用栈
6. frame(t, 2)                         切到第 2 栈帧
7. locals(t)                           当前帧的局部变量
8. regs(t)                             寄存器(结构化 registers)
9. disasm(t, count=8)                  反汇编崩溃点附近
10. run(t, "!heap -stat")              没封装的命令,走统一 run 接口
11. read_mem(t, "@rsp", 64)            读栈内存(结构化 hex/ascii)
12. close(t)
```
第 4 步切线程、第 6 步切栈帧——后续 `stack`/`locals`/`regs` 都作用在切完后的状态上,这才是真实调试。封装之外的命令(如 `!heap`)随时用 `run` 兜底。

### 活进程调试 / 漏洞利用验证
```
1. open("C:/target/vuln.exe", args="input.bin")   -> t
2. bp(t, "vuln!processInput+0x2A")
3. go(t)                            继续;命中断点返回,或超时则 interrupt(t)
4. regs(t)                          看 rip(registers.rip=0x4141...=可控)
5. stack(t)                         看栈
6. step(t)                          单步越过
7. close(t)
```

### attach 到运行中的进程
```
1. open("1234", kind="attach")        -> t
2. run(t, "~"); run(t, "kv"); ...
3. close(t)                           detach 后进程继续运行
```

### 内核 / 驱动调试
```
1. open("net:port=50000,key=1.2.3.4", kind="kernel") -> t
2. run(t, "!process 0 0")
3. run(t, "lm m nt")
4. close(t)
```

## 重要注意事项

- **Windows 专属**:cdb.exe 仅 Windows 可用。
- **`run` 的 timeout 与中断**:`g`、`p`、等待事件类命令会阻塞到事件发生;到 `timeout_ms`
  仍未返回时结果里 `timed_out=true`,调用 `interrupt(token_key)`(发 `CTRL+BREAK`)中断。
- **命令完成判定**:服务通过识别 cdb 提示符(`0:000>` / `kd>` / `lkd>` 等)判断命令结束。
  极少数情况(命令输出含疑似提示符的行)可能提前返回,重发命令即可。
- **cdb 伪寄存器(实测)**:`$ip`(当前指令指针)、`$teb`、`$peb`、`$exentry` 可用;**`$sp` 不存在**——
  读栈指针用 `regs()` 取 esp/rsp,或命令里用 `@esp`/`@rsp`(寄存器引用需 `@` 前缀)。封装已规避
  (`disasm` 用 `$ip`、`capture_state` 用解析出的栈指针)。
- **`mem_list`(!address)**:首次会建立内存映射(输出 'Building memory map...' 进度),耗时且输出
  可能延迟到下一条命令;建议单独调用或会话开始预热一次。`mem_info`(!vprot)轻量、无此问题。
- **编码**:cdb 输出默认按系统首选编码解码(中文系统=gbk/cp936);若乱码设
  `WINDBGMCP_ENCODING=gbk` 或 `utf-8`。
- **cdb 路径**:设 `WINDBGMCP_CDB=<cdb.exe 全路径或其目录>` 可强制指定。
- **日志**:写 stderr;`WINDBGMCP_LOG_LEVEL=DEBUG` 调高 verbosity。
- **安全(.shell 拦截)**:`run` / `initial_commands` 默认拦截 `.shell` / `.pcan`(cdb 可
  借此执行系统命令);确需放行设 `WINDBGMCP_ALLOW_DANGEROUS=1`。
- **会话数上限**:默认最多 8 个并发会话(`open` 超限报错);改 `WINDBGMCP_MAX_SESSIONS`(0=不限)。
  死会话由 `sessions()` 自动清理,server 退出时 `atexit` 关闭全部。
- **并发**:同一 token_key 的命令自动串行化(不会交错);不同 token_key 并发。
- **权限与授权**:能 attach 任意进程、写内存、内核调试,权限极大。仅用于安全研究与授权测试。

## 测试

`tests/selftest.py` 是自测脚本,验证 cdb 交互机制 + 调试流程(不经 MCP 协议,直接驱动
`CdbSession`,同步快速)。换机器 / 升级 cdb / 改 server 后跑一遍即可回归。
**完整说明见 [tests/README.md](tests/README.md)。**

在 `windbg_mcp/` 目录下快速跑:
```bat
py tests/selftest.py --exe "<被调试程序>" --cdb-path "<cdb.exe 或目录>" --symbol-path "<符号路径>"
```
末尾 `PASS=x WARN=y FAIL=z`,FAIL=0 即核心机制可用。

## 声明

仅供安全研究、学习与授权测试。使用者需自行确保对分析对象拥有合法授权。