
Un server MCP (Model Context Protocol) che trasforma tutte le funzioni del debugger Windows pybag in strumenti MCP nativi. Permette ai client compatibili con MCP (Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor e agenti personalizzati) di controllare i processi in modalità utente, le sessioni del kernel e l'analisi dei dump di crash tramite chiamate JSON strutturate.
Un server MCP (Model Context Protocol) che espone ogni funzione del debugger Windows pybag come strumento MCP nativo. Offre a qualsiasi client compatibile con MCP (Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor e agenti personalizzati) il pieno controllo su processi in modalità utente, sessioni kernel e analisi di crash dump — il tutto tramite chiamate di strumenti tipizzate con risposte JSON strutturate.
git clone https://github.com/your-username/windbg-mcp.git cd windbg-mcp
### 2. Installa le dipendenze Python```bat
pip install pybag mcp
Scarica Windows SDK e seleziona Debugging Tools for Windows durante l'installazione: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
Il server viene eseguito come processo stdio locale. Tutti i client di seguito lo avviano allo stesso modo — python <path-to>/windbg_mcp.py — ma ognuno ha il proprio formato di configurazione.
Modifica il file di configurazione di Claude Desktop e aggiungi la voce windbg-mcp:
Posizione del file di configurazione:
%APPDATA%\Claude\claude_desktop_config.jsonRiavvia Claude Desktop. Tutti i 55 strumenti di debug appariranno automaticamente.
---
### Claude Code (CLI)
Esegui il seguente comando una volta per registrare il server. Claude Code memorizza l'entry
nella propria configurazione MCP e rende gli strumenti disponibili in ogni sessione successiva.```bash
claude mcp add windbg-mcp python C:\path\to\windbg-mcp\windbg_mcp.py
Per verificare che il server sia stato registrato:```bash claude mcp list
Per rimuoverlo in seguito:```bash
claude mcp remove windbg-mcp
Ci sono due modi per aggiungere WinDbg MCP a Cowork: tramite configurazione JSON (rapida) o installandolo come bundle plugin .mcpb (portatile, condivisibile).
3. Salva e riavvia Cowork. Gli strumenti saranno disponibili nella tua prossima sessione.
#### Opzione B — Installazione come bundle di plugin `.mcpb`
Un file `.mcpb` è un archivio zip della directory del plugin che Cowork può installare
direttamente. Questo è l'approccio consigliato quando si condivide il server con un team o
tra macchine diverse.
**Passo 1 — Costruisci il file `.mcpb`**
Dalla radice del repository clonato, esegui:```bat
powershell -Command "Compress-Archive -Path '.\*' -DestinationPath 'windbg-mcp.zip'; Rename-Item 'windbg-mcp.zip' 'windbg-mcp.mcpb'"
Questo crea windbg-mcp.mcpb nella directory corrente, raggruppando windbg_mcp.py,
manifest.json e qualsiasi altro file del progetto.
Passaggio 2 — Installazione in Cowork
windbg-mcp.mcpb.manifest.json dal bundle, registra il server MCP e
rende tutti gli strumenti immediatamente disponibili — nessuna configurazione manuale del percorso necessaria.Il manifest.json incluso in questo repository è già configurato correttamente:```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}` viene risolto al momento dell'installazione nella directory in cui Cowork ha decompresso il bundle, quindi non è necessario codificare alcun percorso.
---
### OpenAI Codex CLI
Aggiungi il server al file di configurazione di Codex CLI. Il file si trova solitamente in `~/.codex/config.json` (Linux/macOS) o `%USERPROFILE%\.codex\config.json` (Windows).```json
{
"mcpServers": {
"windbg-mcp": {
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
}
Una volta salvato, avvia una nuova sessione di Codex. Gli strumenti WinDbg saranno disponibili per essere chiamati dal modello.
4. Salva. Cursor si connetterà al server nella sua prossima sessione Composer.
---
### Continue.dev
Aggiungi quanto segue al tuo `~/.continue/config.json` (o al
`.continue/config.json` a livello di workspace):```json
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
]
}
}
Ricarica l'estensione Continue. I 55 strumenti di debug appariranno nell'elenco degli strumenti.
Se stai creando un tuo agente o pipeline di automazione, connettiti a WinDbg MCP tramite il trasporto stdio MCP standard. Il server parla JSON-RPC 2.0 su stdin/stdout.
mcp)```pythonimport 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())
#### TypeScript / Node.js (utilizzando il pacchetto `@modelcontextprotocol/sdk`)```typescript
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)
#### Direct JSON-RPC over stdio (language-agnostic)
Il server comunica tramite messaggi JSON-RPC 2.0 delimitati da nuove righe. Puoi pilotarlo da qualsiasi linguaggio scrivendo sullo stdin del processo e leggendo dallo 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\", ...}"}]}}
create — Avvia un nuovo processo sotto il debugger. Imposta initial_break=True (predefinito) per fermarsi al punto di ingresso del processo.
attach — Si attacca a un processo in esecuzione. Fornisci pid (intero) o name (nome del file del processo). Non fornire entrambi.
kernel_attach — Si connette a un debugger del kernel remoto. connect_string usa la sintassi KD, ad es. "net:port=55000,key=1.2.3.4".
load_dump — Apre un file .dmp per l'analisi post-mortem. Restituisce immediatamente l'indirizzo del crash e il simbolo più vicino.
connect — Si connette a un server di processo per il debug remoto in modalità utente. options usa la sintassi di connessione DbgEng, ad es. "tcp:server=192.168.1.10,port=5555".
go — Riprende l'esecuzione e si blocca fino al prossimo evento di debug (breakpoint, eccezione o timeout). Restituisce il nuovo RIP e tutti i capture raccolti durante l'esecuzione.
step_into — Entra nell'istruzione successiva, seguendo le chiamate nelle funzioni chiamate.
step_over — Esegue l'istruzione successiva, trattando le chiamate come un singolo passo.
step_out — Esegue fino al ritorno della funzione corrente.
goto — Esegue fino a quando non viene raggiunto un simbolo specifico o un indirizzo esadecimale, ad es. "Kernel32!ExitProcess" o "0x7fff12340000".
trace — Esegue N iterazioni di singolo passo e registra ogni istruzione visitata.
bp — Imposta un breakpoint software (codice) su un simbolo o indirizzo.
expr: simbolo ("ntdll!NtCreateFile") o indirizzo esadecimale ("0x7ff800001234")capture: quando true (predefinito), salva automaticamente lo stato completo — registri, stack, memoria — nel buffer di capture ogni volta che questo breakpoint viene attivatoaction: "go" (predefinito) continua l'esecuzione dopo il capture; "break" si fermaoneshot: rimuove il breakpoint dopo che si è attivato una voltapasscount: si attiva solo dopo N passaggi attraverso la posizionehw_bp — Imposta un breakpoint hardware / dati (watchpoint).
addr: indirizzo esadecimale da osservaresize: larghezza di osservazione in byte — 1, 2, 4 o 8 (predefinito 4)access: "e" esegui, "w" scrivi (predefinito), "r" leggi/scrivicapture, action, oneshot: stessa semantica di bplist_bps — Restituisce tutti i breakpoint attualmente attivi con i loro ID, espressioni, tipi e impostazioni.
remove_bp / enable_bp / disable_bp — Gestisci i breakpoint tramite l'id restituito da bp o hw_bp.
I breakpoint con capture: true (predefinito) salvano automaticamente un'istantanea completa del debugger ogni volta che si attivano. L'istantanea include tutti i registri, lo stack delle chiamate, 64 byte di memoria dello stack a RSP e 32 byte di codice a RIP. Le istantanee si accumulano in un buffer e possono essere recuperate in qualsiasi momento con get_captures.
get_captures — Restituisce tutti i capture raccolti dall'ultimo clear_captures. Ogni capture contiene:
registers — tutti i valori dei registri come stringhe esadecimali {name: "0x..."}rip — puntatore all'istruzione al momento del capturesymbol_at_rip — simbolo più vicino a RIPinstruction — disassemblaggio dell'istruzione a RIPstack — i primi 10 frame dello stack delle chiamate con indirizzi e indirizzi di ritornocontext_memory.stack_at_rsp — 64 byte a RSP come esadecimale, formattato e ASCIIcontext_memory.code_at_rip — 32 byte a RIP come esadecimale e formattatoclear_captures — Pulisce il buffer dei capture. Utile prima di iniziare una nuova esecuzione.
capture_state — Scatta un'istantanea immediata su richiesta dello stato corrente. Usalo quando sei già fermato, invece di aspettare che un breakpoint si attivi.
read_mem — Legge size byte grezzi da addr. Restituisce i dati come hex (compatto), formatted (byte separati da spazi) e ascii (caratteri stampabili, . per non stampabili).
write_mem — Scrive byte in memoria. data è una stringa esadecimale — spazi e prefissi \x vengono rimossi automaticamente, ad es. "90909090", "\\x90\\x90\\x90\\x90" o "90 90 90 90".
read_ptr — Legge count valori consecutivi delle dimensioni di un puntatore (4 byte su 32 bit, 8 byte su 64 bit) a partire da addr.
poi — Dereferenzia un singolo puntatore all'indirizzo addr (puntatore di interesse).
read_str — Legge una stringa terminata da null. Imposta wide=true per UTF-16LE (Windows WCHAR).
dump_mem — Dump formattato di dword/puntatore, equivalente a dd/dp in WinDbg.
mem_info — Restituisce le proprietà della regione di memoria per la pagina contenente addr: indirizzo di base, dimensione, tipo, stato e flag di protezione.
mem_list — Elenca tutte le regioni di memoria virtuale nello spazio degli indirizzi del processo target.
get_regs — Restituisce ogni registro disponibile come {name: "0x..."}. L'insieme esatto dipende dall'architettura target (x86 vs x64).
get_reg — Restituisce un singolo registro, ad es. name="rax", name="eflags".
set_reg — Sovrascrive un registro. value accetta stringhe esadecimali ("0x1234") o stringhe decimali intere.
get_pc — Restituisce il puntatore all'istruzione con la risoluzione del simbolo e il testo dell'istruzione decodificata a quell'indirizzo.
get_sp — Restituisce il valore corrente del puntatore allo stack.
resolve — Risolve un nome di simbolo nel suo indirizzo virtuale. Usa il formato Module!Function, ad es. "Kernel32!WriteFile", "ntdll!NtCreateFile".
find_symbols — Ricerca di simboli con wildcard, ad es. "ntdll!*Alloc*", "kernel32!*File*". Restituisce tutte le stringhe di simboli corrispondenti.
addr_to_symbol — Risolve inversamente un indirizzo virtuale nel nome del simbolo più vicino.
disasm — Disassembla count istruzioni a partire da addr. Usa il RIP corrente se non viene fornito alcun indirizzo.
whereami — Restituisce una descrizione leggibile dall'uomo del modulo, funzione e offset all'indirizzo dato.
list_modules — Elenca tutti i moduli caricati nel target, con il loro indirizzo di base e dimensione.
module_info — Restituisce il punto di ingresso e l'elenco delle sezioni (nome, indirizzo virtuale, dimensione) per un modulo specifico, ad es. "kernel32.dll", "ntdll.dll".
get_exports — Restituisce la tabella delle esportazioni completa di un modulo come elenco di stringhe.
get_imports — Restituisce la tabella delle importazioni completa di un modulo come elenco di stringhe.
list_threads — Elenca tutti i thread nel processo target.
get_thread — Restituisce il contesto del thread attualmente attivo.
set_thread — Cambia il contesto del thread attivo tramite ID thread (da list_threads).
get_stack — Restituisce lo stack delle chiamate come dati strutturati. Ogni frame include l'indirizzo dell'istruzione, l'indirizzo di ritorno e il puntatore al frame.
get_teb — Restituisce l'indirizzo del Thread Environment Block per il thread corrente.
get_peb — Restituisce l'indirizzo del Process Environment Block.
get_handles — Elenca tutti gli handle aperti nel processo target.
get_bitness — Restituisce 32 o 64 a seconda dell'architettura target.
raw — Esegue qualsiasi stringa di comando WinDbg e restituisce l'output come testo. Usalo come via di fuga per qualsiasi cosa non coperta dagli altri strumenti:```
raw(cmd="!heap -stat")
raw(cmd="dt _PEB @$peb")
raw(cmd="!locks")
raw(cmd="lm")
raw(cmd="!address @rsp")
---
## Flussi di lavoro tipici
### Verifica degli exploit```
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()
In get_captures, ispeziona captures[0].registers.rip:
"0x4141414141414141" — controlli RIP con byte 'A'Controlla captures[0].context_memory.stack_at_rsp.formatted per vedere padding, indirizzi di ritorno o byte di shellcode nello stack.
### Verifica dello Heap spray```
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
### Debug remoto del kernel```
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
---
## Suggerimenti
**Percorso dei simboli** — Se la risoluzione dei simboli non restituisce risultati, configura il server dei simboli Microsoft:```
raw(cmd=".sympath srv*C:\\symbols*https://msdl.microsoft.com/download/symbols")
raw(cmd=".reload")
Regolazione del timeout — go() predefinito a 30 secondi. Per target che eseguono più a lungo prima di raggiungere un breakpoint:```
go(timeout=120000) # 2 minutes
go(timeout=300000) # 5 minutes
**Formato degli indirizzi** — Tutti i parametri `addr` accettano stringhe esadecimali (`"0x1234abcd"`, `"7fff12340000"`) o numeri interi. Il prefisso `0x` è opzionale per i valori esadecimali.
**Verifica dello shellcode** — Dopo una cattura, usa `read_mem` e `disasm` sull'indirizzo in cui dovrebbe trovarsi il tuo shellcode. Se `disasm` mostra le istruzioni desiderate, il payload è arrivato intatto.
**Dopo `terminate` o `detach`** — Tutte le catture e i breakpoint vengono cancellati automaticamente. Chiama `create` o `attach` per iniziare una nuova sessione.
**`capture_state` vs `get_captures`** — Usa `capture_state` per un'istantanea su richiesta quando sei già fermo a un breakpoint. Usa `get_captures` per recuperare lo stato che è stato automaticamente salvato ogni volta che un breakpoint si è attivato durante una chiamata `go`.
**Comandi `raw` del kernel** — Estensioni comuni di debug del kernel che funzionano bene tramite `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
MIT
| Strumento | Parametri | Ritorna |
|---|
status | — | {connected, type, pid, bitness} |
list_processes | — | [{pid, name, description}] |
create | path (richiesto), args, initial_break | {status, pid, bitness} |
attach | pid o name (non entrambi), initial_break | {status, pid, bitness} |
kernel_attach | connect_string (richiesto), initial_break | {status, type, connect_string} |
load_dump | path (richiesto) | {status, bitness, rip, symbol_at_rip} |
connect | options (richiesto) | {status, options} |
detach | — | {status} |
terminate | — | {status} |
| Strumento | Parametri | Ritorna |
|---|
go | timeout (ms, default 30000) | {status, rip, symbol, new_captures, captures} |
step_into | count (default 1) | {rip, instruction, symbol} |
step_over | count (default 1) | {rip, instruction, symbol} |
step_out | — | {rip, instruction, symbol} |
goto | expr (richiesto) | {rip, symbol} |
trace | count (default 10) | {instructions: [{rip, instruction, symbol}], count} |
| Strumento | Parametri | Ritorna |
|---|
bp | expr (richiesto), capture, action, oneshot, passcount | {id, expr, addr, capture} |
hw_bp | addr (richiesto), size, access, capture, action, oneshot | {id, addr, size, access} |
list_bps | — | [{id, expr, type, capture, action, ...}] |
remove_bp | id (richiesto) | {status, id} |
enable_bp | id (richiesto) | {status, id} |
disable_bp | id (richiesto) | {status, id} |
| Strumento | Parametri | Ritorna |
|---|
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} |
| Strumento | Parametri | Ritorna |
|---|
read_mem | addr (richiesto), size (default 16) | {addr, size, hex, formatted, ascii} |
write_mem | addr (richiesto), data (richiesto, stringa esadecimale) | {status, addr, bytes_written} |
read_ptr | addr (richiesto), count (default 1) | {addr, values: ["0x..."]} |
poi | addr (richiesto) | {addr, value} |
read_str | addr (richiesto), wide (default false) | {addr, value, wide} |
dump_mem | addr (richiesto), count (default 8) | {addr, output} |
mem_info | addr (richiesto) | {addr, info} |
mem_list | — | [region_description_strings] |
| Strumento | Parametri | Ritorna |
|---|
get_regs | — | {rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, r8–r15, eflags, ...} |
get_reg | name (richiesto) | {name, value} |
set_reg | name (richiesto), value (richiesto) | {status, name, value} |
get_pc | — | {value, symbol, instruction} |
get_sp | — | {value} |
| Strumento | Parametri | Ritorna |
|---|
resolve | name (richiesto) | {name, addr} o {name, addr: null, error} |
find_symbols | pattern (richiesto) | [symbol_strings] |
addr_to_symbol | addr (richiesto) | {addr, symbol} |
disasm | addr (default: RIP corrente), count (default 10) | {addr, output} |
whereami | addr (opzionale, default: RIP corrente) | {description} |
| Strumento | Parametri | Ritorna |
|---|
list_modules | — | [{name, base, size}] |
module_info | name (richiesto) | {name, entry_point, sections} |
get_exports | name (richiesto) | [export_strings] |
get_imports | name (richiesto) | [import_strings] |
| Strumento | Parametri | Ritorna |
|---|
list_threads | — | [thread_description_strings] |
get_thread | — | {current_thread} |
set_thread | id (richiesto) | {status, thread} |
get_stack | frames (default 20) | {frames: [{frame, addr, return_addr, frame_ptr}], count} |
get_teb | — | {addr} |
get_peb | — | {addr} |
| Strumento | Parametri | Ritorna |
|---|
get_handles | — | [handle_description_strings] |
get_bitness | — | {bits} |
raw | cmd (richiesto) | {output} |