Windows调试器MCP服务器

一款将Windows调试器功能暴露为MCP工具的服务,支持通过JSON-RPC协议控制用户模式进程、内核会话和崩溃分析。

  • by gengstah
  • Python
  • 本地部署
  • 94 星标
  • 更新于
在 GitHub 查看 gengstah/windbg-mcp

使用说明

Winggy MCP

一个MCP(模型上下文协议)服务器,它公开了每个pybag Windows调试器 作为原生MCP工具。它为任何MCP兼容客户端(Claude Desktop, Claude Code、Cowork、OpenAI Codex CLI、Cursor和自定义代理)完全控制 用户模式进程、内核会话和崩溃转储分析——全部通过类型化 具有结构化JSON响应的工具调用。


需求

  • 仅限Windows --pybag需要Microsoft Windows调试工具
  • Python 3.10+
  • Microsoft Windows调试工具(Windows SDK的一部分)

安装

1.克隆存储库

git clone https://github.com/your-username/windbg-mcp.git
cd windbg-mcp

2.安装Python依赖项

pip install pybag mcp

3.安装Microsoft调试工具

下载Windows SDK并选择 Windows调试工具 在设置过程中: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/


连接到LLM IDE和客户

服务器作为本地stdio进程运行。以下所有客户端都以相同的方式启动它-- python <path-to>/windbg_mcp.py --但每个都有自己的配置格式。


克劳德桌面

编辑Claude Desktop配置文件并添加 windbg-mcp 条目:

配置文件位置:

  • 窗户: %APPDATA%\Claude\claude_desktop_config.json
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "windbg-mcp": {
      "command": "python",
      "args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
    }
  }
}

重新启动克劳德桌面。所有55个调试器工具将自动出现。


克劳德代码(CLI)

运行以下命令一次以注册服务器。Claude Code存储条目 在其自己的MCP配置中,并在每个后续会话中提供这些工具。

claude mcp add windbg-mcp python C:\path\to\windbg-mcp\windbg_mcp.py

要验证服务器是否已注册:

claude mcp list

要稍后删除它,请执行以下操作:

claude mcp remove windbg-mcp

克劳德协作

有两种方法可以将Winge-MCP添加到Cowork中:通过JSON配置(快速)或 通过将其安装为 .mcpb 插件包(可移植、可共享)。

选项A——JSON配置

  1. 打开Claude桌面应用程序并转到 设置→ MCP服务器.
  2. 点击 添加服务器 并粘贴以下内容:
{
  "windbg-mcp": {
    "command": "python",
    "args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
  }
}
  1. 保存并重新启动Cowork。这些工具将在您的下一次会话中可用。

选项B——作为 .mcpb 插件包

A. .mcpb 该文件是Cowork可以安装的插件目录的zip存档 直接。当与团队共享服务器或 跨机器。

第一步——构建 .mcpb 文件

从克隆存储库的根目录运行:

powershell -Command "Compress-Archive -Path '.\*' -DestinationPath 'windbg-mcp.zip'; Rename-Item 'windbg-mcp.zip' 'windbg-mcp.mcpb'"

这创造了 windbg-mcp.mcpb 在当前目录中,捆绑 windbg_mcp.py, manifest.json,以及任何其他项目文件。

第2步——在协作中安装

  1. 打开克劳德桌面应用程序。
  2. 首选 设置→ 插件 (或 扩展).
  3. 点击 安装插件 并选择 windbg-mcp.mcpb.
  4. 合作阅读 manifest.json 从捆绑包中注册MCP服务器,以及 使所有工具立即可用,无需手动配置路径。

这 manifest.json 此仓库中捆绑的已正确配置:

{
  "manifest_version": "0.2",
  "name": "windbg-mcp",
  "version": "1.0.0",
  "description": "WinDbg MCP — full Windows debugger control via MCP tools",
  "server": {
    "type": "python",
    "entry_point": "windbg_mcp.py",
    "mcp_config": {
      "command": "python",
      "args": ["${__dirname}/windbg_mcp.py"]
    }
  }
}

${__dirname} 在安装时解析为Cowork解压缩的目录 bundle,因此您不需要硬编码任何路径。


OpenAI Codex命令行界面

将服务器添加到Codex CLI配置文件中。该文件通常位于 ~/.codex/config.json (Linux/macOS)或 %USERPROFILE%\.codex\config.json (Windows)。

{
  "mcpServers": {
    "windbg-mcp": {
      "command": "python",
      "args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
    }
  }
}

保存后,开始新的Codex会话。Winchester工具将可用于 模型调用。


光标

  1. 打开 光标→ 偏好→ 光标设置.
  2. 导航到 主控程序 选项卡。
  3. 点击 添加新的全局MCP服务器 并使用此配置:
{
  "windbg-mcp": {
    "command": "python",
    "args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
  }
}
  1. 保存。Cursor将在其下一个Composer会话中连接到服务器。

Continue.dev

将以下内容添加到您的 ~/.continue/config.json (或工作空间级别 .continue/config.json):

{
  "experimental": {
    "modelContextProtocolServers": [
      {
        "transport": {
          "type": "stdio",
          "command": "python",
          "args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
        }
      }
    ]
  }
}

重新加载Continue扩展。55个调试器工具将出现在工具列表中。


自定义代理和MCP SDK

如果您正在构建自己的代理或自动化管道,请连接到Winchester MCP 通过标准MCP stdio传输。服务器通过stdin/stdout传输JSON-RPC 2.0。

Python(使用 mcp SDK)

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="python",
    args=[r"C:\path\to\windbg-mcp\windbg_mcp.py"],
)

async def main():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # List all available tools
            tools = await session.list_tools()
            print([t.name for t in tools.tools])

            # Load a crash dump
            result = await session.call_tool(
                "load_dump",
                arguments={"path": r"C:\crashes\crash.dmp"},
            )
            print(result.content)

            # Read 64 bytes at RSP
            result = await session.call_tool(
                "read_mem",
                arguments={"addr": "0x00000000001FF000", "size": 64},
            )
            print(result.content)

asyncio.run(main())

Types/Node.js(使用 @modelcontextprotocol/sdk 包装)

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "python",
  args: ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"],
});

const client = new Client({ name: "my-agent", version: "1.0.0" }, {});
await client.connect(transport);

// Call a tool
const result = await client.callTool({
  name: "load_dump",
  arguments: { path: "C:\\crashes\\crash.dmp" },
});
console.log(result.content);

await client.close();

语言链/语言图

from langchain_mcp_adapters.tools import load_mcp_tools
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(
    command="python",
    args=[r"C:\path\to\windbg-mcp\windbg_mcp.py"],
)

async def get_tools():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            return await load_mcp_tools(session)

通过stdio直接JSON-RPC(语言无关)

服务器通过换行符分隔的JSON-RPC 2.0消息进行通信。你可以开车 通过写入进程的stdin并从stdout读取,可以从任何语言读取:

→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"my-client","version":"1.0"}}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{...},"serverInfo":{"name":"WinDbg MCP","version":"1.0.0"}}}

→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"load_dump","arguments":{"path":"C:\\crashes\\crash.dmp"}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"status\": \"ok\", ...}"}]}}

可用工具(共55个)

会话管理

工具 参数 返回
status — {connected, type, pid, bitness}
list_processes — [{pid, name, description}]
create path (必填), args, initial_break {status, pid, bitness}
attach pid 或 name (不是两者), initial_break {status, pid, bitness}
kernel_attach connect_string (必填), initial_break {status, type, connect_string}
load_dump path (必填) {status, bitness, rip, symbol_at_rip}
connect options (必填) {status, options}
detach — {status}
terminate — {status}

create --在调试器下启动新进程。集 initial_break=True (默认)在进程入口点中断。

attach --附加到正在运行的进程。提供其中之一 pid (整数)或 name (进程文件名)。不要两者都提供。

kernel_attach --连接到远程内核调试器。 connect_string 使用KD语法,例如。 "net:port=55000,key=1.2.3.4".

load_dump --打开a .dmp 验尸分析文件。立即返回崩溃地址和最近的符号。

connect --连接到进程服务器进行远程用户模式调试。 options 使用DbgEng连接语法,例如。 "tcp:server=192.168.1.10,port=5555".


执行控制

工具 参数 返回
go timeout (毫秒,默认30000) {status, rip, symbol, new_captures, captures}
step_into count (默认值1) {rip, instruction, symbol}
step_over count (默认值1) {rip, instruction, symbol}
step_out — {rip, instruction, symbol}
goto expr (必填) {rip, symbol}
trace count (默认值10) {instructions: [{rip, instruction, symbol}], count}

go --恢复执行并阻塞,直到下一个调试事件(断点、异常或超时)。返回新的RIP和运行时收集的任何捕获。

step_into --在调用被调用的函数之后,进入下一条指令。

step_over --逐步执行下一条指令,将通话视为一个步骤。

step_out --运行直到当前函数返回。

goto --运行直到达到特定的符号或十六进制地址,例如。 "Kernel32!ExitProcess" 或 "0x7fff12340000".

trace --执行N次单步迭代,并记录访问的每条指令。


断点

工具 参数 返回
bp expr (必填), capture, action, oneshot, passcount {id, expr, addr, capture}
hw_bp addr (必填), size, access, capture, action, oneshot {id, addr, size, access}
list_bps — [{id, expr, type, capture, action, ...}]
remove_bp id (必填) {status, id}
enable_bp id (必填) {status, id}
disable_bp id (必填) {status, id}

bp --在符号或地址处设置软件(代码)断点。

  • expr:符号("ntdll!NtCreateFile")或十六进制地址("0x7ff800001234")
  • capture:当 true (默认),每次触发此断点时,都会自动将完整状态(寄存器、堆栈、内存)保存到捕获缓冲区
  • action: "go" (默认)捕获后继续执行; "break" 停止
  • oneshot:在断点触发一次后将其删除
  • passcount:只有在N通过该位置后才会开火

hw_bp --设置硬件/数据断点(监视点)。

  • addr:要监视的十六进制地址
  • size:监视宽度(以字节为单位)-- 1, 2, 4,或 8 (默认值 4)
  • access: "e" 执行, "w" 写入(默认), "r" 读
  • capture, action, oneshot:语义与 bp

list_bps --返回所有当前活动的断点及其ID、表达式、类型和设置。

remove_bp / enable_bp / disable_bp --通过以下方式管理断点 id 返回自 bp 或 hw_bp.


州捕获

断点为 capture: true (默认)自动保存完整调试器 每次开火时都要拍快照。快照包括所有寄存器、调用堆栈、, RSP处的64字节堆栈存储器和RIP处的32字节代码。快照不断累积 在缓冲区中,可以随时使用 get_captures.

工具 参数 返回
get_captures — {count, captures: [{bp_id, expr, timestamp, registers, rip, symbol_at_rip, instruction, stack, context_memory}]}
clear_captures — {status}
capture_state — {timestamp, registers, rip, symbol_at_rip, instruction, disasm_5, stack_at_rsp, call_stack}

get_captures --返回自上次以来收集的所有捕获 clear_captures。每次捕获都包含:

  • registers --所有寄存器值均为 {name: "0x..."} 十六进制字符串
  • rip --捕获时的指令指针
  • symbol_at_rip --离RIP最近的符号
  • instruction --RIP指令的拆卸
  • stack --带有地址和返回地址的前10个调用堆栈帧
  • context_memory.stack_at_rsp --RSP为64字节,十六进制、格式化和ASCII
  • context_memory.code_at_rip --RIP为十六进制并格式化为32个字节

clear_captures --清除捕获缓冲区。在开始新的跑步之前有用。

capture_state --立即按需拍摄当前状态的快照。在已被闯入时使用此功能,而不是等待断点触发。


记忆

工具 参数 返回
read_mem addr (必填), size (默认值16) {addr, size, hex, formatted, ascii}
write_mem addr (必填), data (必填,十六进制字符串) {status, addr, bytes_written}
read_ptr addr (必填), count (默认值1) {addr, values: ["0x..."]}
poi addr (必填) {addr, value}
read_str addr (必填), wide (默认为false) {addr, value, wide}
dump_mem addr (必填), count (默认值8) {addr, output}
mem_info addr (必填) {addr, info}
mem_list — [region_description_strings]

read_mem --阅读 size 原始字节来自 addr。返回数据如下 hex (紧凑型), formatted (空格分隔字节),以及 ascii (可打印字符, . 不可打印)。

write_mem --将字节写入内存。 data 是一个十六进制字符串--空格和 \x 前缀会自动删除,例如。 "90909090", "\\x90\\x90\\x90\\x90",或 "90 90 90 90".

read_ptr --阅读 count 从以下位置开始的连续指针大小值(32位4个字节,64位8个字节) addr.

poi --在以下位置取消引用单个指针 addr (感兴趣的指针)。

read_str --读取以null结尾的字符串。Set wide=true 适用于UTF-16LE(Windows WCHAR)。

dump_mem --格式化的dword/指针转储,相当于 dd/dp 在Winge。

mem_info --返回包含以下内容的页面的内存区域属性 addr:基址、大小、类型、状态和保护标志。

mem_list --列出目标进程地址空间中的所有虚拟内存区域。


寄存器

工具 参数 返回
get_regs — {rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, r8–r15, eflags, ...}
get_reg name (必填) {name, value}
set_reg name (必填), value (必填) {status, name, value}
get_pc — {value, symbol, instruction}
get_sp — {value}

get_regs --将每个可用注册表返回为 {name: "0x..."}确切的设置取决于目标架构(x86与x64)。

get_reg --返回单个寄存器,例如。 name="rax", name="eflags".

set_reg --覆盖注册表。 value 接受十六进制字符串("0x1234")或十进制整数字符串。

get_pc --返回具有符号分辨率的指令指针和该地址处的解码指令文本。

get_sp --返回当前堆栈指针值。


符号和拆卸

工具 参数 返回
resolve name (必填) {name, addr} 或 {name, addr: null, error}
find_symbols pattern (必填) [symbol_strings]
addr_to_symbol addr (必填) {addr, symbol}
disasm addr (默认:当前RIP), count (默认值10) {addr, output}
whereami addr (可选,默认:当前RIP) {description}

resolve --将符号名称解析为其虚拟地址。使用 Module!Function 格式,例如。 "Kernel32!WriteFile", "ntdll!NtCreateFile".

find_symbols --通配符符号搜索,例如。 "ntdll!*Alloc*", "kernel32!*File*"。返回所有匹配的符号字符串。

addr_to_symbol --反向将虚拟地址解析为最接近的符号名称。

disasm --拆卸 count 说明开始于 addr。如果没有给出地址,则默认为当前RIP。

whereami --返回给定地址处模块、函数和偏移量的人类可读描述。


模块

工具 参数 返回
list_modules — [{name, base, size}]
module_info name (必填) {name, entry_point, sections}
get_exports name (必填) [export_strings]
get_imports name (必填) [import_strings]

list_modules --列出目标中加载的所有模块及其基址和大小。

module_info --返回特定模块的入口点和部分列表(名称、虚拟地址、大小),例如。 "kernel32.dll", "ntdll.dll".

get_exports --以字符串列表的形式返回模块的完整导出表。

get_imports --以字符串列表的形式返回模块的完整导入表。


线程和堆栈

工具 参数 返回
list_threads — [thread_description_strings]
get_thread — {current_thread}
set_thread id (必填) {status, thread}
get_stack frames (默认值20) {frames: [{frame, addr, return_addr, frame_ptr}], count}
get_teb — {addr}
get_peb — {addr}

list_threads --列出目标进程中的所有线程。

get_thread --返回当前活动的线程上下文。

set_thread --按线程ID切换活动线程上下文(从 list_threads).

get_stack --将调用堆栈作为结构化数据返回。每个帧包括指令地址、返回地址和帧指针。

get_teb --返回当前线程的线程环境块的地址。

get_peb --返回进程环境块的地址。


工艺与公用工程

工具 参数 返回
get_handles — [handle_description_strings]
get_bitness — {bits}
raw cmd (必填) {output}

get_handles --列出目标进程中所有打开的句柄。

get_bitness --退货 32 或 64 这取决于目标架构。

raw --执行任何Winkey命令字符串,并将输出作为文本返回。将此用作其他工具未覆盖的任何东西的逃生口:

raw(cmd="!heap -stat")
raw(cmd="dt _PEB @$peb")
raw(cmd="!locks")
raw(cmd="lm")
raw(cmd="!address @rsp")

典型工作流程

漏洞验证

1. create(path="C:/target/vuln.exe", args="exploit_input.bin")
2. bp(expr="vuln!processInput+0x2A", action="break")
3. go(timeout=15000)
4. get_captures()

在 get_captures,检查 captures[0].registers.rip:

  • "0x4141414141414141" --您使用“A”字节控制RIP
  • 任何与您的模式匹配的值都受控制
  • 一个看起来有效的地址——崩溃但尚未控制

检查 captures[0].context_memory.stack_at_rsp.formatted 查看堆栈上的填充、返回地址或外壳代码字节。


碰撞倾倒分析

1. load_dump(path="C:/crashes/crash.dmp")
2. get_regs()             → full register state at crash time
3. get_stack(frames=30)   → call stack at crash
4. get_sp()               → read RSP value
5. read_mem(addr=<rsp>, size=64) → stack contents
6. disasm()               → instructions at the crash address

堆喷验证

1. attach(name="target.exe")
2. hw_bp(addr="0x1001F000", size=8, access="w", action="break")
3. go()
4. get_captures()                            → see what wrote to the spray address
5. read_mem(addr="0x1001EFC0", size=128)     → surrounding memory context

ASLR检查

1. create(path="C:/target/target.exe")
2. resolve(name="kernel32!WriteFile")   → record base address
3. terminate()
4. create(path="C:/target/target.exe")
5. resolve(name="kernel32!WriteFile")   → compare: changed = ASLR on, same = ASLR off

远程内核调试

1. kernel_attach(connect_string="net:port=55000,key=1.2.3.4")
2. list_modules()                        → all loaded kernel modules
3. module_info(name="ntoskrnl.exe")      → entry point and sections
4. raw(cmd="!process 0 0")              → list all processes from kernel context
5. raw(cmd="!pcr")                       → processor control region

螺纹检查

1. attach(pid=1234)
2. list_threads()          → all thread IDs
3. set_thread(id=2)        → switch context
4. get_stack(frames=20)    → call stack for that thread
5. get_regs()              → registers for that thread
6. get_teb()               → TEB address

提示

符号路径 --如果符号解析未返回结果,请配置Microsoft符号服务器:

raw(cmd=".sympath srv*C:\\symbols*https://msdl.microsoft.com/download/symbols")
raw(cmd=".reload")

超时调整 — go() 默认为30秒。对于在到达断点之前运行时间较长的目标:

go(timeout=120000)   # 2 minutes
go(timeout=300000)   # 5 minutes

地址格式 --全部 addr 参数接受十六进制字符串("0x1234abcd", "7fff12340000")或纯整数。这 0x 前缀对于十六进制值是可选的。

Shellcode验证 --捕获后,使用 read_mem 和 disasm 在您的shellcode应该着陆的地址上。如果 disasm 显示了您的预期指令,有效载荷完好无损地到达。

之后 terminate 或 detach --所有捕获和断点都会自动清除。呼叫 create 或 attach 开始新的会议。

capture_state 对比 get_captures --使用 capture_state 当已经在断点处停止时,用于按需快照。使用 get_captures 检索在每次触发断点时自动保存的状态 go 电话。

内核 raw 命令 --运行良好的通用内核调试扩展 raw:

raw(cmd="!process 0 0")       → list all processes
raw(cmd="!thread")            → current thread details
raw(cmd="!irql")              → current IRQL
raw(cmd="!pcr")               → processor control region
raw(cmd="!pte <addr>")        → page table entry for an address
raw(cmd="dt nt!_EPROCESS @$proc")  → dump EPROCESS structure

许可证

麻省理工学院