windbg_mcp
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 子进程会继承,否则符号解析多半失败。
安装
cd windbg_mcp
pip install -r requirements.txt # 只装 mcp SDK接入客户端
启动方式统一:python <本目录>/server.py。
Claude Code
claude mcp add windbg-mcp -- py -3.13 C:\path\to\windbg_mcp\server.py手写 .mcp.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)
工具 | 作用 |
| 打开会话 → |
| 统一原始接口:执行任意 cdb 命令 |
| 中断当前命令(阻塞的 g 等) |
| 关闭会话(终止被调试进程) |
| 分离会话(被调试进程继续运行,cdb qd) |
| 列出活跃会话(自动清理死会话,含 kind/target) |
| 列出目录下转储文件(不需会话) |
open 的 kind:dump / launch / attach / kernel / remote / auto(默认,按 target 形态判断)。
基础命令封装(37)
封装内部都走会话的 run,参数结构化、不用记 cdb 语法。没有封装的命令仍用 run 直接发。
分组 | 工具(对应 cdb 命令) |
执行 |
|
寄存器/内存 |
|
符号/模块 |
|
栈/线程/帧/局部 |
|
断点 |
|
状态捕获 |
|
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。
在 windbg_mcp/ 目录下快速跑:
py tests/selftest.py --exe "<被调试程序>" --cdb-path "<cdb.exe 或目录>" --symbol-path "<符号路径>"末尾 PASS=x WARN=y FAIL=z,FAIL=0 即核心机制可用。
声明
仅供安全研究、学习与授权测试。使用者需自行确保对分析对象拥有合法授权。