A Model Context Protocol (MCP) server that gives AI agents complete control over a Windows VM for kernel debugging, reverse engineering, malware analysis, and vulnerability research.
Built in C# (.NET 8), it wraps the Windows Debugger Engine (DbgEng COM), VMware Workstation, Frida, and dbgsrv into 29 MCP tools that any MCP-compatible LLM client can call.
Other WinDbg MCP servers exist — most are Python wrappers that launch cdb.exe or windbg.exe as a subprocess and drive it over stdin/stdout. That's easy to prototype but fragile in practice: the child debugger crashes, hangs on modal dialogs, deadlocks its own pipes, or dies mid-session and takes the agent's context with it.
This project takes a different approach:
- Direct DbgEng COM — calls the Windows Debugger Engine natively through its COM interface. No subprocess to babysit, no stdout parsing, no hung pipes. Commands execute inside the server process on a dedicated MTA COM thread with an event pump — so the debugger can't drag the whole MCP server down with it.
- Kernel debugging is the primary use case, not an afterthought — full KDNET integration: attach to a running kernel, set breakpoints, step, run any WinDbg command while the target is halted, wait for events with hard timeouts, detect BSODs, and pass first-chance exceptions through so Windows keeps running normally. Execution-control commands (
g/t/p) are blocked inkd_executeso the LLM can't accidentally run away from a breakpoint — it has to use the explicitkd_continue/kd_steptools, which always return. - User-mode is covered too — Frida (with eternalized hooks for persistent instrumentation across sessions), dbgsrv for noninvasive process inspection with full WinDbg command access, and TTD (Time Travel Debugging) — all behind the same server.
- VM lifecycle is integrated with the debug session — snapshot restore cleanly tears down KD/Frida/dbgsrv before reverting the VM; guest commands, file transfer, and process control are all gated on VM power state so the agent never trips on "wrong state" errors.
vm_set_targetlets a running server switch to a different VM at runtime. - Designed for LLM agents — every tool has a hard timeout (nothing blocks forever), every error message explicitly tells the LLM what to do next, and
get_system_stategives the model a single "where am I?" snapshot on demand. AStateCoordinatorvalidates preconditions before every call so the agent gets useful feedback instead of silent failures.
An LLM connected to this server can autonomously:
- Control a VM — start, stop, pause, resume, snapshot, restore, screenshot
- Kernel debug — connect via KDNET, set breakpoints, step, execute any WinDbg command, wait for events
- Run commands in the guest — execute programs, transfer files, list/kill processes
- User-mode debug — attach Frida to hook functions, inspect processes via dbgsrv, record TTD traces
All without the LLM ever needing direct access to WinDbg, a terminal, or the VM itself.
| Requirement | Purpose |
|---|---|
| .NET 8 SDK | Build & run the server |
| VMware Workstation Pro | VM management (vmrun) |
| Windows Guest VM | Target for debugging |
| KDNET enabled in guest | Kernel debugging (see Setup) |
| frida-tools (optional) | User-mode instrumentation |
| WinDbg Preview (optional) | Provides dbgsrv.exe for remote user-mode debugging |
git clone https://github.com/memoryforensics1/windbg-mcp.git
cd windbg-mcp
dotnet build src/WinDbgMCP.Server/WinDbgMCP.Server.csprojEverything in appsettings.json is just a default — the LLM can change VM target, credentials, KDNET key, and more at runtime via vm_set_target. Expand only if you want to set a starting config.
Copy src/WinDbgMCP.Server/appsettings.example.json to appsettings.json and edit:
{
"Vm": {
"VmxPath": "C:\\path\\to\\your\\vm.vmx",
"VmrunPath": "C:\\Program Files (x86)\\VMware\\VMware Workstation\\vmrun.exe",
"VmPassword": "",
"GuestUsername": "YourUser",
"GuestPassword": "YourPass"
},
"KernelDebug": {
"Transport": "kdnet",
"Kdnet": {
"Port": 50000,
"Key": "your.kdnet.key.here"
},
"SymbolPath": "srv*C:\\Symbols*https://msdl.microsoft.com/download/symbols"
},
"Guest": {
"FridaPort": 27042,
"DbgsrvPort": 5064
}
}Claude Code (.mcp.json in project root):
{
"mcpServers": {
"windbg-mcp": {
"command": "dotnet",
"args": ["run", "--project", "C:\\path\\to\\windbg-mcp\\src\\WinDbgMCP.Server\\WinDbgMCP.Server.csproj"]
}
}
}Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"windbg-mcp": {
"command": "C:\\Program Files\\dotnet\\dotnet.exe",
"args": ["run", "--project", "C:\\path\\to\\windbg-mcp\\src\\WinDbgMCP.Server\\WinDbgMCP.Server.csproj"]
}
}
}The server starts automatically when your MCP client connects. It communicates over stdio.
# Or run standalone for testing
dotnet run --project src/WinDbgMCP.Server/WinDbgMCP.Server.csproj| Tool | Description |
|---|---|
get_system_state |
Full state overview — VM power, KD, guest ops, UMD. Always allowed. |
| Tool | Description |
|---|---|
vm_start |
Power on the VM |
vm_stop |
Shut down (graceful or hard) |
vm_pause |
Freeze entire VM |
vm_resume |
Unpause a paused VM |
vm_snapshot_restore |
Restore a named snapshot (debug sessions are cleanly torn down and can reconnect after) |
vm_snapshot_list |
List available snapshots |
vm_screenshot |
Capture VM display as PNG |
vm_set_target |
Switch the active VM target at runtime (VMX path + credentials) |
| Tool | Description |
|---|---|
kd_connect |
Attach to kernel via KDNET. Target breaks on connect. |
kd_disconnect |
Detach from kernel. Resumes target so VM keeps running. |
kd_break |
Halt running target (Ctrl+Break) |
kd_continue |
Resume target execution |
kd_step |
Step one instruction (into or over) |
kd_execute |
Run any WinDbg command (k, r, lm, !process 0 0, !analyze -v, etc.) |
kd_wait_for_event |
Wait for breakpoint/exception with timeout. Always returns. |
| Tool | Description |
|---|---|
guest_run_command |
Execute command in guest OS, capture stdout/stderr |
guest_transfer_to_vm |
Copy file from host to guest |
guest_transfer_from_vm |
Copy file from guest to host |
guest_list_processes |
List running processes with PIDs |
guest_kill_process |
Kill a process by PID |
| Tool | Description |
|---|---|
umd_frida_attach |
Attach Frida to a guest process |
umd_frida |
Inject JS, eval expressions, list processes, detach |
umd_frida_skill |
Frida best practices and API reference for LLMs |
umd_dbgsrv_connect |
Connect to remote dbgsrv in guest |
umd_dbgsrv_execute |
Attach to PID, run WinDbg commands, detach |
umd_dbgsrv_skill |
dbgsrv best practices and WinDbg command reference for LLMs |
umd_ttd |
Time Travel Debugging — record, stop, retrieve, list traces |
umd_ttd_query |
Query TTD traces (not yet implemented) |
get_system_state
kd_connect
kd_execute("lm") # list loaded modules
kd_execute("!process 0 0") # list all processes
kd_execute("vertarget") # target version info
kd_disconnect
kd_connect
kd_execute("bp nt!NtCreateFile") # set breakpoint
kd_continue # let target run
kd_wait_for_event(30) # wait up to 30s for hit
kd_execute("k") # show call stack
kd_execute("r") # show registers
kd_disconnect
guest_transfer_to_vm("MyDriver.sys", "C:\\Windows\\System32\\drivers\\MyDriver.sys")
guest_run_command("sc create MyDrv type= kernel binPath= C:\\Windows\\System32\\drivers\\MyDriver.sys")
guest_run_command("sc start MyDrv")
kd_connect
kd_execute("lm m MyDrv") # verify driver loaded
kd_execute("bp MyDrv!DriverEntry")
kd_disconnect
umd_frida_skill # read best practices first
umd_frida_attach(processName="target.exe")
umd_frida(action="eval", code="Process.enumerateModules().map(m=>m.name)")
umd_frida(action="inject", code="""
Interceptor.attach(Module.getExportByName('kernel32.dll','CreateFileW'), {
onEnter(args) { console.log('CreateFileW: ' + args[0].readUtf16String()); }
});
console.log('Hook installed');
""", timeoutSeconds=10)
umd_frida(action="detach")
umd_dbgsrv_skill # read best practices first
guest_run_command("start /b C:\\Tools\\DbgSrv\\dbgsrv.exe -t tcp:port=5064")
umd_dbgsrv_connect(vmIpAddress="192.168.x.x")
umd_dbgsrv_execute(action="attach", argument="<PID>")
umd_dbgsrv_execute(action="command", argument="lm")
umd_dbgsrv_execute(action="command", argument="!peb")
umd_dbgsrv_execute(action="command", argument="~*k")
umd_dbgsrv_execute(action="detach")
umd_dbgsrv_execute(action="disconnect")
kd_connect # connect after BSOD
kd_execute("!analyze -v") # automated crash analysis
kd_execute("k") # faulting stack
kd_execute("r") # registers at crash
kd_execute(".trap") # switch to trap frame
kd_disconnect
┌─────────────────────────────────────────────────────────────┐
│ MCP Client (LLM) │
│ Claude Code / Claude Desktop │
└──────────────────────────┬──────────────────────────────────┘
│ stdio (JSON-RPC)
┌──────────────────────────▼──────────────────────────────────┐
│ WinDbgMCP.Server │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ VM Tools │ │ KD Tools │ │ Guest Tools │ │
│ │ (vmrun) │ │ (DbgEng) │ │ (vmrun guest) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼───────┐ ┌──────▼───────┐ ┌──────▼───────────┐ │
│ │ VmwareManager│ │ DbgEngManager│ │ GuestExecManager │ │
│ └──────────────┘ └──────┬───────┘ └──────────────────┘ │
│ │ │
│ ┌────────────────────────▼─────────────────────────────┐ │
│ │ StateCoordinator │ │
│ │ (precondition gate — validates every tool call) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ UMD: Frida │ │ UMD: dbgsrv │ │ UMD: TTD │ │
│ │ (frida CLI) │ │ (DbgEng COM) │ │ (TTD.exe) │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Windows Guest VM │
│ (VMware Workstation Pro) │
│ │
│ KDNET (:50000) │ frida-server (:27042) │ dbgsrv (:5064)│
└─────────────────────────────────────────────────────────────┘
- Every tool validates preconditions —
StateCoordinatorchecks VM power, KD state, guest ops availability before any operation executes - Every operation has a timeout — no blocking calls, ever. The LLM never hangs.
- Error messages are prompts — every error tells the LLM exactly what to do next
- Execution-control commands are blocked —
g,t,pare blocked inkd_execute; usekd_continue/kd_stepinstead - DbgEng COM thread affinity — all COM calls marshaled to a dedicated MTA thread with an event pump
- BSOD detection — bugchecks are detected and handled differently from normal breakpoints
- Snapshot restore resets everything — KD, Frida, dbgsrv sessions are all cleaned up
In the guest VM (elevated cmd):
bcdedit /debug on
bcdedit /dbgsettings net hostip:<HOST_IP> port:50000 key:<YOUR_KEY>
shutdown /r /t 0Generate a key with kdnet.exe from the Windows SDK, or use any dotted-quad format key.
On the host:
pip install frida-toolsDownload frida-server-<version>-windows-x86_64.exe from Frida releases, deploy to guest as C:\Tools\frida-server.exe, and run:
frida-server.exe -l 0.0.0.0:27042Copy dbgsrv.exe, dbgeng.dll, and dbghelp.dll from WinDbg Preview (or Windows SDK) to the guest, then run:
dbgsrv.exe -t tcp:port=5064The host firewall must allow:
- UDP port 50000 inbound (KDNET)
- TCP port 27042 outbound (Frida)
- TCP port 5064 outbound (dbgsrv)
src/WinDbgMCP.Server/
├── Program.cs # Entry point, MCP server setup, DI
├── appsettings.json # Configuration (VM creds, KDNET, timeouts)
├── Configuration/
│ └── ServerConfig.cs # Typed configuration model
├── State/
│ ├── SystemState.cs # State model + enums
│ ├── StateCoordinator.cs # Precondition gate (heart of system)
│ ├── ErrorMessages.cs # LLM-friendly error catalog
│ └── ToolResult.cs # Result type
├── Vmware/
│ └── VmwareManager.cs # vmrun wrapper
├── KernelDebug/
│ ├── DbgEngThread.cs # Dedicated MTA thread for COM
│ ├── DbgEngManager.cs # Kernel debug session manager
│ ├── DebugEventCallbacks.cs # Breakpoint/exception/module events
│ ├── OutputCapture.cs # Command output capture
│ └── Interop/ # P/Invoke, constants
├── Guest/
│ └── GuestExecManager.cs # Guest command execution + file transfer
├── UserModeDebug/
│ ├── FridaManager.cs # Frida CLI wrapper
│ ├── DbgsrvManager.cs # Remote user-mode debugging via dbgsrv
│ └── TtdManager.cs # Time Travel Debugging
└── Tools/
├── VmTools.cs # vm_* tools
├── KernelDebugTools.cs # kd_* tools
├── GuestTools.cs # guest_* tools
├── UserModeDebugTools.cs # umd_* tools
└── MetaTools.cs # get_system_state
src/WinDbgMCP.Tests/ # Unit tests (126 tests)
dotnet test src/WinDbgMCP.Tests/WinDbgMCP.Tests.csproj| Component | Technology |
|---|---|
| Runtime | .NET 8 (C#) |
| MCP SDK | ModelContextProtocol 0.1.0-preview.12 |
| DbgEng | Native COM interop (dbgeng.dll) |
| VM Control | VMware vmrun CLI |
| User-Mode Hooking | Frida (frida-tools Python CLI) |
| Remote Debugging | dbgsrv.exe (WinDbg component) |
| TTD | TTD.exe (Time Travel Debugging) |
MIT