
Un serveur MCP (Model Context Protocol) qui transforme toutes les fonctions du débogueur Windows pybag en outils MCP natifs. Il permet aux clients compatibles MCP (Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor et agents personnalisés) de contrôler les processus en mode utilisateur, les sessions noyau et l'analyse de vidages sur incident via des appels JSON structurés.
Un serveur MCP (Model Context Protocol) qui expose chaque fonction de débogueur Windows pybag en tant qu'outil MCP natif. Il donne à tout client compatible MCP (Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor et agents personnalisés) un contrôle total sur les processus en mode utilisateur, les sessions noyau et l'analyse des fichiers de vidage sur incident — le tout via des appels d'outils typés avec des réponses JSON structurées.
git clone https://github.com/your-username/windbg-mcp.git cd windbg-mcp
### 2. Installer les dépendances Python```bat
pip install pybag mcp
Téléchargez le SDK Windows et sélectionnez Outils de débogage pour Windows lors de l'installation : https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
Le serveur s'exécute en tant que processus stdio local. Tous les clients ci-dessous le lancent de la même manière — python <path-to>/windbg_mcp.py — mais chacun a son propre format de configuration.
Modifiez le fichier de configuration de Claude Desktop et ajoutez l'entrée windbg-mcp :
Emplacement du fichier de configuration :
%APPDATA%\Claude\claude_desktop_config.jsonRedémarrez Claude Desktop. Les 55 outils de débogage apparaîtront automatiquement.
---
### Claude Code (CLI)
Exécutez la commande suivante une fois pour enregistrer le serveur. Claude Code stocke l'entrée dans sa propre configuration MCP et rend les outils disponibles dans chaque session suivante.```bash
claude mcp add windbg-mcp python C:\path\to\windbg-mcp\windbg_mcp.py
Pour vérifier que le serveur a été enregistré :```bash claude mcp list
Pour le supprimer plus tard :```bash
claude mcp remove windbg-mcp
Il y a deux façons d'ajouter WinDbg MCP à Cowork : via une configuration JSON (rapide) ou en l'installant comme un bundle de plugin .mcpb (portable, partageable).
3. Enregistrez et redémarrez Cowork. Les outils seront disponibles dans votre prochaine session.
#### Option B — Installer en tant que bundle de plugin `.mcpb`
Un fichier `.mcpb` est une archive zip du répertoire du plugin que Cowork peut installer directement. Cette approche est recommandée lorsque l'on partage le serveur avec une équipe ou entre machines.
**Étape 1 — Construire le fichier `.mcpb`**
Depuis la racine du dépôt cloné, exécutez :```bat
powershell -Command "Compress-Archive -Path '.\*' -DestinationPath 'windbg-mcp.zip'; Rename-Item 'windbg-mcp.zip' 'windbg-mcp.mcpb'"
Cela crée windbg-mcp.mcpb dans le répertoire courant, regroupant windbg_mcp.py,
manifest.json et tous les autres fichiers du projet.
Étape 2 — Installation dans Cowork
windbg-mcp.mcpb.manifest.json du bundle, enregistre le serveur MCP et rend tous les outils immédiatement disponibles — aucune configuration manuelle de chemin requise.Le manifest.json inclus dans ce dépôt est déjà correctement configuré :```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}` est résolu lors de l'installation dans le répertoire où Cowork a décompressé le bundle, vous n'avez donc pas besoin de coder en dur des chemins.
---
### OpenAI Codex CLI
Ajoutez le serveur à votre fichier de configuration Codex CLI. Le fichier se trouve généralement à l'emplacement
`~/.codex/config.json` (Linux/macOS) ou `%USERPROFILE%\.codex\config.json` (Windows).```json
{
"mcpServers": {
"windbg-mcp": {
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
}
Une fois enregistré, démarrez une nouvelle session Codex. Les outils WinDbg seront disponibles pour que le modèle puisse les appeler.
4. Enregistrez. Cursor se connectera au serveur lors de sa prochaine session Composer.
---
### Continue.dev
Ajoutez ce qui suit à votre `~/.continue/config.json` (ou au `.continue/config.json` au niveau de l'espace de travail) :```json
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
]
}
}
Reload the Continue extension. Les 55 outils de débogage apparaîtront dans la liste des outils.
Si vous construisez votre propre agent ou pipeline d'automatisation, connectez-vous à WinDbg MCP via le transport standard MCP stdio. Le serveur parle JSON-RPC 2.0 via stdin/stdout.
mcp SDK)```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 (utilisant le `@modelcontextprotocol/sdk` package)```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)
#### JSON-RPC direct via stdio (indépendant du langage)
Le serveur communique via des messages JSON-RPC 2.0 délimités par des sauts de ligne. Vous pouvez le piloter depuis n'importe quel langage en écrivant dans stdin du processus et en lisant depuis 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 — Lance un nouveau processus sous le débogueur. Définissez initial_break=True (par défaut) pour vous arrêter au point d'entrée du processus.
attach — S'attache à un processus en cours d'exécution. Fournissez soit pid (entier) soit name (nom du fichier du processus). Ne fournissez pas les deux.
kernel_attach — Se connecte à un débogueur noyau distant. connect_string utilise la syntaxe KD, par ex. "net:port=55000,key=1.2.3.4".
load_dump — Ouvre un fichier .dmp pour une analyse post-mortem. Retourne immédiatement l'adresse du crash et le symbole le plus proche.
connect — Se connecte à un serveur de processus pour le débogage utilisateur distant. options utilise la syntaxe de connexion DbgEng, par ex. "tcp:server=192.168.1.10,port=5555".
go — Reprend l'exécution et bloque jusqu'au prochain événement de débogage (point d'arrêt, exception ou timeout). Retourne le nouveau RIP et toutes les captures collectées pendant l'exécution.
step_into — Exécute pas à pas l'instruction suivante, en suivant les appels dans les fonctions appelées.
step_over — Exécute pas à pas l'instruction suivante, en traitant les appels comme une seule étape.
step_out — S'exécute jusqu'à ce que la fonction courante retourne.
goto — S'exécute jusqu'à ce qu'un symbole spécifique ou une adresse hexadécimale soit atteint, par ex. "Kernel32!ExitProcess" ou "0x7fff12340000".
trace — Effectue N itérations pas à pas uniques et enregistre chaque instruction visitée.
bp — Définit un point d'arrêt logiciel (code) à un symbole ou une adresse.
expr : symbole ("ntdll!NtCreateFile") ou adresse hexadécimale ("0x7ff800001234")capture : quand true (par défaut), sauvegarde automatiquement l'état complet — registres, pile, mémoire — dans le tampon de capture chaque fois que ce point d'arrêt se déclencheaction : "go" (par défaut) continue l'exécution après la capture ; "break" s'arrêteoneshot : supprime le point d'arrêt après un seul déclenchementpasscount : ne se déclenche qu'après N passages à cet emplacementhw_bp — Définit un point d'arrêt matériel / de données (watchpoint).
addr : adresse hexadécimale à surveillersize : largeur de la surveillance en octets — 1, 2, 4 ou 8 (défaut 4)access : "e" exécution, "w" écriture (par défaut), "r" lecture/écriturecapture, action, oneshot : mêmes sémantiques que bplist_bps — Retourne tous les points d'arrêt actifs avec leurs ID, expressions, types et réglages.
remove_bp / enable_bp / disable_bp — Gère les points d'arrêt par l'id retourné par bp ou hw_bp.
Les points d'arrêt avec capture: true (par défaut) sauvegardent automatiquement un instantané complet du débogueur chaque fois qu'ils se déclenchent. L'instantané comprend tous les registres, la pile d'appels, 64 octets de pile à RSP, et 32 octets de code à RIP. Les instantanés s'accumulent dans un tampon et peuvent être récupérés à tout moment avec get_captures.
get_captures — Retourne toutes les captures collectées depuis le dernier clear_captures. Chaque capture contient :
registers — toutes les valeurs des registres sous forme de chaînes hexadécimales {name: "0x..."}rip — pointeur d'instruction au moment de la capturesymbol_at_rip — symbole le plus proche de RIPinstruction — désassemblage de l'instruction à RIPstack — 10 premières trames de la pile d'appels avec adresses et adresses de retourcontext_memory.stack_at_rsp — 64 octets à RSP sous forme hexadécimale, formatée et ASCIIcontext_memory.code_at_rip — 32 octets à RIP sous forme hexadécimale et formatéeclear_captures — Vide le tampon de capture. Utile avant de commencer une nouvelle exécution.
capture_state — Prend un instantané immédiat à la demande de l'état actuel. Utilisez-le lorsque vous êtes déjà arrêté, plutôt que d'attendre qu'un point d'arrêt se déclenche.
read_mem — Lit size octets bruts à partir de addr. Retourne les données sous forme hex (compacte), formatted (octets séparés par des espaces) et ascii (caractères imprimables, . pour les non imprimables).
write_mem — Écrit des octets en mémoire. data est une chaîne hexadécimale — les espaces et les préfixes \x sont automatiquement supprimés, par ex. "90909090", "\\x90\\x90\\x90\\x90" ou "90 90 90 90".
read_ptr — Lit count valeurs consécutives de taille pointeur (4 octets en 32 bits, 8 octets en 64 bits) à partir de addr.
poi — Déréférence un pointeur unique à addr (pointeur d'intérêt).
read_str — Lit une chaîne terminée par un caractère nul. Définissez wide=true pour UTF-16LE (WCHAR Windows).
dump_mem — Vidange formatée de dword/pointeur, équivalent à dd/dp dans WinDbg.
mem_info — Retourne les propriétés de la région mémoire pour la page contenant addr : adresse de base, taille, type, état et drapeaux de protection.
mem_list — Liste toutes les régions de mémoire virtuelle dans l'espace d'adressage du processus cible.
get_regs — Retourne chaque registre disponible sous forme {name: "0x..."}. L'ensemble exact dépend de l'architecture cible (x86 vs x64).
get_reg — Retourne un registre unique, par ex. name="rax", name="eflags".
set_reg — Remplace un registre. value accepte les chaînes hexadécimales ("0x1234") ou les chaînes d'entiers décimaux.
get_pc — Retourne le pointeur d'instruction avec la résolution de symbole et le texte d'instruction décodé à cette adresse.
get_sp — Retourne la valeur actuelle du pointeur de pile.
resolve — Résout un nom de symbole en son adresse virtuelle. Utilisez le format Module!Fonction, par ex. "Kernel32!WriteFile", "ntdll!NtCreateFile".
find_symbols — Recherche de symboles avec caractère générique, par ex. "ntdll!*Alloc*", "kernel32!*File*". Retourne toutes les chaînes de symboles correspondantes.
addr_to_symbol — Résolution inverse d'une adresse virtuelle vers le nom de symbole le plus proche.
disasm — Désassemble count instructions à partir de addr. Par défaut, utilise le RIP actuel si aucune adresse n'est donnée.
whereami — Retourne une description lisible du module, de la fonction et du décalage à l'adresse donnée.
list_modules — Liste tous les modules chargés dans la cible, avec leur adresse de base et leur taille.
module_info — Retourne le point d'entrée et la liste des sections (nom, adresse virtuelle, taille) pour un module spécifique, par ex. "kernel32.dll", "ntdll.dll".
get_exports — Retourne la table d'exportation complète d'un module sous forme de liste de chaînes.
get_imports — Retourne la table d'importation complète d'un module sous forme de liste de chaînes.
list_threads — Liste tous les threads dans le processus cible.
get_thread — Retourne le contexte de thread actuellement actif.
set_thread — Bascule le contexte de thread actif par l'ID de thread (depuis list_threads).
get_stack — Retourne la pile d'appels sous forme de données structurées. Chaque trame comprend l'adresse de l'instruction, l'adresse de retour et le pointeur de trame.
get_teb — Retourne l'adresse du Thread Environment Block pour le thread actuel.
get_peb — Retourne l'adresse du Process Environment Block.
get_handles — Liste tous les handles ouverts dans le processus cible.
get_bitness — Retourne 32 ou 64 selon l'architecture cible.
raw — Exécute n'importe quelle commande WinDbg et retourne la sortie sous forme de texte. Utilisez ceci comme une échappatoire pour tout ce qui n'est pas couvert par les autres outils :```
raw(cmd="!heap -stat")
raw(cmd="dt _PEB @$peb")
raw(cmd="!locks")
raw(cmd="lm")
raw(cmd="!address @rsp")
---
## Flux de travail typiques
### Vérification d'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()
Dans get_captures, inspectez captures[0].registers.rip :
"0x4141414141414141" — vous contrôlez RIP avec les octets 'A'Vérifiez captures[0].context_memory.stack_at_rsp.formatted pour voir le padding, les adresses de retour ou les octets de shellcode sur la pile.
---
### Vérification du 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
---
### Débogage à distance du noyau```
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
---
## Conseils
**Chemin des symboles** — Si la résolution des symboles ne renvoie aucun résultat, configurez le serveur de symboles Microsoft :```
raw(cmd=".sympath srv*C:\\symbols*https://msdl.microsoft.com/download/symbols")
raw(cmd=".reload")
Timeout tuning — go() par défaut à 30 secondes. Pour les cibles qui s'exécutent plus longtemps avant d'atteindre un point d'arrêt:```
go(timeout=120000) # 2 minutes
go(timeout=300000) # 5 minutes
**Format d'adresse** — Tous les paramètres `addr` acceptent des chaînes hexadécimales (`"0x1234abcd"`, `"7fff12340000"`) ou des entiers simples. Le préfixe `0x` est optionnel pour les valeurs hexadécimales.
**Vérification du shellcode** — Après une capture, utilisez `read_mem` et `disasm` sur l'adresse où votre shellcode doit atterrir. Si `disasm` affiche vos instructions prévues, la charge utile est arrivée intacte.
**Après `terminate` ou `detach`** — Toutes les captures et points d'arrêt sont effacés automatiquement. Appelez `create` ou `attach` pour démarrer une nouvelle session.
**`capture_state` vs `get_captures`** — Utilisez `capture_state` pour un instantané à la demande lorsque vous êtes déjà arrêté à un point d'arrêt. Utilisez `get_captures` pour récupérer l'état qui a été automatiquement sauvegardé à chaque déclenchement d'un point d'arrêt lors d'un appel `go`.
**Commandes `raw` du noyau** — Extensions de débogage du noyau courantes qui fonctionnent bien via `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
| Outil | Paramètres | Retours |
|---|
status | — | {connected, type, pid, bitness} |
list_processes | — | [{pid, name, description}] |
create | path (obligatoire), args, initial_break | {status, pid, bitness} |
attach | pid ou name (pas les deux), initial_break | {status, pid, bitness} |
kernel_attach | connect_string (obligatoire), initial_break | {status, type, connect_string} |
load_dump | path (obligatoire) | {status, bitness, rip, symbol_at_rip} |
connect | options (obligatoire) | {status, options} |
detach | — | {status} |
terminate | — | {status} |
| Outil | Paramètres | Retours |
|---|
go | timeout (ms, défaut 30000) | {status, rip, symbol, new_captures, captures} |
step_into | count (défaut 1) | {rip, instruction, symbol} |
step_over | count (défaut 1) | {rip, instruction, symbol} |
step_out | — | {rip, instruction, symbol} |
goto | expr (obligatoire) | {rip, symbol} |
trace | count (défaut 10) | {instructions: [{rip, instruction, symbol}], count} |
| Outil | Paramètres | Retours |
|---|
bp | expr (obligatoire), capture, action, oneshot, passcount | {id, expr, addr, capture} |
hw_bp | addr (obligatoire), size, access, capture, action, oneshot | {id, addr, size, access} |
list_bps | — | [{id, expr, type, capture, action, ...}] |
remove_bp | id (obligatoire) | {status, id} |
enable_bp | id (obligatoire) | {status, id} |
disable_bp | id (obligatoire) | {status, id} |
| Outil | Paramètres | Retours |
|---|
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} |
| Outil | Paramètres | Retours |
|---|
read_mem | addr (obligatoire), size (défaut 16) | {addr, size, hex, formatted, ascii} |
write_mem | addr (obligatoire), data (obligatoire, chaîne hexadécimale) | {status, addr, bytes_written} |
read_ptr | addr (obligatoire), count (défaut 1) | {addr, values: ["0x..."]} |
poi | addr (obligatoire) | {addr, value} |
read_str | addr (obligatoire), wide (défaut false) | {addr, value, wide} |
dump_mem | addr (obligatoire), count (défaut 8) | {addr, output} |
mem_info | addr (obligatoire) | {addr, info} |
mem_list | — | [region_description_strings] |
| Outil | Paramètres | Retours |
|---|
get_regs | — | {rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, rip, r8–r15, eflags, ...} |
get_reg | name (obligatoire) | {name, value} |
set_reg | name (obligatoire), value (obligatoire) | {status, name, value} |
get_pc | — | {value, symbol, instruction} |
get_sp | — | {value} |
| Outil | Paramètres | Retours |
|---|
resolve | name (obligatoire) | {name, addr} ou {name, addr: null, error} |
find_symbols | pattern (obligatoire) | [symbol_strings] |
addr_to_symbol | addr (obligatoire) | {addr, symbol} |
disasm | addr (défaut : RIP actuel), count (défaut 10) | {addr, output} |
whereami | addr (optionnel, défaut : RIP actuel) | {description} |
| Outil | Paramètres | Retours |
|---|
list_modules | — | [{name, base, size}] |
module_info | name (obligatoire) | {name, entry_point, sections} |
get_exports | name (obligatoire) | [export_strings] |
get_imports | name (obligatoire) | [import_strings] |
| Outil | Paramètres | Retours |
|---|
list_threads | — | [thread_description_strings] |
get_thread | — | {current_thread} |
set_thread | id (obligatoire) | {status, thread} |
get_stack | frames (défaut 20) | {frames: [{frame, addr, return_addr, frame_ptr}], count} |
get_teb | — | {addr} |
get_peb | — | {addr} |
| Outil | Paramètres | Retours |
|---|
get_handles | — | [handle_description_strings] |
get_bitness | — | {bits} |
raw | cmd (obligatoire) | {output} |