Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,20 @@ Every re-dispatch MUST include the output-redirection instruction (see section E

## F. Specialist Workload Lifecycle (ADR-0006)

Domain-specific specialists need sidecar services to function — `ad_operator` calls BHCE for attack-graph queries, `postexploit` / `exploit` may need a Sliver C2 team server, `reverser` needs the Ghidra MCP bridge. These workloads are **opt-in**: they are not running when the engagement starts. You spawn them through the `ops_*` toolset (only the orchestrator carries those — sub-agents cannot start arbitrary infrastructure).
Domain-specific specialists need sidecar services to function — `ad_operator` calls BHCE for attack-graph queries, `postexploit` / `exploit` may need a Sliver C2 team server, `reverser` needs the Ghidra MCP bridge only for Ghidra MCP / headless decompilation. Basic triage / Radare2 work still goes to `reverser` without the `reversing` workload. These workloads are **opt-in**: they are not running when the engagement starts. You spawn them through the `ops_*` toolset (only the orchestrator carries those — sub-agents cannot start arbitrary infrastructure).

| Specialist | Workload to spawn | When |
|---|---|---|
| `ad_operator` | `ad` | Recon SUMMARY.md reports an Active Directory environment (SMB / Kerberos / LDAP / DC banner / Windows-domain naming) |
| `postexploit` (and `exploit` if it needs C2-bound payloads) | `c2-sliver` | After foothold — initial RCE / cred dump / sandbox shell is captured |
| `reverser` | `reversing` | A binary needs decompilation / static analysis that bash cannot drive |
| `reverser` | `reversing` | Only for Ghidra MCP / headless decompilation, xrefs, P-code, or batch deep analysis. Do NOT start it for identify/strings/packer/import-risk/ROP/Radare2 triage. |

**Workflow** (mandatory order):

1. Before any `task("<specialist>", ...)` whose workload row above applies, call `ops_start("<workload>")`. **The tool returns IMMEDIATELY** with `state: "starting"` — the daemon spawns the workload in the background. The current engagement tag is attached automatically; never pass an `engagement_id=` argument.
2. **Do NOT poll `ops_status` waiting for it.** Within one or two turns a `<system-reminder>` is injected automatically: `● Workload 'ad': starting → running engagement=...`. That reminder is the authoritative ready signal. If the reminder says `→ stopped` or `→ unknown` the workload failed to come up — treat as a blocked specialist objective (or, when ops daemon was never reachable to begin with — `make dev` / `make smoke` ship daemon-less — fall back to specialist tools that do not require the workload).

For `reverser`: Do NOT block binary triage just because `ops_start("reversing")` fails or opscontrol is unavailable. Dispatch `reverser` for identify/strings/packer/import-risk/ROP/Radare2 triage, and record that Ghidra-only deep analysis is unavailable if needed.
3. On the turn you receive the `→ running` reminder, dispatch the specialist `task()` as usual.
4. After the specialist returns, decide whether the workload is still needed:
- **OPPLAN still has pending tasks that need it** → leave it running, do not call `ops_stop`.
Expand Down
19 changes: 14 additions & 5 deletions packages/decepticon/decepticon/agents/prompts/standard/reverser.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ Your operating loop is:
2. UNPACK — bin_packer; if entropy > 7, unpack before further work
3. HARVEST — bin_strings (url, ip, crypto, secret, version, import)
4. RISK — bin_symbols_report on the import table
5. DEEPEN — ghidra_analyze for full analysis; ghidra_decompile for pseudocode
6. XREFS — ghidra_xrefs to trace dangerous-import callers
7. EXPLOIT — bin_rop for gadget inventory if memory corruption suspected
8. PERSIST — every observation → `findings/FIND-NNN.md`; cross-reference
5. RADARE2 — bin_r2_script + bash/r2 when Ghidra MCP/headless is unavailable
6. DEEPEN — ghidra_analyze for full analysis; ghidra_decompile for pseudocode
7. VIRT — VMProtect / VMP2 / Themida: identify VMEnter/VMEXIT/VIP, then prefer incremental lifting and control-flow recovery over brittle handler matching
8. XREFS — ghidra_xrefs to trace dangerous-import callers
9. EXPLOIT — bin_rop for gadget inventory if memory corruption suspected
10. PERSIST — every observation → `findings/FIND-NNN.md`; cross-reference
related observations with explicit links between files
</IDENTITY>

<CRITICAL_RULES>
- Start with ghidra_status to confirm the Ghidra MCP bridge is live.
If MCP is unavailable, fall back to bin_ghidra_script + bash.
If MCP/headless is unavailable, continue with Radare2/r2 via bin_r2_script + bash.
- Record every binary you look at in `findings/binaries/<binary>.md`.
Cross-reference secrets, imports, and crashes from that file.
- Version strings from bin_strings feed cve_lookup / cve_by_package —
Expand Down Expand Up @@ -60,6 +62,13 @@ After memory-corruption bug is identified (e.g. from a fuzzer crash):
2. filter_gadgets_by_pattern for pop/pop/ret, stack pivots, etc.
3. Check bin_identify → if PIE is true, ASLR means you need an info
leak first — note that as a hypothesis.

## Lane E — Virtualized protectors
VMProtect / VMP2 / Themida samples start with normal triage and packer
signals, then follow `/skills/standard/reverser/virtualized-protectors/SKILL.md`.
Do not promise automatic devirtualization. Recover VMEnter, VMEXIT, VIP,
handler-table clues, and branch behavior; use Radare2/Ghidra facts to plan
incremental lifting or trace collection.
</HUNTING_LANES>

<ENVIRONMENT>
Expand Down
6 changes: 3 additions & 3 deletions packages/decepticon/decepticon/agents/standard/reverser.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ def create_reverser_agent(
description=(
"Binary reversing specialist. Use for ELF/PE/Mach-O/firmware triage, "
"packer detection, classified string extraction, symbol risk reports, "
"ROP gadget inventories, Ghidra/radare2 recon script generation, "
"and deep analysis via Ghidra headless + MCP bridge (decompilation, "
"cross-references, P-code emulation, batch analysis, 245 tools). "
"ROP gadget inventories, and Radare2-assisted basic triage without the reversing workload. "
"Use Ghidra headless + MCP bridge only for deep decompilation, "
"cross-references, P-code emulation, batch analysis, and 245-tool workflows. "
"Ideal for thick clients, IoT firmware, game cheats, malware triage, "
"and exploit dev hand-offs."
),
Expand Down
10 changes: 8 additions & 2 deletions packages/decepticon/decepticon/skills/.graph/skills.cypher

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
name: reverser-overview
description: Root pointer for the binary reversing lane. Covers triage, string extraction, packer unpacking, symbol risk, ROP, Ghidra deep analysis, and firmware extraction.
description: Root pointer for the binary reversing lane. Covers triage, Radare2 fallback, string extraction, packer unpacking, virtualized protectors, symbol risk, ROP, Ghidra deep analysis, and firmware extraction.
metadata:
subdomain: reverse-engineering
when_to_use: "reverser binary reversing triage strings packer unpack rop ghidra firmware overview routing"
upstream_ref: "Decepticon reverser lane catalog — Ghidra, AFL++, libFuzzer, binwalk, and binary triage tooling"
when_to_use: "reverser binary reversing triage strings packer unpack rop ghidra firmware VMProtect VMP2 Themida virtualized protectors overview routing"
upstream_ref: "Decepticon reverser lane catalog — Ghidra, Radare2, Back Engineering VMProtect/Themida research, AFL++, libFuzzer, binwalk, and binary triage tooling"
---

# Reverser Skill Catalog
Expand All @@ -15,6 +15,7 @@ metadata:
| `/skills/standard/reverser/triage/SKILL.md` | First-pass ELF/PE/Mach-O triage |
| `/skills/standard/reverser/firmware/SKILL.md` | Router / IoT firmware extraction |
| `/skills/standard/reverser/packer-unpacking/SKILL.md` | UPX / ASPack / Themida / VMProtect |
| `/skills/standard/reverser/virtualized-protectors/SKILL.md` | VMProtect / VMP2 / Themida workflow |
| `/skills/standard/reverser/rop-chain/SKILL.md` | Gadget hunting for exploit dev |
| `/skills/standard/reverser/anti-debug-bypass/SKILL.md` | IsDebuggerPresent, ptrace, NtGlobalFlag |
| `/skills/standard/reverser/ghidra/SKILL.md` | Deep Ghidra analysis — decompile, xrefs, imports, P-code |
Expand All @@ -27,6 +28,6 @@ metadata:
5. `bin_strings` — category=url/ip/crypto/secret/version to seed the graph
6. `bin_symbols_report` — risk bucket classification
7. Version strings → `cve_lookup` + `cve_by_package`
8. `ghidra_analyze` for full analysis, or `bin_ghidra_script` / `bin_r2_script` as fallback
8. `ghidra_analyze` for full analysis, or `bin_ghidra_script` / `bin_r2_script` for headless Ghidra / Radare2 fallback
9. `ghidra_decompile` on interesting functions, `ghidra_xrefs` on dangerous imports
10. Record every observation in the knowledge graph
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: reverser-virtualized-protectors
description: VMProtect, VMP2, Themida, and CodeVirtualizer reversing workflow using Radare2/Ghidra facts and Back Engineering Labs research guidance.
metadata:
subdomain: reverse-engineering
when_to_use: "VMProtect VMP2 VMProtect2 Themida CodeVirtualizer virtualized protector devirtualization VMEnter VMEXIT VIP handler table"
upstream_ref: "Back Engineering Labs vmp2, vmhook, and Static Devirtualization of Themida research"
---

# VMProtect / VMP2 / Themida Workflow

Use this for VMProtect 2, VMP2 tooling questions, Themida, CodeVirtualizer,
and other VM-based protectors. Do not promise automatic devirtualization.
Most wins come from disciplined recovery of VM control-flow facts.

## Source Guidance

Back Engineering Labs' public VMP2/Themida work points to one durable rule:
Avoid brittle VM-handler pattern matching. Handler layouts, opcode tables,
and dispatch glue change too easily. Prefer incremental lifting and control-flow recovery with as little VM-specific logic as possible.

## Loop

1. `bin_identify` and `bin_packer` first; record format, arch, entropy, and
protector strings.
2. Use `bin_r2_script` or Ghidra to locate VMEnter stubs, handler-table
references, VM context/virtual stack sections, and suspicious indirect
dispatch loops.
3. Recover VIP movement. For VMProtect 2, track bytecode/module loads feeding
the indirect jump. For Themida, expect branch state in VM context and trace
the branch-taken flag through the VPC update.
4. Classify VMEXIT behavior: return to native epilog, call-shaped exit, or
unsupported-instruction exit. Record stack displacement evidence.
5. Prefer trace/lift plans that run simple optimizations to convergence:
constant promotion over VM-private ranges, constant folding, instruction
combination, branch folding, dead-store/dead-dependency cleanup, and stack
pointer rewrite.
6. If using VMP2-style tooling, treat `vmemu`/`vmprofiler`/`vmprofiler-cli`
outputs as evidence, not ground truth. Re-check recovered paths in r2/Ghidra.
7. Write findings with exact addresses, section names, vmenter candidates,
VMEXIT classification, VIP source, and unresolved symbolic branches.

## Report Shape

- Protector: VMProtect / VMProtect 2 / Themida / CodeVirtualizer / unknown
- Entrypoints: candidate VMEnter addresses and why
- Dispatch: handler-table location, decrypt/transform clues, indirect jump path
- VIP: source load or Themida branch flag/VPC update evidence
- VMEXIT: stack displacement and native continuation/call target
- Recovery plan: trace, lift, or manual simplification; no fake devirt claims
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from __future__ import annotations

from pathlib import Path

_REPO_ROOT = Path(__file__).resolve().parents[5]
_PROMPTS = _REPO_ROOT / "packages/decepticon/decepticon/agents/prompts/standard"
_REVERSER_AGENT = _REPO_ROOT / "packages/decepticon/decepticon/agents/standard/reverser.py"

_REVERSER_SKILL = _REPO_ROOT / "packages/decepticon/decepticon/skills/standard/reverser/SKILL.md"
_VIRTUALIZED_SKILL = (
_REPO_ROOT
/ "packages/decepticon/decepticon/skills/standard/reverser/virtualized-protectors/SKILL.md"
)


def test_orchestrator_dispatches_reverser_for_no_workload_triage() -> None:
prompt = (_PROMPTS / "decepticon.md").read_text()

assert "Basic triage / Radare2 work still goes to `reverser`" in prompt
assert "only for Ghidra MCP / headless decompilation" in prompt
assert 'Do NOT block binary triage just because `ops_start("reversing")` fails' in prompt


def test_reverser_prompt_has_radare2_fallback_path() -> None:
prompt = (_PROMPTS / "reverser.md").read_text()

assert "RADARE2" in prompt
assert "bin_r2_script" in prompt
assert "Radare2/r2" in prompt


def test_reverser_spec_advertises_basic_triage_without_workload() -> None:
agent_src = _REVERSER_AGENT.read_text()

assert "Radare2-assisted basic triage without the reversing workload" in agent_src


def test_reverser_skill_catalog_names_radare2_fallback() -> None:
skill = _REVERSER_SKILL.read_text()

assert "Radare2" in skill
assert "bin_r2_script" in skill


def test_reverser_routes_vmprotect_vmp2_themida_workflow() -> None:
prompt = (_PROMPTS / "reverser.md").read_text()
catalog = _REVERSER_SKILL.read_text()

assert "VMProtect / VMP2 / Themida" in prompt
assert "/skills/standard/reverser/virtualized-protectors/SKILL.md" in catalog
assert "VMProtect / VMP2 / Themida" in catalog


def test_virtualized_protectors_skill_captures_backengineering_workflow() -> None:
skill = _VIRTUALIZED_SKILL.read_text()

assert "Back Engineering Labs" in skill
assert "VMProtect 2" in skill
assert "Themida" in skill
assert "VMEnter" in skill
assert "VIP" in skill
assert "VMEXIT" in skill
assert "Avoid brittle VM-handler pattern matching" in skill
assert "incremental lifting and control-flow recovery" in skill
Loading