小橙子 b5e62e3251
fix(reverse-engineering): only ship 3 end-to-end verified MCP servers (live-smoked) + smoke script (#32)
* fix(reverse-engineering): declare missing tool-binary prereqs for 4 servers

Real-world repro on a fresh Win11 machine showed 4 servers fail in ways
the desktop one-click install couldn't surface, because their declared
`prerequisites[]` only covered the runner (uvx / java) and not the
underlying tool binary the runner needs at startup.

Smoke from a clean machine (uvx just installed, no other tools):

  Before this PR
  --------------
  ghidra     ⚠️  spawned, no JSON-RPC response in 5s   (uv pkg slow first start)
  radare2     prereq missing: radare2                  ← already correct
  gdb         prereq missing: gdb                      ← already correct
  lldb        process exited (code=1)                  ← cause invisible
  jadx       ⚠️  spawned, no JSON-RPC response in 5s    ← cause invisible
  apktool     process exited (code=1)                  ← cause invisible
  frida       process exited (code=1)                  ← cause invisible

  After this PR (same machine, same tools)
  ----------------------------------------
  ghidra     ⚠️  spawned, no JSON-RPC response in 5s   (unchanged — Ghidra is a GUI binary configured via GHIDRA_INSTALL_DIR, not a PATH command)
  radare2     prereq missing: radare2
  gdb         prereq missing: gdb
  lldb        prereq missing: lldb                    ← now actionable
  jadx        prereq missing: jadx                    ← now actionable
  apktool     prereq missing: apktool                 ← now actionable
  frida       prereq missing: frida                   ← now actionable

  → All actionable failures now route through the existing
    `PluginPrerequisitesModal` one-click install flow with per-platform
    install commands, instead of letting the server crash mid-startup.

What this PR adds (servers.json)

- lldb     prereq adds `lldb`     → win32 LLVM (winget/scoop), macOS xcode-select / brew, linux apt/dnf
- jadx     prereq adds `jadx`     → win32 scoop, darwin brew, linux apt/snap
- apktool  prereq adds `apktool`  → win32 scoop, darwin brew, linux apt/snap
- frida    prereq adds `frida`    → uv tool / pipx / pip / brew (frida-tools is a Python pkg providing the `frida` CLI)

Plus a sibling `scripts/smoke-reverse-engineering-mcps.ts` that:

- reads the same plugin's `servers.json`
- probes each prereq via `where` / `command -v` (same primitive as
  the desktop's `prerequisitesService`)
- if all prereqs pass, spawns the server and sends an LSP-framed
  JSON-RPC `initialize` request, waits 5 s for the response
- prints a status matrix + auto-generated install commands per
  platform (sourced from servers.json itself, not duplicated)
- flags schema gaps where a server name implies a tool that's NOT
  in its prereq list (e.g. catches future regressions of this PR's
  fix, plus the existing ghidra/Ghidra-binary case is correctly
  excluded since Ghidra is GUI/env-var driven)

Why a smoke script in the repo

The existing `scripts/dev-mcp-test.ps1` is **only** the chrome-devtools
browser-MCP environment launcher (Vite proxy + H5 token) — not an
RE-plugin smoke. There was no equivalent reverse-engineering smoke,
so each maintainer had to reproduce by hand. With this script, future
"is the RE plugin healthy?" questions are one `bun run` away.

Plugin version: 0.4.3 → 0.4.4

Verification

- Manually ran `bun run scripts/smoke-reverse-engineering-mcps.ts`
  on a fresh Win11 26200 with uv 0.11.21 just installed:
  → 6/7 cleanly classified as `prereq missing`, 1/7 (ghidra) gets
    no response (expected — it needs `GHIDRA_INSTALL_DIR` to point
    at a user-installed Ghidra binary; not a PATH command).
- The script's schema-gap heuristic correctly flags zero remaining
  gaps after this PR.

Tested: live smoke on a real machine; before/after diff above.
Not-tested: macOS / Linux paths (only Win32 install map exercised).
The macOS/Linux paths are direct mirrors of the Win32 ones using the
relevant native package managers, sourced from each tool's official
install docs.

Confidence: high
Scope-risk: narrow

* fix(reverse-engineering): only ship 3 end-to-end verified MCP servers

Live smoke on a fresh Win11 26200 + an HTTP proxy showed that **4 of
the 7 MCP servers in this plugin cannot be made to start** under any
reachable upstream configuration:

| Server  | Upstream tried                                       | Failure mode |
|---------|------------------------------------------------------|---|
| radare2 | npm @radareorg/radare2-mcp; drvcvt fork; r2 official | npm 404; drvcvt has no `dist/`; official is C/Meson requiring compile |
| lldb    | stass/lldb-mcp; stableversion/lldb_mcp               | both upstream are single .py with no pyproject.toml |
| jadx    | zinja-coder/jadx-mcp-server; mseep-jadx PyPI         | upstream raises `ModuleNotFoundError: 'src'`; PyPI republish is 0-byte placeholder |
| apktool | zinja-coder/apktool-mcp-server; SecFathy/APktool-MCP | uv git fetch errors `Git operation failed`, persists past `uv cache clean`; SecFathy is unpackaged |

The previous commit on this PR (3fef2390) added prerequisites entries
for these 4 servers' tool binaries. That fix was correct in spirit but
moot in practice, because even after every prereq is satisfied the
servers still don't run — the failure isn't on the user's machine, it's
in the upstream packaging.

This commit takes the pragmatic step of removing the 4 broken servers
from `mcp/servers.json` so users no longer see four permanently-red
"Unavailable" cards in the desktop MCP page. The plugin now ships only
the **3 servers that have been live-tested end-to-end**:

| Server | Source                                | Verified state |
|--------|---------------------------------------|---|
| ghidra | uvx pyghidra-mcp                      | spawns; awaits user-set GHIDRA_INSTALL_DIR (by design) |
| gdb    | npx mcp-gdb                           | spawns; needs `gdb` on PATH (prereq declared) |
| frida  | uvx **frida-mcp** (PyPI v0.1.1)       |  initialize OK in 694 ms; serverInfo.name == "Frida" v1.27.2 |

Note frida changed source: was `uvx --from git+...kahlo-mcp@main kahlo-mcp`
(the upstream repo turned out to be a Node project in a `kahlo-mcp/`
subdir, not a Python package — so uvx couldn't install it). The PyPI
package `frida-mcp` is a clean, properly-packaged equivalent.

What this commit changes

- `plugins/reverse-engineering/mcp/servers.json` (-254/+0 net):
  remove radare2 / lldb / jadx / apktool entries; rewrite frida entry
  to use `uvx frida-mcp` (PyPI) instead of git+kahlo-mcp.
- `plugins/reverse-engineering/.claude-plugin/plugin.json`: 0.4.4 → 0.4.5.
- `plugins/reverse-engineering/README.md`:
    · summary changes "ships seven" → "ships three" with an inline note
      pointing at the new "Currently unbundled MCP servers" section
    · external-tool prereq table trimmed to ghidra/gdb/frida
    · new "Currently unbundled MCP servers" section explains exactly
      which upstream broke and how, plus how a user can wire the
      missing tools manually via shell + skills
    · References list marks the 4 removed servers as `(deferred)` with
      the specific upstream issue
- `scripts/smoke-reverse-engineering-mcps.ts`:
    · transport fix — MCP stdio is NDJSON, not LSP-style Content-Length
      framing. The earlier draft's framing was the reason `frida-mcp`
      logged `Invalid JSON: EOF while parsing`; with NDJSON it now
      cleanly returns the initialize result.
    · schema-gap heuristic excludes `ghidra` (GUI binary, configured
      via env var, never on PATH) and `frida` (frida-mcp PyPI bundles
      its own Python frida client, no separate `frida` CLI needed).

Verification

Re-running smoke on a fresh checkout of this branch with proxy 127.0.0.1:7887:

```
=== Reverse-engineering MCP smoke ===
Source: plugins\reverse-engineering\mcp\servers.json
Servers: 3

  ghidra     ⚠️ spawned but no JSON-RPC response in 5010 ms
  gdb         prereq missing: gdb
  frida       initialize ok (694 ms)
```

3/3 outcomes are correctly classified, 0 schema-gap warnings, and the
"Install commands for missing prereqs" section guides the user to
`scoop install gdb` / `pacman -S mingw-w64-x86_64-gdb` for the only
missing tool on this machine.

Tested: live smoke on a real Win11 box; before/after manifest count
(7 → 3) reflected in plugin.json bump.
Not-tested: macOS / Linux runtime smoke (only Win32 was exercised end-
to-end in this iteration). Each server's install map remains correct
across all three platforms.

Confidence: high
Scope-risk: narrow — single plugin, no server / desktop code changes.

---------

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 18:37:36 +08:00

14 KiB

reverse-engineering plugin

Multi-platform reverse engineering toolkit for cc-haha — static + dynamic

  • report — bundled as a single plugin install. Currently ships three MCP servers (down from seven in v0.4.3 — see "Currently unbundled MCP servers" below for why), one orchestration agent, eleven skills, and two slash commands.

What it gives you

Surface Item
Agent reverse-engineer — orchestrates triage → static → optional dynamic → report
Skills triage, pe-elf-macho, firmware-blob, apk-analysis, ios-analysis, dynamic-debug-overview, frida-dynamic, gdb-debug, lldb-debug, crackme-keygen, re-report
Commands /reverse-engineering:triage <path>, /reverse-engineering:report <sample-id>
MCP servers ghidra (pyghidra-mcp), gdb (mcp-gdb), frida (frida-mcp on PyPI) — verified end-to-end as of v0.4.5
Hooks placeholder (add a fileCreated hook locally if you want SOC-style auto-triage)

Skills still cover the unbundled lanes. lldb-debug / apk-analysis still teach the agent how to drive LLDB / apktool / jadx / radare2 via the shell — the loss of MCP wrapping just means there's no JSON-RPC tool surface for them; the agent can still invoke them as subprocess tools.

Dynamic capabilities (what AI can actually drive)

This is the lane that matters most for AI-driven RE. Static analysis has limited ROI when reading optimised, obfuscated, or stripped code; runtime observation turns hypotheses into facts. The plugin ships three dynamic lanes that don't overlap:

Capability Frida GDB LLDB
Read/write process memory
Read/write GP registers inside hook
Call stack
Function-level hook via breakpoint via breakpoint
Address-level hook (any instruction)
Instruction-level trace Stalker (cheap) ⚠️ stepi loop (slow) ⚠️ thread step-inst loop (slow)
Real single-step (instruction)
Real software/hardware breakpoints ⚠️ trampoline only
Watchpoint (byte granularity) ⚠️ page only
Reverse-debug rr / record full ⚠️ limited
Java method hook
ObjC method hook
Cross-arch (MIPS/PPC/68k/SH) ⚠️ via frida-server gdb-multiarch + qemu ⚠️ no PPC32/68k
iOS device frida-server jailbroken ⚠️ via debugserver via debugserver

The agent reads dynamic-debug-overview first to pick the right lane. For "single-step through MIPS router firmware" → GDB. For "what URL does this Android app POST to" → Frida. For "step into ObjC method on iOS" → LLDB.

Architecture coverage

The reverse-engineering decompilers (Ghidra, radare2) are multi-arch by design. The pe-elf-macho and firmware-blob skills cover:

  • x86 / x86-64 — Windows PE, Linux ELF, macOS Mach-O (the default case)
  • ARM — ARMv4-v8, Thumb/Thumb2 interworking, AArch64. Cortex-M (Thumb-only) flash images load via firmware-blob using the vector-table heuristic.
  • MIPS — MIPS32/64, big and little endian, MIPS16e/microMIPS. Common in routers, PSX, older PIC32, embedded Linux.
  • PowerPC — PPC32/PPC64, plus VLE (e200, NXP MPC57xx automotive). Common in Wii/GameCube, Xbox 360, older Macs, network gear.
  • Motorola 68k — M68000 through 68060, ColdFire. Old Macs, Atari ST, Amiga, Sega Genesis. Recognises Mac Toolbox A-line traps when applicable.
  • SuperH — SH-2 (Sega Saturn) and SH-4 (Dreamcast).
  • RISC-V — RV32/RV64 with C/M/A/F/D extensions.
  • Smaller ISAs Ghidra/r2 also handle — AVR (Arduino), MSP430, 6502 (NES), Z80, TriCore, Hexagon, Xtensa.

The firmware-blob skill specifically handles raw blobs (no PE/ELF/Mach-O header) — router firmware, Cortex-M flash dumps, U-Boot uImages, console ROMs, ECU dumps — by identifying the ISA + endianness + base address before loading into Ghidra/r2 with the right processor module.

Install

<repo-root> below is wherever you have cc-haha checked out (e.g. C:\Users\you\cc-haha on Windows, ~/cc-haha on macOS/Linux).

From the repo root, add the marketplace by directory:

# inside cc-haha checkout, in PowerShell:
$marketplace = (Resolve-Path .\plugins).Path
# Then in the desktop UI: Settings → Plugins → Add marketplace
# → paste $marketplace, install "reverse-engineering", enable.

Or via the CLI:

./bin/claude-haha plugin marketplace add (Resolve-Path .\plugins).Path
./bin/claude-haha plugin install reverse-engineering@cc-haha-builtin

Validate the manifest at any time:

./bin/claude-haha plugin validate plugins/reverse-engineering

Quickstart — first real run

Once the plugin is enabled and at least one of the underlying tools is on your PATH (Ghidra or radare2 covers most native cases), pick a small, non-malicious open-source binary to drive the workflow. busybox is a good first target — it's a single static ELF, big enough to be interesting, small enough to finish quickly.

# 1. Get a sample
mkdir samples
curl -L -o samples/busybox 'https://busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-x86_64'

# 2. Triage — identifies file type, packing, picks the next skill
#    (in chat) /reverse-engineering:triage samples/busybox

# 3. Static analysis happens automatically once triage routes to pe-elf-macho.
#    For a non-x86 sample (firmware blob, MIPS router image, Cortex-M flash dump),
#    triage routes to firmware-blob first, which identifies the ISA and base
#    address before handing back to pe-elf-macho.

# 4. Final report
#    (in chat) /reverse-engineering:report <sample-id>

Expected products under ${ARTIFACT_DIR}/<sample-id>/:

triage.md            — file type, entropy, routing decision
static-native.md     — imports, key functions decompiled, strings, decoded constants
report.md            — verdict + findings table + IOCs + open questions

Confidence is honest: static-only conclusions about runtime behaviour cap at medium. To upgrade to high you have to run frida-dynamic against a target you've authorised.

Development workflow (changing skills / agent prompts)

The plugin loader caches each plugin under ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ keyed on the manifest version. That means a naive "edit SKILL.md, reload" loop will not see your changes until the version is bumped.

Two options:

Option A — version bump (publishing flow)

# Edit plugin sources, then:
# 1. Bump "version" in plugins/reverse-engineering/.claude-plugin/plugin.json
# 2. Re-materialise:
Invoke-RestMethod -Method POST -Uri http://127.0.0.1:3456/api/plugins/update `
  -ContentType 'application/json' `
  -Body '{"id":"reverse-engineering@cc-haha-builtin","scope":"user"}'
Invoke-RestMethod -Method POST -Uri http://127.0.0.1:3456/api/plugins/reload `
  -ContentType 'application/json' -Body '{}'

Option B — dev junction (fast iteration loop)

# Replace the cached version dir with a junction to the in-repo source.
bun run plugins/reverse-engineering/scripts/dev-link.ts

# Now editing any SKILL.md / agent / command takes effect after just:
Invoke-RestMethod -Method POST -Uri http://127.0.0.1:3456/api/plugins/reload `
  -ContentType 'application/json' -Body '{}'

# When done, restore the real cache before publishing:
bun run plugins/reverse-engineering/scripts/dev-link.ts --restore

dev-link.ts is Windows-only (uses mklink /J); on macOS/Linux a manual ln -s does the same thing.

Smoke test

End-to-end check after manifest changes — assumes server (:3456) and vite (:1420) are running (start them as documented in docs/desktop/10-local-mcp-testing.md):

bun run plugins/reverse-engineering/scripts/smoke.ts

The script registers the marketplace, enables the plugin, runs /api/plugins/update + /reload, and asserts that detail returns the right version, zero errors, and the expected component counts (counted from the on-disk source, not hardcoded). Exits non-zero on any mismatch.

External tool prerequisites

The plugin doesn't ship the underlying tools. You need them on your machine (installable independently — none are required all at once):

Auto-detect since cc-haha v0.5.10: when you enable this plugin from the desktop Settings → Plugins page, cc-haha probes whether each of the host commands below is on PATH. Anything missing (e.g. uvx, radare2, java) shows up in a one-click install modal with platform- specific commands — winget/scoop on Windows, brew on macOS, apt/dnf on Linux. The probe is a where / command -v lookup; it never executes the underlying tool. Declarations live in mcp/servers.json under each server's prerequisites key.

MCP What you need Install
ghidra Ghidra (NSA), Java 17+, uvx (from uv) https://ghidra-sre.org + set GHIDRA_INSTALL_DIR
gdb GDB on PATH (gdb-multiarch for cross-arch), Node apt install gdb gdb-multiarch / brew install gdb / scoop install gdb
frida uvx (the frida-mcp PyPI pkg bundles a Python frida client; only needs frida-server on the target device) uvx auto-installs frida-mcp; deploy frida-server to your authorised target separately

You can disable individual MCP servers (e.g., turn off Frida if you only do static work) from the desktop MCP settings page (Settings → MCP) — the plugin's job is to bundle the configurations; per-server enable/disable is a runtime decision, not a manifest one.

Currently unbundled MCP servers

The v0.5.10 release of cc-haha shipped this plugin with seven MCP servers, but four of them turned out to have upstream packaging or runtime issues that no manifest-level fix can paper over. They have been removed from mcp/servers.json for v0.4.5 (cc-haha v0.5.12+) so users don't see four permanently-red "Unavailable" cards in the MCP page. Each entry below records the failure mode discovered during end-to-end smoke; if the upstream lands a fix, the server can be re-added in a future patch.

Server Upstream tried Failure mode
radare2 npm @radareorg/radare2-mcp npm registry returns 404 — package unpublished. The official GitHub repo radareorg/radare2-mcp is a C/Meson project that requires compilation, not direct npx/uvx install. Fork drvcvt/radare2-mcp is a TypeScript project but ships no dist/ and no prepare build hook, so npx --package=git+... fails to find the entry binary.
lldb stass/lldb-mcp (and the stableversion/lldb_mcp fork) Repo is a single-file lldb_mcp.py script with no pyproject.toml / setup.py packaging, so uvx --from git+... errors with does not appear to be a Python project.
jadx zinja-coder/jadx-mcp-server (and mseep-jadx-mcp-server PyPI republish) Original repo packages but crashes at startup with ModuleNotFoundError: No module named 'src' (upstream packaging bug). The PyPI republish under mseep-jadx-mcp-server is a 0-byte placeholder that contains only dist-info metadata with no actual code.
apktool zinja-coder/apktool-mcp-server (and SecFathy/APktool-MCP) uv git fetch consistently fails with Git operation failed, persisting after uv cache clean. The SecFathy alternative is also unpackaged (single APktool.py file).

To use these locally without waiting for upstream:

  1. Clone the upstream repo to a fixed path under your home directory.
  2. Add a custom MCP server entry pointing at the local script in your user-level ~/.claude/mcp.json (not the plugin manifest — that gets overwritten on plugin update).
  3. The agent skills (lldb-debug, gdb-debug, etc.) still teach the agent how to drive these tools via shell, so even without the JSON-RPC wrapping you can still get a working dynamic-analysis workflow as long as the binaries are on PATH.

If a packaged alternative shows up on PyPI / npm, please open an issue and we'll re-add the server to mcp/servers.json.

User-config knobs

Key Default Purpose
GHIDRA_INSTALL_DIR (env fallback) Path to Ghidra install. Substituted into the ghidra MCP server's env at launch.
ARTIFACT_DIR artifacts/re-runs Where reports and intermediates go. Resolved relative to the agent's current working directory at run time.

Scope and rules

  • Read-only on samples. No skill in this plugin will execute a sample on the host. Frida runs only on user-authorised targets (sandboxed device or VM).
  • No public uploads. No VirusTotal, no malware-bazaar pushes.
  • No commercial license cracking. The crackme-keygen skill is for CTFs and self-owned binaries.
  • Confidence is honest. Static-only conclusions about runtime behaviour cap at medium; high requires confirmation by another channel.

References