
모든 pybag Windows 디버거 기능을 네이티브 MCP 도구로 전환하는 MCP(Model Context Protocol) 서버입니다. MCP 호환 클라이언트(Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor 및 사용자 지정 에이전트)가 구조화된 JSON 호출을 통해 사용자 모드 프로세스, 커널 세션 및 크래시 덤프 분석을 제어할 수 있게 해줍니다.
MCP (Model Context Protocol) 서버로, 모든 pybag Windows 디버거 함수를 네이티브 MCP 도구로 노출합니다. 모든 MCP 호환 클라이언트(Claude Desktop, Claude Code, Cowork, OpenAI Codex CLI, Cursor 및 사용자 정의 에이전트)에게 사용자 모드 프로세스, 커널 세션 및 크래시 덤프 분석에 대한 완전한 제어를 제공합니다. 모든 것은 타입화된 도구 호출과 구조화된 JSON 응답을 통해 이루어집니다.
git clone https://github.com/your-username/windbg-mcp.git cd windbg-mcp
### 2. Python 종속성 설치```bat
pip install pybag mcp
Windows SDK를 다운로드하고 설치 중 Debugging Tools for Windows를 선택하세요: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
서버는 로컬 stdio 프로세스로 실행됩니다. 아래의 모든 클라이언트는 동일한 방식으로 실행됩니다 —
python <path-to>/windbg_mcp.py — 하지만 각자 고유한 구성 형식을 가지고 있습니다.
Claude Desktop 구성 파일을 편집하고 windbg-mcp 항목을 추가하세요:
구성 파일 위치:
%APPDATA%\Claude\claude_desktop_config.jsonClaude Desktop를 다시 시작하세요. 55개의 모든 디버거 도구가 자동으로 나타납니다.
---
### Claude Code (CLI)
다음 명령을 한 번 실행하여 서버를 등록하세요. Claude Code는 항목을 자체 MCP config에 저장하고 이후 모든 세션에서 도구를 사용할 수 있게 합니다.```bash
claude mcp add windbg-mcp python C:\path\to\windbg-mcp\windbg_mcp.py
서버가 등록되었는지 확인하려면:```bash claude mcp list
나중에 제거하려면:```bash
claude mcp remove windbg-mcp
Claude Cowork에 WinDbg MCP를 추가하는 방법은 두 가지입니다: JSON 구성(빠른 방법) 또는 .mcpb 플러그인 번들(이식 가능, 공유 가능)로 설치하는 방법입니다.
3. 저장하고 Cowork를 다시 시작하세요. 도구들은 다음 세션에서 사용할 수 있습니다.
#### 옵션 B — `.mcpb` 플러그인 번들로 설치
`.mcpb` 파일은 Cowork가 직접 설치할 수 있는 플러그인 디렉터리의 zip 아카이브입니다. 이는 팀이나 여러 머신 간에 서버를 공유할 때 권장되는 방법입니다.
**1단계 — `.mcpb` 파일 빌드**
클론된 저장소의 루트에서 다음을 실행하십시오:```bat
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단계 — Cowork에 설치
windbg-mcp.mcpb를 선택합니다.manifest.json을 읽고, MCP 서버를 등록하며, 모든 도구를 즉시 사용할 수 있도록 합니다. 수동 경로 구성이 필요하지 않습니다.이 리포지토리에 번들링된 manifest.json은 이미 올바르게 구성되어 있습니다:```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가 번들을 푼
디렉터리로 확인되므로 경로를 하드코딩할 필요가 없습니다.
---
### OpenAI Codex CLI
Codex CLI 구성 파일에 서버를 추가하십시오. 파일은 일반적으로 다음 위치에 있습니다.
`~/.codex/config.json` (Linux/macOS) 또는 `%USERPROFILE%\.codex\config.json` (Windows).```json
{
"mcpServers": {
"windbg-mcp": {
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
}
저장한 후 새로운 Codex 세션을 시작하세요. WinDbg 도구를 모델이 호출할 수 있게 됩니다.
4. 저장합니다. Cursor는 다음 Composer 세션에서 서버에 연결됩니다.
---
### Continue.dev
다음 내용을 `~/.continue/config.json` (또는 작업공간 레벨의 `.continue/config.json`)에 추가하십시오:```json
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "stdio",
"command": "python",
"args": ["C:\\path\\to\\windbg-mcp\\windbg_mcp.py"]
}
}
]
}
}
Continue 확장 프로그램을 다시 로드하세요. 도구 목록에 55개의 디버거 도구가 나타납니다.
자체 에이전트 또는 자동화 파이프라인을 구축하는 경우, 표준 MCP stdio 전송을 통해 WinDbg MCP에 연결하세요. 서버는 stdin/stdout을 통해 JSON-RPC 2.0으로 통신합니다.
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 (`@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 (언어 독립적)
서버는 개행으로 구분된 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\", ...}"}]}}
create — 디버거 하에서 새 프로세스를 시작합니다. initial_break=True(기본값)로 설정하면 프로세스 진입점에서 중단됩니다.
attach — 실행 중인 프로세스에 연결합니다. pid(정수) 또는 name(프로세스 파일 이름) 중 하나를 제공하세요. 둘 다 제공하지 마세요.
kernel_attach — 원격 커널 디버거에 연결합니다. connect_string은 KD 구문을 사용합니다. 예: "net:port=55000,key=1.2.3.4".
load_dump — 사후 분석을 위해 .dmp 파일을 엽니다. 충돌 주소와 가장 가까운 심볼을 즉시 반환합니다.
connect — 원격 사용자 모드 디버깅을 위해 프로세스 서버에 연결합니다. options은 DbgEng 연결 구문을 사용합니다. 예: "tcp:server=192.168.1.10,port=5555".
go — 실행을 재개하고 다음 디버그 이벤트(중단점, 예외 또는 시간 초과)까지 차단합니다. 실행 중 수집된 새 RIP와 모든 캡처를 반환합니다.
step_into — 다음 명령어로 한 단계씩 들어가며, 호출된 함수 내부로 따라갑니다.
step_over — 다음 명령어를 한 단계씩 실행하되, 호출은 단일 단계로 처리합니다.
step_out — 현재 함수가 반환될 때까지 실행합니다.
goto — 특정 심볼 또는 16진수 주소에 도달할 때까지 실행합니다. 예: "Kernel32!ExitProcess" 또는 "0x7fff12340000".
trace — N번의 단일 단계 반복을 수행하고 방문한 각 명령어를 기록합니다.
bp — 심볼 또는 주소에 소프트웨어(코드) 중단점을 설정합니다.
expr: 심볼("ntdll!NtCreateFile") 또는 16진수 주소("0x7ff800001234")capture: true(기본값)인 경우 이 중단점이 발생할 때마다 레지스터, 스택, 메모리 등 전체 상태를 자동으로 캡처 버퍼에 저장합니다.action: "go"(기본값)는 캡처 후 실행을 계속하고, "break"는 중단합니다.oneshot: 중단점이 한 번 발생하면 제거합니다.passcount: 해당 위치를 N번 통과한 후에만 발생합니다.hw_bp — 하드웨어/데이터 중단점(워치포인트)을 설정합니다.
addr: 감시할 16진수 주소size: 감시 폭(바이트 단위) — 1, 2, 4, 또는 8(기본값 4)access: "e" 실행, "w" 쓰기(기본값), "r" 읽기/쓰기capture, action, oneshot: bp와 동일한 의미list_bps — 현재 활성화된 모든 중단점을 해당 ID, 표현식, 유형 및 설정과 함께 반환합니다.
remove_bp / enable_bp / disable_bp — bp 또는 hw_bp에서 반환된 id로 중단점을 관리합니다.
capture: true(기본값)인 중단점은 발생할 때마다 전체 디버거 스냅샷을 자동으로 저장합니다. 스냅샷에는 모든 레지스터, 호출 스택, RSP의 스택 메모리 64바이트, RIP의 코드 32바이트가 포함됩니다. 스냅샷은 버퍼에 누적되며 get_captures로 언제든지 검색할 수 있습니다.
get_captures — 마지막 clear_captures 이후 수집된 모든 캡처를 반환합니다. 각 캡처에는 다음이 포함됩니다:
registers — 모든 레지스터 값을 {name: "0x..."} 16진수 문자열로 나타냅니다.rip — 캡처 시점의 명령어 포인터symbol_at_rip — RIP에 가장 가까운 심볼instruction — RIP에 있는 명령어의 디스어셈블리stack — 주소와 반환 주소가 포함된 상위 10개 호출 스택 프레임context_memory.stack_at_rsp — RSP의 64바이트를 16진수, 포맷, ASCII로 표시context_memory.code_at_rip — RIP의 32바이트를 16진수 및 포맷으로 표시clear_captures — 캡처 버퍼를 지웁니다. 새 실행을 시작하기 전에 유용합니다.
capture_state — 현재 상태의 즉각적인 온디맨드 스냅샷을 찍습니다. 중단점이 발생할 때까지 기다리지 않고 이미 중단된 상태에서 사용하세요.
read_mem — addr에서 size 바이트의 원시 데이터를 읽습니다. 데이터를 hex(압축), formatted(공백으로 구분된 바이트), ascii(인쇄 가능한 문자, 인쇄 불가능 문자는 .)로 반환합니다.
write_mem — 메모리에 바이트를 씁니다. data는 16진수 문자열입니다. 공백과 \x 접두사는 자동으로 제거됩니다. 예: "90909090", "\\x90\\x90\\x90\\x90", "90 90 90 90".
read_ptr — addr에서 시작하여 count개의 연속적인 포인터 크기 값(32비트에서는 4바이트, 64비트에서는 8바이트)을 읽습니다.
poi — addr의 단일 포인터를 역참조합니다(관심 포인터).
read_str — null로 종료되는 문자열을 읽습니다. UTF-16LE(Windows WCHAR)의 경우 wide=true로 설정합니다.
dump_mem — 포맷된 dword/포인터 덤프로, WinDbg의 dd/dp와 동일합니다.
mem_info — addr을 포함하는 페이지의 메모리 영역 속성(기준 주소, 크기, 유형, 상태, 보호 플래그)을 반환합니다.
mem_list — 대상 프로세스 주소 공간의 모든 가상 메모리 영역을 나열합니다.
get_regs — 사용 가능한 모든 레지스터를 {name: "0x..."} 형태로 반환합니다. 정확한 집합은 대상 아키텍처(x86 vs x64)에 따라 다릅니다.
get_reg — 단일 레지스터를 반환합니다. 예: name="rax", name="eflags".
set_reg — 레지스터를 덮어씁니다. value는 16진수 문자열("0x1234") 또는 10진수 정수 문자열을 허용합니다.
get_pc — 심볼 확인과 해당 주소의 디코딩된 명령어 텍스트를 포함한 명령어 포인터를 반환합니다.
get_sp — 현재 스택 포인터 값을 반환합니다.
resolve — 심볼 이름을 가상 주소로 확인합니다. Module!Function 형식을 사용하세요. 예: "Kernel32!WriteFile", "ntdll!NtCreateFile".
find_symbols — 와일드카드 심볼 검색입니다. 예: "ntdll!*Alloc*", "kernel32!*File*". 일치하는 모든 심볼 문자열을 반환합니다.
addr_to_symbol — 가상 주소를 가장 가까운 심볼 이름으로 역확인합니다.
disasm — addr에서 시작하는 count개의 명령어를 디스어셈블합니다. 주소가 제공되지 않으면 기본적으로 현재 RIP를 사용합니다.
whereami — 주어진 주소에서 모듈, 함수 및 오프셋에 대한 사람이 읽을 수 있는 설명을 반환합니다.
list_modules — 대상에 로드된 모든 모듈을 기준 주소와 크기와 함께 나열합니다.
module_info — 특정 모듈(예: "kernel32.dll", "ntdll.dll")의 진입점과 섹션 목록(이름, 가상 주소, 크기)을 반환합니다.
get_exports — 모듈의 전체 내보내기 테이블을 문자열 목록으로 반환합니다.
get_imports — 모듈의 전체 가져오기 테이블을 문자열 목록으로 반환합니다.
list_threads — 대상 프로세스의 모든 스레드를 나열합니다.
get_thread — 현재 활성 스레드 컨텍스트를 반환합니다.
set_thread — 스레드 ID(list_threads에서 얻은)로 활성 스레드 컨텍스트를 전환합니다.
get_stack — 호출 스택을 구조화된 데이터로 반환합니다. 각 프레임에는 명령어 주소, 반환 주소, 프레임 포인터가 포함됩니다.
get_teb — 현재 스레드의 스레드 환경 블록(TEB) 주소를 반환합니다.
get_peb — 프로세스 환경 블록(PEB) 주소를 반환합니다.
get_handles — 대상 프로세스의 모든 열린 핸들을 나열합니다.
get_bitness — 대상 아키텍처에 따라 32 또는 64를 반환합니다.
raw — WinDbg 명령어 문자열을 실행하고 출력을 텍스트로 반환합니다. 다른 도구로 처리되지 않는 모든 작업에 대한 탈출구로 사용하세요.```
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. 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
### 원격 커널 디버깅```
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
---
## 팁
**심볼 경로** — 심볼 해석 결과가 없으면 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` 매개변수는 16진수 문자열(`"0x1234abcd"`, `"7fff12340000"`) 또는 일반 정수를 허용합니다. 16진수 값의 경우 `0x` 접두사는 선택사항입니다.
**셸코드 검증** — 캡처 후에는 셸코드가 위치해야 하는 주소에서 `read_mem` 및 `disasm`을 사용하세요. `disasm`이 의도한 명령어를 표시하면 페이로드가 온전히 도착한 것입니다.
**`terminate` 또는 `detach` 이후** — 모든 캡처와 중단점이 자동으로 지워집니다. 새 세션을 시작하려면 `create` 또는 `attach`를 호출하세요.
**`capture_state`와 `get_captures` 비교** — 중단점에서 이미 멈춰 있을 때 주문형 스냅샷을 얻으려면 `capture_state`를 사용하세요. `go` 호출 중에 중단점이 발생할 때마다 자동으로 저장된 상태를 검색하려면 `get_captures`를 사용하세요.
**커널 `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
MIT
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
go | timeout (ms, 기본값 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} |
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
read_mem | addr (필수), size (기본값 16) | {addr, size, hex, formatted, ascii} |
write_mem | addr (필수), data (필수, 16진수 문자열) | {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] |
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
list_modules | — | [{name, base, size}] |
module_info | name (필수) | {name, entry_point, sections} |
get_exports | name (필수) | [export_strings] |
get_imports | name (필수) | [import_strings] |
| 도구 | 매개변수 | 반환값 |
|---|
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} |
| 도구 | 매개변수 | 반환값 |
|---|
get_handles | — | [handle_description_strings] |
get_bitness | — | {bits} |
raw | cmd (필수) | {output} |