
The reverse-engineering expert agent: plans its own analysis path, derives every fact from raw evidence, and converges under mechanical verification gates — firmware, protocols, web/JS, risk-control, binaries.
kunglao-agent is an autonomous reverse-engineering system. You hand it a target and the questions you need answered; it works the problem for hours or days on its own — planning its own path, recovering from worker deaths, resuming after crashes — and converges only when every answer is derived from raw evidence and survives mechanical verification gates.
English · Simplified Chinese
It currently ships as a Claude Code plugin — Claude Code is the interface you talk to, not what the product is. The product is the loop: specialist workers analyse (static first), an independent verifier re-derives every fact blind from the raw evidence, and mechanical gates decide when the work is done. The deliverable is a fact base where every claim is byte-anchored, independently verified, and evidence-indexed — trust is enforced by machinery, not convention.
PROVEN until an independent verifier re-derives it blind from the raw artifact; every fact cites a sha256-indexed raw artifact through evidence/_index.json.kunglao-agent runs inside Claude Code. From a sample on disk to a verdict:
From any directory, in Claude Code:
/plugin marketplace add amd2g2zz/kunglao-agent
/plugin install kunglao-agent@kunglao-agent
(Alternative: claude --plugin-dir /path/to/kunglao-agent for development.)
/kunglao-agent:init ~/cases/synth-dropper --type windows
kunglao-init scaffolds the workspace, writes CLAUDE.md, probes the toolchain for your --type, and scaffolds .mcp.json. It HARD-rejects when a required tool for your type is missing — the fix guidance is in the error block.
/kunglao-agent:analysis ~/cases/synth-dropper
> Goal: confirm this dropper's persistence mechanism and network endpoints;
> every conclusion must be reproducible from raw evidence.
> Verification: key findings count only if an independent verifier re-derives
> them blind and reaches the same answer.
> Constraints: static-first; never execute the sample on the host.
Write the brief so an independent reviewer could judge the result: analysis goal (what you need to know), verification logic (what makes an answer trustworthy — e.g. "the signature must be reproducible from the same inputs"), constraints (e.g. "no execution on the host"). Everything is recorded in task_spec.yaml; from there the loop drives itself. For how the common asks turn into well-formed statements, see How to state the task.
claim-register.yaml # every claim terminal, with verifier sign-off
facts/F<NNN>.md # byte-anchored, reproducible, frontmatter contract
evidence/_index.json # every fact → raw artifact (sha256 + path)
runs/ # session audit trail
The loop derives its completion criterion — the oracle — mechanically from the end-state you state. A vague statement yields a vague oracle, and the analysis drifts toward whatever can be proven instead of what you needed. Four phrasings cover most of that drift. For each: what users say, what it usually means, a well-formed statement, and what the oracle anchors on.
Usually means: offline reproduction of the app's signing/crypto routine — a unidbg harness or a rewrite that runs with no device and no app at run time. Not "analyze the app"; the app is only where the algorithm lives.
> Sample: the v7.2 APK; behavior: the signer producing the `sign`
> header on api.example.com/v2/* requests.
> Criterion: a standalone reproduction (unidbg or rewrite) replays
> every captured (input → sign) pair byte-exact — including the
> withheld pairs — with no device or app at run time.
> Attach: captures/sign-pairs.jsonl — 20 input/output pairs captured
> from a live session; 10 of them withheld from the analysis.
Oracle anchors on: byte-exact replay on every pair, including the withheld ones — and the reproduction running standalone.
Usually means one of two different targets — say which:
Well-formed (a):
> Sample: the v7.2 APK; behavior: the local config cache
> files/.cfg/v2.dat is encrypted at rest.
> Criterion: produce the plaintext of the captured v2.dat and validate
> it against what the app renders (field names and values match the
> screenshot captured alongside).
Well-formed (b):
> Sample: the v7.2 APK; behavior: request bodies on
> api.example.com/v2/* are encrypted with a static key.
> Criterion: identify the algorithm and the key, then run a canary
> round-trip — encrypt a known plaintext with the recovered key and
> match the ciphertext the device produced, byte for byte.
> Attach: captures/request-bodies.jsonl — ciphertext bodies captured
> from the device, with the requests that produced them.
Oracle anchors on: (a) the plaintext validating against what the app renders; (b) algorithm + key identified and the canary round-trip byte-identical to device-produced ciphertext. "It decrypted once" satisfies neither.
Usually means: wire-format recovery — framing, field semantics, and a codec you can run.
> Sample: the Android chat app; behavior: the TCP protocol on
> gateway.example.com:443, as captured in gateway-session.pcap.
> Criterion: a codec that round-trips every captured frame byte-exact,
> and decodes the held-out frame to fields matching the observed app
> behavior.
> Attach: captures/gateway-session.pcap — 40 frames, plus 1 held-out
> frame kept out of the analysis.
Oracle anchors on: the codec round-tripping every captured frame byte-exact, and the held-out frame decoding to fields that match observed app behavior.
Usually means: a location with proof. Naming a point in the code is cheap; the answer is only useful with evidence that this point is the point.
> Sample: the v7.2 APK; behavior: the `sign` header attached to every
> request.
> Criterion: name the class/method (or native function) where `sign`
> is computed, and hook that point to reproduce the captured `sign`
> values from the same inputs.
> Attach: captures/sign-session.jsonl — captured `sign` values with
> their request inputs.
Oracle anchors on: a named class/method/native function, plus a hook at that point reproducing the captured values.
sign header on api.example.com/v2/*" is a target.Typical order: init creates the workspace → analysis states the task and starts → (resume if anything goes sideways) → read the report at convergence → upgrade old workspaces after plugin updates.
The shape of an engagement — what you type, what comes back, where to look. A synthetic example: a small Windows dropper lands in ~/cases/synth-dropper:
/kunglao-agent:init ~/cases/synth-dropper --type windows # probes Ghidra, VM reachability
/kunglao-agent:analysis ~/cases/synth-dropper
> "What does this binary do, and where does it phone home?"
From there the loop runs itself — the route adapts to what the sample turns out to be. You can walk away (see Long-horizon autonomy). When it converges, read the deliverable below.
Two more end-to-end paths — pick the one matching your target (for a plain Windows PE / Linux ELF binary, the worked case above is the path).
/kunglao-agent:init ~/cases/sample.apk --type android
/kunglao-agent:analysis ~/cases/sample.apk
> "Does this APK load code dynamically or fight debugging? If so, where is
> the hidden logic and what does it do?"
bins/<sha256> (the APK), facts/ (class graph, native .so inventory), evidence/ (captures, dumps)./kunglao-agent:init ~/cases/example-site.com --type web
/kunglao-agent:analysis ~/cases/example-site.com
> "how is the XHR request signed, and where does the nonce come from?"
evidence/ (captures, deobfuscated code), facts/ (signing key, nonce derivation).web is a beta-stage target — the toolchain bar is deliberately light; missing capability surfaces when the loop actually needs it, not at init.A claim register and fact base where trust is mechanical, not conventional:
PROVEN requires an independent blind verifier's exact-match sign-off; CONVERGED requires every primary question answered with byte-proof, zero orphan claims, no spinning.evidence/_index.json to a raw artifact (capture / trace / dump / binary). Derived summaries are excluded by design.No claim reaches PROVEN on its author's word: an independent verifier must re-derive it blind, and a set of mechanical gates must pass. The full gate design lives in docs/design/loop-engineering.md.
After the run, the files answer different questions:
Example fact:
id: F061
status: VERIFIED-BY-W01-static-byte-recheck
claim_id: C-401
provenance:
- {role: sample, path: bins/<sha>}
- {role: capture_log, path: runs/c329-inner-pe.bin} # via evidence/_index.json
reproduce: python -c "import struct; ..." # runs against the cited artifact
verifier_sign_off: {verifier: kunglao-redteam, verdict: CONFIRMED}
Real engagements are not a twenty-minute chat. kunglao-agent stays on the problem without a human shepherding every step:
/kunglao-agent:resume <workspace> rebuilds where things stood from on-disk state and names the next action.You give it a target and the questions; it works the problem for hours or days, recovers from failures, and you read the verdict when it converges.
local.runs/ mean the loop is alive; a dead heartbeat or the same decision repeating with no new facts means it is not — /kunglao-agent:resume <workspace> diagnoses and names the next move.The --type you pick at init locks which HARD-tier tools must be installed. Guidance is collapsed — expand your target. All types require two MCP servers: ghidra (claude mcp add ghidra -- <path>/bridge-mcp-ghidra.exe) and sequential-thinking (claude mcp add sequential-thinking -- npx -y @modelcontextprotocol/server-sequential-thinking).
Single manifest source for everything above — probe it any time: python scripts/mcp_probe.py <ws> --type <windows|linux|android|web|macos> (exit 1 = HARD missing).
Dynamic debugging needs an execution control plane the agent can drive. KUNGLAO_CHANNEL selects one of five first-class channels — use what your environment already has; none is a degraded mode:
localred line: local is for static work only — never execute, debug, or inject the sample on the host. Any dynamic requirement switchesKUNGLAO_CHANNELtovmr/ssh/docker/adb; init HARD-rejects a dynamic task onlocal.
Channel probes run only for dynamic tasks (static-only tasks skip them). ssh-channel execution flows through the ssh-mcp control plane (npm i -g ssh-mcp); plain CLI ssh is the fallback. For remote docker over ssh, set KUNGLAO_DOCKER_CONTAINER.
Four variables cover most setups:
Rarely needed: KUNGLAO_DOCKER_CONTAINER (docker execution target for the ssh/docker channels), KUNGLAO_FRIDA_PORT (default 1337), KUNGLAO_DIE (DIE path, falls back to PATH), KUNGLAO_CLAUDE_JSON (test override for the user-level MCP registry).
block_malware_exec hook enforces it; dynamic work runs VM/container/device-only and requires per-session authorization.Contributions are welcome. Workflow: branch from dev, one branch per change, PR back to dev.
git worktree add .worktrees/<name> -b <name> dev
uv sync --locked
uv run python -m pytest -q
gh pr create --base dev
The authoritative full-suite entry is python -m pytest -q (see .github/workflows/release-check.yml).
Design documentation lives in docs/ and specs/. See License.
Single source of truth: scripts/mcp_probe.py; kunglao-init scaffolds a workspace .mcp.json when missing (--no-mcp skips; an existing file is never overwritten). Probe: python scripts/mcp_probe.py <ws> --type <windows|linux|android|web|macos> — exit 1 = HARD missing, 2 = WARN missing only.
One workspace per sample engagement:
<workspace>/
├── bins/<sha256> # the sample (gitignored)
├── task_spec.yaml # primary_questions / scope / constraints / success_criteria
├── claim-register.yaml # claims C-NN with status (OPEN/PROVEN/STAMP/...)
├── claim_deps.yaml # claim DAG
├── facts/ # byte-anchored facts F-NNN.md + _INDEX.md
├── evidence/ # raw artifacts + _index.json (eid → path + sha256)
├── runs/ # worker-status, plans, ledgers, .heartbeat.json
├── blockers/ # failure-attribution records per claim
└── CLAUDE.md # workspace rules, generated by kunglao-init
kunglao hooks are wired at workspace level; your global ~/.claude/settings.json is never written.
Dual-licensed: AGPL-3.0 for personal, academic, and internal use (free — see LICENSE); a commercial license is required for closed-source or SaaS commercial use — see LICENSE-commercial.md.
| Tool | Why | Install |
|---|
| Claude Code | where kunglao-agent runs | per Anthropic docs |
| Python 3.10+ (Python 2 is not supported) | the plugin carries a pinned env via uv; you do not touch it | system or uv-managed |
uv | locked env resolver | pip install uv or astral.sh/uv |
| Ghidra or IDA | one static-analysis suite for decompilation | see Toolchain by target |
| Command | Use when | What it does |
|---|
/kunglao-agent:init <workspace> [--type windows|linux|android|web|macos] [--lane malware|algorithm|protocol|web|data|app] | starting an engagement, first | scaffolds the workspace, probes the toolchain for the type, writes CLAUDE.md and .mcp.json; HARD-rejects with fix guidance when a required tool is missing |
/kunglao-agent:analysis <workspace> (alias analyze) | after init — state the task and start | collects your goal / verification logic / constraints once, then runs the convergence loop: dispatch / verify cycles until the report |
/kunglao-agent:resume <workspace> | after a crash, reboot, or any "where was I?" | read-only breakpoint brief (health, open claims, in-flight workers, crash timeline) plus the next action from the state machine |
/kunglao-agent:upgrade <workspace> [--dry-run] | after a plugin update, on an older workspace (or when the upgrade prompt says the stamp is behind) | migrates the workspace scaffold (hooks, templates, event vocab) to the current plugin version; --dry-run previews; user data (claims, facts, evidence) is never touched — byte drift refuses with RC=4 |
/kunglao-agent:help | anything else | prints the usage list |
| Question | Where |
|---|
| Is it done? | the loop's exit code — CONVERGED (0) means every primary question has a verified answer; per-claim status in claim-register.yaml |
| What did it find? | facts/F<NNN>.md — one byte-anchored fact per file, mapped to claims by claim-register.yaml |
| How do I reproduce it? | evidence/_index.json — fact → raw artifact (path + sha256); each fact carries a reproduce: command |
| What exactly happened? | runs/ — the tick-by-tick ledger and worker status |
| Tier | Tool | Install |
|---|
| HARD | pefile (Python) | pip install pefile |
| HARD | die (Detect It Easy) | KUNGLAO_DIE env or on PATH — ntinfo.com |
| HARD | floss (FLARE FLOSS) | per flare-floss docs |
| HARD | Ghidra or IDA | one of them; see Internals |
| HARD (T2/T3) | VMware + vmr-shell, or an ssh/docker channel | see Bring your own environment |
| HARD (T2/T3) | frida-server (renamed, custom port) | device/VM-side binary, default port 1337 |
Windows T3 dynamic also uses the x64dbg MCP; volatility (memory forensics) and IDA-Pro MCP are optional — see the MCP manifest under Internals.
| Tier | Tool | Install |
|---|
| HARD | file, readelf, objdump | binutils package |
| HARD | Ghidra or IDA | one of them |
| HARD (T2/T3) | VMware + vmr-shell, or an ssh/docker control plane | see Bring your own environment |
| HARD (T2/T3) | frida-server (renamed, custom port) | device-side binary, port 1337 |
| WARN | gdbserver (host-side PATH), strace, ltrace | optional extras |
ssh-mcp enables the ssh control plane for remote / cloud / docker hosts.
| Tier | Tool | Install |
|---|
| HARD | aapt or aapt2 (or unzip fallback) | Android SDK build-tools |
| HARD | jadx (DEX → Java decompiler) | skylot/jadx |
| HARD | apktool (APK resource decode/rebuild) | iBotPeaches/Apktool |
| HARD | gitnexus (post-decompile graph) | npm i -g gitnexus |
| HARD | Ghidra or IDA | only if the APK contains native .so |
| HARD | adb + a rooted device with ro.debuggable=1 | platform-tools + custom frida on device |
| HARD | frida-server (renamed, custom port 1337) | device-side binary |
| HARD | android_server (IDA remote debugging) | device-side binary, port 23946 |
| WARN | apkid | pip install apkid |
| WARN | baksmali | from smali releases |
| Tier | Tool | Install |
|---|
| WARN | camoufox-reverse MCP (web) | anti-detect Firefox for hook / trace / network capture |
| WARN | docker (web channel default) | Docker Desktop, or set KUNGLAO_CHANNEL=ssh explicitly |
| WARN | lipo, otool, nm, codesign, xattr (macOS) | Xcode Command Line Tools |
| WARN | ghidra MCP (macOS) | recommended — see the manifest under Internals |
Both are beta-stage targets: missing capability surfaces when the loop actually needs it, not at init. macOS dynamic work uses the ssh channel (to a Mac host); for the optional x64dbg browser-debug path, install the Windows toolchain above.
| Channel | What it drives | Prerequisites |
|---|
vmr (default) | VMware VM, any guest OS — snapshot/revert workflows are its irreplaceable value | vmr-shell skill; KUNGLAO_VM_HOST + ports 9876/1337 |
ssh | Any ssh-reachable box: bare metal, cloud VM, Mac, remote docker host | key auth — the probe runs a real BatchMode ssh ... true |
docker | Local or remote docker daemon — docker exec is equivalent to any control path | docker version green; optional KUNGLAO_DOCKER_CONTAINER |
adb | Android emulator or real device | adb devices shows it; adb forward tcp:1337 tcp:1337 for frida |
local | Static-only analysis on the host | none — see the red line |
| Variable | Default | Meaning |
|---|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS | unset | must stay unset or 0 — truthy values route dispatches through the teammate channel and are rejected |
KUNGLAO_CHANNEL | vmr | dynamic execution control plane: vmr | ssh | docker | adb | local — see Bring your own environment |
KUNGLAO_VM_HOST | unset | VM/host for dynamic analysis (vmr-shell :9876, Frida :1337) |
GHIDRA_HOME | unset | Ghidra install root (must contain support/analyzeHeadless.bat) |
| MCP server | Tier | Scope | Purpose | Registration |
|---|
ghidra | HARD | required, all types | decompilation / static analysis | claude mcp add ghidra -- <path>/bridge-mcp-ghidra.exe |
sequential-thinking | HARD | required, all types | structured reasoning | claude mcp add sequential-thinking -- npx -y @modelcontextprotocol/server-sequential-thinking |
x64dbg | HARD | Windows T3 dynamic | dynamic debugging (VM remote) | claude mcp add x64dbg -- x64dbg-automate-mcp |
volatility | WARN | Windows T3 | memory forensics | claude mcp add volatility -- python <path>/volatility_mcp_server.py |
ida-pro-vm | WARN | when IDA chosen | remote IDA analysis | claude mcp add --transport http ida-pro-vm <ida-mcp-url> |
gitnexus | HARD | Android graph building | post-decompile knowledge graph | claude mcp add gitnexus -- gitnexus mcp |
virustotal | WARN | CTI | threat intel (family-attribution hypotheses) | claude mcp add virustotal -- npx -y @burtthecoder/mcp-virustotal |
ssh-mcp | WARN | channel | ssh execution control plane | claude mcp add ssh-mcp -- ssh-mcp |
camoufox-reverse | WARN | web (beta) | browser JS reversing (hooks / trace / network capture) | claude mcp add camoufox-reverse -- python -m camoufox_reverse_mcp |