phantomscope-mcp
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
phantomscope
DMA-driven reverse engineering. Static PE analysis, live memory inspection over MemProcFS, snapshot emulation, and an MCP server that exposes all of it to an LLM agent.
What this is
DMA hardware (a PCILeech FPGA, or a memory dump) gives you read/write access to physical memory — and nothing else. No thread suspension, no register context, no single step, no breakpoints. phantomscope is built around that constraint: it splits "understand and change a target" into four engines, each honest about what it can actually do, and exposes the whole thing as 74 MCP tools so an agent can drive it.
It also runs with no hardware at all — the mock backend is a deterministic
synthetic target and the physical backend reads raw dumps, so the full pipeline
is testable in CI.
Related MCP server: Antardrishti
What this is not
Not a debugger. You cannot break on a live DMA target. The emulator is the substitute: capture the function and its data, then run it deterministically.
Not an injection tool. It does not inject DLLs, load a driver, or execute new code in the target.
writechanges bytes that are already there, and every write is journaled and reversible.Not a kernel-structure parser. The
physicalbackend deliberately assumes no OS: it finds PEs by header structure, not by walkingPsActiveProcessHead.
Authorized use only. Reading another machine's memory over DMA is illegal in most jurisdictions without written permission. Use phantomscope on systems you own or are explicitly engaged to test.
Contents
Architecture
+----------------------- MCP client (agent) -----------------------+
| session_* processes modules read scan_* emu_* ... |
+---------------------------------+--------------------------------+
| stdio / JSON-RPC
+---------------------------------v--------------------------------+
| phantomscope |
| |
| +----------------+ +----------------+ +----------------+ |
| | StaticEngine | | DynamicEngine | | Emulator | |
| | pefile | | scans | | unicorn | |
| | capstone | | patches | | step | |
| | xrefs, structs | | ptr chains | | watchpoints | |
| +-------+--------+ +-------+--------+ +-------+--------+ |
| | | | |
| +--------------------+--------------------+ |
| | |
| +-------v-------+ |
| | MemoryBackend | |
| +-------+-------+ |
| | |
| | | | |
| +--------------------+--------------------+ |
| MemProcFS physical mock |
+------------+--------------------+--------------------+-----------+
| | |
v v v
PCILeech FPGA raw dump / pmem demo targetWhy four engines
DMA cannot stop a thread, read a register context or single-step. So a classic debugger is not available, and phantomscope replaces each of its jobs:
Need | Engine | What it does |
Understand a module | Static | The bytes captured over DMA are a complete PE image; parse, disassemble, index, infer structs locally |
Find a value / structure | Dynamic | Value scans, AOB scans, narrowing, pointer chains, snapshot diffs |
Change something | Dynamic | Journaled writes with one-call restore |
"Debug" a function | Emulator | Snapshot code + data, run it in Unicorn, use watchpoints and API hooks as breakpoints |
No OS at all | Physical | Raw dump: physical addressing, page-table walking, PE discovery by structure |
That last row is the honest substitute for breakpoints: you cannot break on a live DMA target, but you can capture the function, its arguments and its data, then emulate it deterministically — as many times as you like.
Install
git clone https://github.com/Absurdity233/phantomscope
cd phantomscope
python -m venv .venv# Windows — the only platform MemProcFS supports
.\.venv\Scripts\python.exe -m pip install -e ".[dev,emulate]"# Linux / macOS — mock, static and physical layers work; the DMA backend does not
.venv/bin/python -m pip install -e ".[dev,emulate]"memprocfs ships the vmm.dll runtime and installs on Windows only.
unicorn is optional: without it everything except the emu_* tools still
works, and those report unavailable instead of failing.
Quick start (no hardware)
.\.venv\Scripts\phantomscope.exe demoThe demo builds a synthetic target — a mapped PE module, a heap struct with health/ammo/score, two threads with callstacks, symbols, handles — and walks the whole pipeline: enumerate, read, scan, patch, restore, capture the module, disassemble it, and emulate the entry point.
Live workflow
First, sanity-check the device and time every backend operation:
# against a PCILeech FPGA
.\.venv\Scripts\python.exe tools\probe_backend.py fpga
# or an offline dump
.\.venv\Scripts\python.exe tools\probe_backend.py C:\dumps\win10.rawprobe_backend.py opens the device, then reports the latency of every backend
operation (processes, modules, regions, threads, callstack, a module read,
symbolisation, kernel modules, physical map, handles). Run it before blaming the
toolkit when a tool call feels slow.
Typical numbers on a PCILeech FPGA: the first Vmm() initialisation takes
20–40 s while the device is probed and the target's process list is built; after
that list_processes() is sub-second. If the target is asleep or DMA reads
stall, MemProcFS blocks instead of returning an error — that is a device
state problem, not a phantomscope bug, and probe_backend.py shows exactly which
call is stuck.
Then start the server and drive it from an MCP client:
phantomscope-mcp// 1. open a session against the FPGA
{"tool": "session_open", "arguments": {"device": "fpga"}}
// 2. find and attach to a process
{"tool": "attach", "arguments": {"process": "target.exe"}}
// 3. look around
{"tool": "modules", "arguments": {}}
{"tool": "regions", "arguments": {}}
{"tool": "threads", "arguments": {}}
{"tool": "callstack", "arguments": {"tid": 1234}}
{"tool": "handles", "arguments": {"filter": "File"}}
// 4. read a struct
{"tool": "read_typed", "arguments": {"va": "0x21A00000120", "value_type": "int32"}}
// 5. find it yourself
{"tool": "scan_value", "arguments": {"value": "100", "value_type": "int32", "label": "health"}}
{"tool": "scan_next", "arguments": {"key": "health", "value": "95"}}
// 6. change it (journaled) and undo
{"tool": "write", "arguments": {"va": "0x21A00000120", "value": "250", "value_type": "int32"}}
{"tool": "patch_restore", "arguments": {"all": true}}
// 7. hand the live module to the static engine
{"tool": "capture_module", "arguments": {"module": "target.exe", "key": "target"}}
{"tool": "functions", "arguments": {"key": "target", "limit": 50}}
{"tool": "disassemble", "arguments": {"key": "target", "va": "0x7FF600001000", "function": true}}
{"tool": "xrefs_to", "arguments": {"key": "target", "va": "0x7FF600001040"}}
{"tool": "struct_layout", "arguments": {"key": "target", "va": "0x7FF600001040"}}
// 8. emulate a function out of the snapshot
{"tool": "emu_create", "arguments": {"key": "target"}}
{"tool": "emu_call", "arguments": {"key": "target", "va": "0x7FF600001040", "args": "0x21A00000120"}}
{"tool": "emu_events", "arguments": {"key": "target"}}Sessions
Each session owns its backend, static engine and emulators. Open a live FPGA and an offline dump at the same time and their images, scans and emulators never collide:
{"tool": "session_open", "arguments": {"name": "live", "device": "fpga"}}
{"tool": "session_open", "arguments": {"name": "disk", "device": "C:/dumps/win10.raw"}}
{"tool": "images", "arguments": {"session": "live"}}Every static and emulation tool accepts an optional session. Without one, a
bare image key is resolved against whichever engine owns it, and reusing a key
across sessions is refused rather than silently overwriting. session_close
drops that session's images and emulators.
Static-only work (load_file with no session) lands in a standalone engine that
outlives any session, so phantomscope is usable as a plain PE analyser.
Physical layer (no operating system)
Some targets have no usable OS view: a dump from a hibernated machine, a
pre-boot capture, a system whose kernel structures are corrupted, or a device
that only gives you physical memory. The physical backend assumes nothing:
# build a synthetic dump to try it without hardware
python tools\make_demo_dump.py demo_phys.raw
phantomscope physical demo_phys.raw --capture 0x100000{"tool": "session_open", "arguments": {"backend": "physical", "device": "demo_phys.raw"}}
{"tool": "physical_info", "arguments": {}}
{"tool": "physical_read", "arguments": {"pa": "0x100000", "size": 64}}
{"tool": "physical_images", "arguments": {}} // PEs found by structure alone
{"tool": "physical_scan", "arguments": {"pattern": "48 83 ec 28"}}
{"tool": "translate", "arguments": {"va": "0x7ff600001000", "cr3": "0x200000"}}
{"tool": "virtual_read", "arguments": {"va": "0x7ff600001000", "size": 64, "cr3": "0x200000"}}
{"tool": "walk", "arguments": {"va": "0x7ff600000000", "size": 4096, "cr3": "0x200000"}}
{"tool": "capture_image", "arguments": {"pa": "0x100000", "key": "phys"}}
{"tool": "disassemble", "arguments": {"key": "phys", "count": 8}}What the layer provides:
Piece | File | Notes |
|
| Sparse segments ( |
|
| 4- and 5-level paging, 4 KiB / 2 MiB / 1 GiB pages, present-bit aware |
|
| Page-aligned |
|
| Implements |
Two honest limitations: a physical image has no process, module, thread or
symbol list, and capture_image bases the image at its PE ImageBase rather
than its physical address, because RVAs are relative to the load address.
Tool reference
74 tools. Every dynamic tool needs a session; every static tool needs a loaded
image key (images lists them).
Session
session_open, session_list, session_close, attach, server_info
Process / module / thread
processes, process_info, modules, regions, threads, callstack,
address_info
address_info resolves a VA to its module, region, protection and symbol.
Kernel / handles / heaps
kernel_modules, kernel_processes, handles, heaps
handles filters on type or name. kernel_processes currently delegates to the
user-mode list — a hook for a real kernel view, not one yet.
Memory
read, read_typed, write, patch_list, patch_restore, dump_region
Every write is journaled and reversible; patch_restore(all=true) undoes the
whole session.
Discovery
scan_aob, scan_value, scan_next, scan_results, pointer_scan,
snapshot, diff, watch
scan_value → scan_next is the narrowing loop: scan once, let the target
change, then keep only the addresses whose value still matches.
Static
load_file, capture_module, images, unload_image, image_summary,
sections, imports, exports, strings, functions, function_info,
disassemble, basic_blocks, xrefs_to, xrefs_from, callers, callees,
scan_pattern, rtti, static_read, static_address_info,
struct_layout, globals_touched
struct_layout turns every [rcx+0x10] in a function into a field hypothesis —
offset, width, read/write, and the instructions that produced it. It reports
what the code touches, not what the struct contains, and the base is a role
(arg0, arg1, …) because nothing proves which object a register holds.
Emulation
emu_available, emu_create, emu_regs, emu_step, emu_run, emu_read,
emu_write, emu_watch, emu_events, emu_call
emu_call runs a function with the x64 Microsoft calling convention and stops at
a return trap, so you get a retval without a debugger.
Physical (no OS)
physical_info, physical_map, physical_read, physical_write,
translate, virtual_read, virtual_write, walk, physical_images,
physical_scan, capture_image
CLI
phantomscope demo # hardware-free end-to-end walkthrough
phantomscope static notepad.exe # PE summary
phantomscope functions notepad.exe # discovered functions
phantomscope disasm notepad.exe 0x1400019c0 --count 20
phantomscope strings notepad.exe --contains password
phantomscope scan notepad.exe "48 8B ?? E8"
phantomscope physical dump.raw --capture 0x100000 # raw dump, no OS
phantomscope mcp # MCP server on stdioThe CLI builds the same engines the MCP server uses, so anything reachable there is reachable here.
Registering the MCP server
A project-scoped .mcp.json is included; point its command at
your checkout. To add it to your user scope instead:
claude mcp add phantomscope --scope user -- <checkout>\.venv\Scripts\phantomscope-mcp.exeOr add it by hand to mcpServers in ~/.claude.json:
"phantomscope": {
"type": "stdio",
"command": "<checkout>\\.venv\\Scripts\\phantomscope-mcp.exe",
"args": [],
"env": {}
}Layout
src/phantomscope/
backend.py MemoryBackend contract + capabilities
backends/
memprocfs_backend.py PCILeech FPGA / dumps / pmem / LeechAgent
mock.py deterministic in-memory target
demo.py synthetic PE + process used by tests and `demo`
factory.py create_backend()
analysis/
pe.py mapped PE image (file or DMA capture)
disasm.py capstone wrapper, normalised instructions
index.py functions, basic blocks, xrefs, call graph
scan.py strings, AOB patterns, RTTI/vtables
structs.py struct layout inference from memory operands
engine.py StaticEngine facade
dynamic/
engine.py DynamicEngine: reads, patches, scans, diffs
emulator.py unicorn snapshot emulator
session.py backend + engines, named sessions
physical/
image.py sparse raw physical image
paging.py x86-64 page-table walking
pescan.py PE discovery by structure alone
backend.py MemoryBackend over physical memory
mcp_server.py MCP tools (74)
cli.py command line front end
tests/ 137 tests, no hardware required
tools/probe_backend.py live device sanity check with per-step timings
tools/make_demo_dump.py synthetic raw dump for the physical layerDesign notes
Mapped images everywhere. A PE from disk and a module captured over DMA both become an RVA-indexed buffer, so every read, scan and disassembly path is identical for both.
capture_moduleis the bridge between the two engines.Bounded work. Function discovery is capped by
max_instructions(1.5M), scans byMAX_SCAN_BYTES(512 MB) and emulation by a step budget, so a tool call cannot hang a session.Every write is reversible.
writerecords the original bytes;patch_listandpatch_restoregive you a full journal.Honest capability reporting.
MemoryBackend.capsand theUnavailableerror make it explicit when a backend cannot provide something (for example callstacks on a raw dump) instead of returning plausible-looking zeros.Per-session state. Each session owns its static engine and emulators, so two targets in one server cannot overwrite each other's images or scans.
Limitations
No breakpoints on a live target. Emulate a snapshot instead.
struct_layoutis a hypothesis, not a type. Untouched fields are invisible, and overlapping accesses are reported rather than merged.The physical layer does not parse kernel structures. It finds PEs by header and translates VAs given a CR3; it will not hand you a process list.
Function discovery is heuristic. Recursive descent plus a linear sweep and prologue scanning; heavily virtualised or obfuscated code will be incomplete.
DMA reads can block. If the target sleeps or the link stalls, MemProcFS blocks instead of erroring — use
probe_backend.pyto find the stuck call.The live FPGA path is not covered by CI. Tests run against the mock and synthetic dumps; hardware paths need a device.
Tests
.\.venv\Scripts\python.exe -m pytest tests -q
.\.venv\Scripts\python.exe tests\mcp_smoke.py # real stdio MCP round tripEmulator tests are skipped automatically when unicorn is not installed.
Development
To add a tool: implement it in the relevant engine, wrap it in mcp_server.py
with @server.tool(), and add the name to
tests/test_mcp.py::test_tool_registry_is_complete.
License
MIT — see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Persistent memory, hybrid search and a goal graph for AI agents, over stdio or remote HTTP.
Code intelligence platform for AI agents. 20 tools for architecture, security & impact analysis.
Your personal library, readable by AI agents: durable snapshots, hybrid search, 46 MCP tools.
30 security tools for AI agents: phishing links, exposure maps, DNS, SSL, PQC, history, pivots.
Related MCP Servers
- AlicenseBqualityCmaintenanceEnables AI coding agents to reverse engineer an operating system from its ISO and rebuild it component by component, exposing 39 tools over stdio for ISO fingerprinting, sandboxed QEMU VM lifecycle, PE analysis, API-surface dumps, syscall tracing, behavior diffs, component scaffolding, compat shims, app-compat testing, and knowledge-base search. It lets an agent survey a reference OS, generate clean-room specs and code, boot the rebuild, and test real applications against it.39MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to locally analyze and reverse engineer binaries through a compact 12-tool MCP interface for triage, function discovery, disassembly/decompilation, cross-references, strings/imports, call graphs, memory reads, annotations, and binary diffs.1MIT
- AlicenseBqualityCmaintenanceEnables agents to drive an end-to-end reverse-engineering loop on protected binaries: launch and dump live processes, rebuild memory images into file-aligned PEs with repaired imports, map Delphi RTTI methods, scan strings/entropy/xrefs, emulate code, and decompile through Ghidra or .NET. It also supports scripted live x64 and WOW64 debugging with breakpoints, registers, and module dumps exposed as callable tools.351MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI coding agents to inspect and reverse-engineer large binaries through bounded stdio JSON-RPC 2.0 tools for graph queries, SSA facts, path solving, runtime grounding, and mod scaffolding.1AGPL 3.0