diff --git a/packages/decepticon/decepticon/agents/prompts/standard/decepticon.md b/packages/decepticon/decepticon/agents/prompts/standard/decepticon.md index 97eb5d26c..2f69f600b 100644 --- a/packages/decepticon/decepticon/agents/prompts/standard/decepticon.md +++ b/packages/decepticon/decepticon/agents/prompts/standard/decepticon.md @@ -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("", ...)` whose workload row above applies, call `ops_start("")`. **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 `` 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`. diff --git a/packages/decepticon/decepticon/agents/prompts/standard/reverser.md b/packages/decepticon/decepticon/agents/prompts/standard/reverser.md index 3e3fa8991..2aa536943 100644 --- a/packages/decepticon/decepticon/agents/prompts/standard/reverser.md +++ b/packages/decepticon/decepticon/agents/prompts/standard/reverser.md @@ -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 - 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/.md`. Cross-reference secrets, imports, and crashes from that file. - Version strings from bin_strings feed cve_lookup / cve_by_package — @@ -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. diff --git a/packages/decepticon/decepticon/agents/standard/reverser.py b/packages/decepticon/decepticon/agents/standard/reverser.py index 62889f15b..ede06edbf 100644 --- a/packages/decepticon/decepticon/agents/standard/reverser.py +++ b/packages/decepticon/decepticon/agents/standard/reverser.py @@ -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." ), diff --git a/packages/decepticon/decepticon/skills/.graph/skills.cypher b/packages/decepticon/decepticon/skills/.graph/skills.cypher index 3bb25542a..6cbd7f804 100644 --- a/packages/decepticon/decepticon/skills/.graph/skills.cypher +++ b/packages/decepticon/decepticon/skills/.graph/skills.cypher @@ -1,5 +1,5 @@ // AUTOGENERATED by decepticon.skillogy.builder. Do not edit by hand. -// nodes=2132 edges=5733 +// nodes=2133 edges=5735 // === nodes === MERGE (n:AssetType {name: 'active-directory'}) @@ -935,13 +935,15 @@ SET n.aatmf_tactic_raw = [], n.allowed_tools = ['Bash Read Write'], n.body = '# MERGE (n:Skill {name: 'reverser-malware-triage'}) SET n.aatmf_tactic_raw = [], n.allowed_tools = ['Bash Read Write'], n.body = '# Malware Triage — 15 minute first verdict\n\nYou have a suspicious binary. Goal: in 15 minutes, decide CLEAN / SUSPICIOUS / MALICIOUS / NEEDS-DEEPER.\n\n## Phase 1: Static (5 min)\n\n```bash\n# 1. File format\nfile sample.bin\nexiftool sample.bin # author / compile timestamp / version\n\n# 2. Hash + reputation\nsha256sum sample.bin\n# Submit to: VirusTotal, MalwareBazaar, IntelX, Joe Sandbox, ANY.RUN\n# Often the verdict already exists — saves you 14 minutes.\n\n# 3. Strings — fast triage signal\nstrings -n 8 sample.bin | sort -u | head -100\nstrings -e l -n 8 sample.bin | sort -u | head -50 # wide (UTF-16) strings on Windows\n\n# Suspicious strings to grep for:\nstrings sample.bin | grep -iE \'http|https|wmic|powershell|cmd.exe|temp|appdata|amsi|defender|reflectiveloader\'\n\n# 4. Format-specific: PE\nperesearcher sample.exe # OR python pefile\npython3 -c \'\nimport pefile\np = pefile.PE("sample.exe")\nprint("Compile time:", p.FILE_HEADER.TimeDateStamp)\nprint("Sections:", [(s.Name.decode().rstrip("\\x00"), s.SizeOfRawData, s.get_entropy()) for s in p.sections])\nprint("Imports:", [(e.dll.decode(), [i.name.decode() if i.name else hex(i.ordinal) for i in e.imports]) for e in p.DIRECTORY_ENTRY_IMPORT])\n\'\n\n# 5. Entropy → packed?\npython3 -c \'\nimport math\ndata = open("sample.bin","rb").read()\ncounts = [data.count(bytes([b])) for b in range(256)]\ntotal = len(data)\nent = -sum((c/total)*math.log2(c/total) for c in counts if c)\nprint(f"Entropy: {ent:.3f} / 8 — {\'packed\' if ent > 7.5 else \'normal\'}")\n\'\n\n# 6. YARA against canonical rulesets\nyara -r /opt/yara-rules/ sample.bin\nyara -r /opt/Neo23x0-signature-base/ sample.bin\n```\n\n## Phase 2: Dynamic (5 min — in an isolated VM)\n\n```bash\n# Pre-flight (do this once, save snapshot)\n# - Disconnected network OR use INetSim/FakeNet-NG to fake services\n# - Procmon recording (Process / File / Network / Registry filters)\n# - Wireshark capturing on the snapshot\'s network adapter\n# - Fakedns / inetsim listening for DNS / HTTP / SMTP / FTP\n\n# Detonate\ncp sample.bin C:\\tmp\\sample.exe\n# Right-click → Run as admin OR sample.exe in cmd\n\n# Observe for 60-180 seconds, then take snapshot\n# Then revert VM for next run\n```\n\n### Things to look for\n\n| Signal | Verdict |\n|---|---|\n| Writes to `\\AppData\\Local\\Temp` then executes | Likely dropper |\n| Creates Run/RunOnce registry key | Persistence |\n| Schedules a task | Persistence |\n| Modifies firewall via netsh | Defense evasion |\n| Spawns powershell + LongStringEncoded | Stage 2 |\n| Network: HTTPS to a no-SNI IP | C2 callback |\n| DNS to a DGA-looking domain | C2 callback |\n| Reads process memory of lsass.exe / winlogon.exe | Credential theft |\n| Writes to userinit / shells / image-file-exec-options | Persistence |\n| Touches `\\Microsoft\\Cryptography\\Defaults\\Provider` | Cert injection |\n\n## Phase 3: Unpack (if entropy was high, optional 5 min)\n\n```bash\n# In dynamic VM, after detonation, dump memory:\n# Scylla (UI) → attach to process, dump PE image\n# OR PE-sieve (command-line):\npe-sieve.exe /pid 1234 /dir dumped\n# OR DnSpy + DotNetReactorUnpacker for .NET\n# OR de4dot for obfuscated .NET\n\n# Then static-re the unpacked binary (Phase 1 strings/imports against the dump)\n```\n\n## Phase 4: Verdict + handoff\n\n| Verdict | Indicators | Next step |\n|---|---|---|\n| **CLEAN** | Known-good hash, signed, expected strings/imports, no suspicious behavior | Mark + move on |\n| **SUSPICIOUS** | Unsigned, low rep, mildly unusual imports/strings, no clear malicious behavior | Sandbox 30 min longer, YARA against custom rules |\n| **MALICIOUS** | C2 callback, drops files, persistence, credential theft, packed + evades VMs | IOC extraction, then deep RE (load `reverser/ghidra/SKILL.md`) |\n| **NEEDS-DEEPER** | High entropy, anti-analysis, custom-packed, no obvious signal | Unpack first (Phase 3), then re-triage |\n\n## IOC extraction template\n\nIf MALICIOUS:\n- Hashes (md5, sha1, sha256)\n- C2 domains / IPs (from PCAP)\n- Mutex names (Procmon: CreateMutex events)\n- File paths created\n- Registry keys modified\n- YARA signature (generate from unique strings/code)\n\n## Tooling cheatsheet\n\n| Stage | Tool | Use |\n|---|---|---|\n| Static (PE) | pefile, capa, exiftool, Detect It Easy (DIE) | Format + capability scan |\n| Static (ELF) | readelf, objdump, radare2 | Format + symbols |\n| Static (Mach-O) | jtool2, otool, MachOView | Format + symbols |\n| Dynamic | Cuckoo, CAPE, ANY.RUN, Joe Sandbox, Hatching Triage | Automated sandbox |\n| Network | Wireshark, mitmproxy, FakeNet-NG, INetSim | Traffic capture + fake services |\n| Memory | Volatility 3, PE-sieve, Scylla | Memory forensics + unpacking |\n| Disassembly | Ghidra, IDA, Binary Ninja | Full RE — see `reverser/ghidra/SKILL.md` |\n| YARA | yara, capa rules | Signature matching |\n\n## References\n\n- "Practical Malware Analysis" — Sikorski & Honig (still the canonical book)\n- MITRE ATT&CK — for behavior → technique mapping\n- Lenny Zeltser\'s "REMnux" — pre-built malware analysis distro\n- DEFCON "Malware Forensics" track recordings\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:8f9061034128a02818f872f6ed7dc5b363a7304a1fe700d10e786ff368849c5a', n.description = 'Fast malware triage workflow — static (PE/Mach-O/ELF format, strings, imports, signatures, entropy/packed indicators), dynamic (sandbox with INetSim, Wireshark, Process Monitor, Procmon, time-shift), unpack (Scylla/PE-sieve), then full RE with Ghidra/IDA. Designed for ≤15 min initial verdict.', n.mitre_attack_raw = ['T1059', 'T1518'], n.path = '/skills/standard/reverser/malware-triage/SKILL.md', n.size_bytes = 4998, n.subdomain = 'reverse-engineering', n.tags_raw = ['malware', 'triage', 'sandbox', 'ghidra'], n.upstream_ref_raw = '', n.when_to_use = 'malware triage sample first look static dynamic sandbox cuckoo capemon inetsim wireshark procmon unpack packed entropy yara'; MERGE (n:Skill {name: 'reverser-overview'}) -SET n.aatmf_tactic_raw = [], n.allowed_tools = [], n.body = '# Reverser Skill Catalog\n\n## Playbooks\n| Skill | Use for |\n|---|---|\n| `/skills/standard/reverser/triage/SKILL.md` | First-pass ELF/PE/Mach-O triage |\n| `/skills/standard/reverser/firmware/SKILL.md` | Router / IoT firmware extraction |\n| `/skills/standard/reverser/packer-unpacking/SKILL.md` | UPX / ASPack / Themida / VMProtect |\n| `/skills/standard/reverser/rop-chain/SKILL.md` | Gadget hunting for exploit dev |\n| `/skills/standard/reverser/anti-debug-bypass/SKILL.md` | IsDebuggerPresent, ptrace, NtGlobalFlag |\n| `/skills/standard/reverser/ghidra/SKILL.md` | Deep Ghidra analysis — decompile, xrefs, imports, P-code |\n\n## Workflow\n1. `ghidra_status` — check Ghidra MCP bridge and headless availability\n2. `bin_identify` — format, arch, NX/PIE\n3. `bin_packer` — entropy + signature\n4. If packed → follow the packer-unpacking skill, re-identify after unpack\n5. `bin_strings` — category=url/ip/crypto/secret/version to seed the graph\n6. `bin_symbols_report` — risk bucket classification\n7. Version strings → `cve_lookup` + `cve_by_package`\n8. `ghidra_analyze` for full analysis, or `bin_ghidra_script` / `bin_r2_script` as fallback\n9. `ghidra_decompile` on interesting functions, `ghidra_xrefs` on dangerous imports\n10. Record every observation in the knowledge graph\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:b11692b82b1b50458ed6dc1f29a027a5d220f88d562ea6b1558a52c929d749dc', n.description = 'Root pointer for the binary reversing lane. Covers triage, string extraction, packer unpacking, symbol risk, ROP, Ghidra deep analysis, and firmware extraction.', n.mitre_attack_raw = [], n.path = '/skills/standard/reverser/SKILL.md', n.size_bytes = 1326, n.subdomain = 'reverse-engineering', n.tags_raw = [], n.upstream_ref_raw = 'Decepticon reverser lane catalog — Ghidra, AFL++, libFuzzer, binwalk, and binary triage tooling', n.when_to_use = 'reverser binary reversing triage strings packer unpack rop ghidra firmware overview routing'; +SET n.aatmf_tactic_raw = [], n.allowed_tools = [], n.body = '# Reverser Skill Catalog\n\n## Playbooks\n| Skill | Use for |\n|---|---|\n| `/skills/standard/reverser/triage/SKILL.md` | First-pass ELF/PE/Mach-O triage |\n| `/skills/standard/reverser/firmware/SKILL.md` | Router / IoT firmware extraction |\n| `/skills/standard/reverser/packer-unpacking/SKILL.md` | UPX / ASPack / Themida / VMProtect |\n| `/skills/standard/reverser/virtualized-protectors/SKILL.md` | VMProtect / VMP2 / Themida workflow |\n| `/skills/standard/reverser/rop-chain/SKILL.md` | Gadget hunting for exploit dev |\n| `/skills/standard/reverser/anti-debug-bypass/SKILL.md` | IsDebuggerPresent, ptrace, NtGlobalFlag |\n| `/skills/standard/reverser/ghidra/SKILL.md` | Deep Ghidra analysis — decompile, xrefs, imports, P-code |\n\n## Workflow\n1. `ghidra_status` — check Ghidra MCP bridge and headless availability\n2. `bin_identify` — format, arch, NX/PIE\n3. `bin_packer` — entropy + signature\n4. If packed → follow the packer-unpacking skill, re-identify after unpack\n5. `bin_strings` — category=url/ip/crypto/secret/version to seed the graph\n6. `bin_symbols_report` — risk bucket classification\n7. Version strings → `cve_lookup` + `cve_by_package`\n8. `ghidra_analyze` for full analysis, or `bin_ghidra_script` / `bin_r2_script` for headless Ghidra / Radare2 fallback\n9. `ghidra_decompile` on interesting functions, `ghidra_xrefs` on dangerous imports\n10. Record every observation in the knowledge graph\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:b42639b9f96999b88dcfc9bd0ac4a7982bb62bcc1fa1c120026a5503c8654d4a', n.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.', n.mitre_attack_raw = [], n.path = '/skills/standard/reverser/SKILL.md', n.size_bytes = 1455, n.subdomain = 'reverse-engineering', n.tags_raw = [], n.upstream_ref_raw = 'Decepticon reverser lane catalog — Ghidra, Radare2, Back Engineering VMProtect/Themida research, AFL++, libFuzzer, binwalk, and binary triage tooling', n.when_to_use = 'reverser binary reversing triage strings packer unpack rop ghidra firmware VMProtect VMP2 Themida virtualized protectors overview routing'; MERGE (n:Skill {name: 'reverser-ransomware-analysis'}) SET n.aatmf_tactic_raw = [], n.allowed_tools = ['Bash Read Write'], n.body = '# Ransomware Analysis\n\nIdentify ransomware families, analyze encryption implementations, attempt key recovery, and extract IOCs for threat intelligence and incident response.\n\n## Quick Reference\n\n```bash\n# Identify ransomware family from ransom note\n# Upload to: id-ransomware.malwarehunterteam.com\n# Or match locally:\nstrings ransom_note.txt | head -20\n\n# Check encrypted file extension\nls -la /path/to/encrypted/ | head -20\n# Common: .lockbit, .revil, .conti, .blackcat, .encrypted, .crypt\n\n# Check for shadow copy deletion (recovery inhibition)\nstrings | grep -iE \'vssadmin|wmic.*shadowcopy|bcdedit|wbadmin\'\n\n# Entropy analysis on encrypted files\npython3 -c "\nimport math\ndata=open(\'encrypted_file\',\'rb\').read()\nc=[data.count(bytes([b])) for b in range(256)]\nt=len(data)\ne=-sum((x/t)*math.log2(x/t) for x in c if x)\nprint(f\'Entropy: {e:.3f}/8 — {\\"fully encrypted\\" if e > 7.9 else \\"partial/header encryption\\" if e > 7.0 else \\"not encrypted\\"}\')"\n\n# Check Emsisoft/NoMoreRansom for free decryptors\n# https://www.nomoreransom.org/en/decryption-tools.html\n# https://www.emsisoft.com/en/ransomware-decryption/\n```\n\n## MITRE ATT&CK Mapping\n\n| Technique | ID | How It Appears |\n|---|---|---|\n| Data Encrypted for Impact | T1486 | File encryption using AES/ChaCha20 + RSA key wrap |\n| Inhibit System Recovery | T1490 | vssadmin delete shadows, bcdedit, wbadmin delete catalog |\n| Service Stop | T1489 | Stopping SQL, Exchange, backup services before encryption |\n| System Shutdown/Reboot | T1529 | Forced reboot after encryption; bootlocker ransomware |\n| Data Destruction | T1485 | Wiper variants masquerading as ransomware |\n| Exfiltration Over C2 | T1041 | Double extortion: data exfil before encryption |\n| Defacement: Internal | T1491.001 | Desktop wallpaper change to ransom note |\n\n## 1. Family Identification\n\nDetermine which ransomware family you\'re dealing with.\n\n```bash\n# Method 1: Ransom note analysis\ncat ransom_note.txt\n# Key identifiers:\n# - Tor .onion URL → payment portal (extract for IOC)\n# - Bitcoin/Monero wallet address\n# - Unique victim ID / personal key\n# - File extension mentioned in note\n\n# Method 2: Encrypted file extension mapping\n# .lockbit → LockBit 2.0/3.0\n# .revil / .sodinokibi → REvil/Sodinokibi\n# .CONTI → Conti\n# .blackcat / random 6-7 char → BlackCat/ALPHV\n# .royal → Royal\n# .akira → Akira\n# .play → Play\n# .8base → 8Base (Phobos variant)\n# . → often Phobos family\n\n# Method 3: ID Ransomware (web-based)\n# Upload: ransom note + encrypted file sample\n# https://id-ransomware.malwarehunterteam.com/\n\n# Method 4: Binary analysis\nstrings | grep -iE \'lockbit\\|revil\\|conti\\|blackcat\\|phobos\\|dharma\\|stop\\|djvu\'\nyara -r /opt/yara-rules/ransomware/ \n\n# Method 5: Mutex / named pipe identification\nstrings | grep -iE \'Global\\\\|mutex\\|pipe\'\n# Known mutexes: "Global\\LockBit" → LockBit\n```\n\n## 2. Encryption Scheme Analysis\n\nReverse the crypto implementation to assess recoverability.\n\n```bash\n# Identify crypto libraries / APIs used\nstrings | grep -iE \'CryptEncrypt\\|CryptGenKey\\|CryptImportKey\\|CryptAcquireContext\'\nstrings | grep -iE \'AES\\|RSA\\|ChaCha\\|Salsa\\|Blowfish\\|RC4\\|ECDH\\|Curve25519\'\nstrings | grep -iE \'BCrypt\\|NCrypt\\|OpenSSL\\|mbedtls\\|sodium\\|libcrypto\'\n\n# Common ransomware crypto patterns:\n# Pattern A: RSA-2048 master key + AES-256-CBC per file (LockBit, Conti)\n# Pattern B: Curve25519 + XSalsa20 (BlackCat/ALPHV — Rust-based)\n# Pattern C: RSA-2048 + ChaCha20 per file (REvil, Hive)\n# Pattern D: Hardcoded AES key (weak — decryptable!) (older Dharma, STOP/Djvu offline)\n\n# Check import table for crypto APIs\npython3 << \'EOF\'\nimport pefile\npe = pefile.PE("")\nfor entry in pe.DIRECTORY_ENTRY_IMPORT:\n dll = entry.dll.decode()\n for imp in entry.imports:\n name = imp.name.decode() if imp.name else f"ord_{imp.ordinal}"\n if any(k in name.lower() for k in [\'crypt\', \'rsa\', \'aes\', \'key\', \'encrypt\', \'hash\', \'rand\']):\n print(f" {dll}: {name}")\nEOF\n\n# Analyze encrypted file structure\npython3 << \'EOF\'\nimport struct\nwith open("encrypted_file.lockbit", "rb") as f:\n # Many ransomware families append metadata to encrypted files\n f.seek(-256, 2) # Read last 256 bytes\n trailer = f.read()\n print("Trailer hex:", trailer.hex())\n # Look for:\n # - Encrypted AES key (RSA-encrypted, typically 128/256 bytes)\n # - Original file size\n # - IV / nonce\n # - File marker / magic bytes\n\n # Check file header\n f.seek(0)\n header = f.read(64)\n print("Header hex:", header.hex())\n # Full-file encryption: high entropy from byte 0\n # Partial encryption: original header partially intact\nEOF\n```\n\n## 3. Key Recovery Techniques\n\n```bash\n# Technique 1: Weak/Hardcoded Key (STOP/Djvu offline key, older variants)\n# If ransomware uses CryptGenRandom but falls back on failure:\nstrings | grep -c "CryptGenRandom"\n# If absent → may use deterministic key derivation (time-based, PID-based)\n\n# Technique 2: Memory forensics — extract key from process memory\nvol3 -f memory.raw windows.memmap --pid --dump\n# Search dump for AES key schedule patterns\npython3 << \'EOF\'\nimport re\ndata = open("pid..dmp", "rb").read()\n# AES-256 key schedule: 240 bytes with specific expansion pattern\n# Search for high-entropy 32-byte sequences near CryptoAPI structures\ncandidates = []\nfor i in range(len(data) - 32):\n block = data[i:i+32]\n # Quick entropy check\n unique = len(set(block))\n if unique > 28: # High byte diversity suggests key material\n candidates.append((i, block.hex()))\nprint(f"Found {len(candidates)} candidates")\nfor off, h in candidates[:20]:\n print(f" 0x{off:08x}: {h}")\nEOF\n\n# Technique 3: Known-plaintext attack\n# If you have the original unencrypted file AND the encrypted version:\npython3 << \'EOF\'\norig = open("original.docx", "rb").read()\nenc = open("original.docx.encrypted", "rb").read()\n# XOR to recover keystream (if XOR/stream cipher was used)\nkeystream = bytes(a ^ b for a, b in zip(orig, enc))\nprint("First 64 bytes of keystream:", keystream[:64].hex())\n# If keystream repeats → short key XOR → trivially breakable\nEOF\n\n# Technique 4: Flawed PRNG / implementation bugs\n# WannaCry: CryptGenRandom not called → primes recoverable from memory\n# Some Dharma: PID + timestamp as seed → predictable key\n# GandCrab v1: RSA key generation flaw → Bitdefender decryptor\n\n# Technique 5: Check for existing decryptors\n# NoMoreRansom project: nomoreransom.org\n# Emsisoft: emsisoft.com/ransomware-decryption\n# Kaspersky: noransom.kaspersky.com\n# Avast: avast.com/ransomware-decryption-tools\n```\n\n## 4. Recovery Inhibition Analysis\n\n```bash\n# Shadow copy deletion\nstrings | grep -iE \'vssadmin\\s+delete\\|shadows\\s*/all\'\nstrings | grep -iE \'wmic\\s+shadowcopy\\s+delete\'\nstrings | grep -iE \'Get-WmiObject.*ShadowCopy.*Delete\'\n\n# Boot recovery disabling\nstrings | grep -iE \'bcdedit.*recoveryenabled.*no\'\nstrings | grep -iE \'bcdedit.*bootstatuspolicy.*ignoreallfailures\'\n\n# Backup catalog deletion\nstrings | grep -iE \'wbadmin\\s+delete\\s+catalog\'\nstrings | grep -iE \'delete\\s+systemstatebackup\'\n\n# Service stopping (databases, backups, AV)\nstrings | grep -iE \'net\\s+stop\\|sc\\s+stop\\|taskkill\\|Stop-Service\'\n# Common targets: MSSQLSERVER, SQLAgent, MySQL, oracle, veeam, backup, sophos, defender\n\n# Attempt shadow copy recovery (if deletion failed or was partial)\nvssadmin list shadows\n# If shadows exist → mount and recover files\n\n# Check for Volume Shadow Copy remnants in raw disk\n# Even after vssadmin delete, data may persist until overwritten\n```\n\n## 5. Ransom Note Parsing and IOC Extraction\n\n```bash\n# Extract IOCs from ransom note\npython3 << \'EOF\'\nimport re\n\nwith open("ransom_note.txt", "r", errors="ignore") as f:\n note = f.read()\n\n# Extract Tor onion URLs\nonions = re.findall(r\'[a-z2-7]{16,56}\\.onion\', note)\nprint("Tor URLs:", onions)\n\n# Extract Bitcoin addresses (P2PKH, P2SH, Bech32)\nbtc = re.findall(r\'\\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\\b\', note)\nbtc += re.findall(r\'\\bbc1[a-zA-HJ-NP-Z0-9]{25,90}\\b\', note)\nprint("BTC addresses:", btc)\n\n# Extract Monero addresses\nxmr = re.findall(r\'\\b4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}\\b\', note)\nprint("XMR addresses:", xmr)\n\n# Extract email addresses\nemails = re.findall(r\'[\\w.+-]+@[\\w-]+\\.[\\w.]+\', note)\nprint("Emails:", emails)\n\n# Extract victim/personal IDs\nvictim_ids = re.findall(r\'(?:ID|key|token|code)\\s*[:=]\\s*([A-Za-z0-9+/=\\-]{16,})\', note, re.I)\nprint("Victim IDs:", victim_ids)\nEOF\n\n# Trace Bitcoin wallet for attribution\n# blockchain.com/explorer or blockchair.com\n# Check if wallet is tagged in ransomwhere.re database\n```\n\n## 6. Binary-Level Behavioral Analysis\n\n```bash\n# File enumeration logic\nstrings | grep -iE \'\\.doc\\|\\.xls\\|\\.pdf\\|\\.jpg\\|\\.png\\|\\.sql\\|\\.mdb\\|\\.zip\'\n# Ransomware typically targets specific file extensions and skips system files\n\n# Exclusion list (files/dirs ransomware avoids to keep OS bootable)\nstrings | grep -iE \'windows\\|system32\\|boot\\|ntldr\\|bootmgr\\|\\.exe\\|\\.dll\\|\\.sys\'\n# Skipping these is characteristic of ransomware (vs wipers)\n\n# Network enumeration (worm-like propagation)\nstrings | grep -iE \'NetShareEnum\\|WNetOpenEnum\\|GetAdaptersInfo\\|IcmpSendEcho\'\n# SMB scanning → lateral movement before encryption\n\n# Process/service termination commands\nstrings | grep -iE \'taskkill.*sql\\|taskkill.*oracle\\|taskkill.*backup\'\n\n# Privilege escalation\nstrings | grep -iE \'SeDebugPrivilege\\|AdjustTokenPrivileges\\|runas\\|ShellExecute.*admin\'\n\n# Anti-analysis checks\nstrings | grep -iE \'IsDebuggerPresent\\|CheckRemoteDebugger\\|GetSystemDefaultLangID\\|GetKeyboardLayout\'\n# Language check: many ransomware families skip CIS countries (Russian keyboard = exit)\n```\n\n## Tools & Resources\n\n| Tool | Purpose | Install |\n|---|---|---|\n| ID Ransomware | Family identification from note/sample | id-ransomware.malwarehunterteam.com |\n| CyberChef | Crypto analysis, encoding/decoding | gchq.github.io/CyberChef |\n| Emsisoft Decryptors | Free decryption tools | emsisoft.com/ransomware-decryption |\n| NoMoreRansom | Decryptor repository | nomoreransom.org |\n| ransomwhe.re | Ransomware payment tracking | ransomwhe.re |\n| Volatility 3 | Memory forensics for key recovery | github.com/volatilityfoundation/volatility3 |\n| YARA | Ransomware family signatures | github.com/Yara-Rules/rules |\n| vssadmin | Shadow copy management | Built into Windows |\n| Raccine | Ransomware vaccine (VSS protection) | github.com/Neo23x0/Raccine |\n\n## Detection Signatures\n\n| Indicator | Description | Detection |\n|---|---|---|\n| Mass file rename with new extension | Bulk encryption in progress | Sysmon File Create (Event 11) burst |\n| `vssadmin delete shadows /all /quiet` | Shadow copy deletion | Sysmon Event ID 1 + command line |\n| `bcdedit /set recoveryenabled no` | Recovery disabling | Sysmon Event ID 1 |\n| Ransom note dropped in every directory | Encryption complete signal | File create events for `README.txt`, `DECRYPT.txt` |\n| High-entropy file writes | Active encryption | I/O monitoring: write entropy > 7.9 |\n| Services stopped in rapid succession | Pre-encryption service kill | Windows Event Log 7036 burst |\n| Network share enumeration | Lateral movement / spread | NetShareEnum API calls, SMB traffic |\n| Mutex creation with known names | Family identification | Sysmon Event ID 17 |\n\n## Error Handling & Edge Cases\n\n| Issue | Resolution |\n|---|---|\n| Ransomware sample won\'t run (anti-VM) | Patch anti-analysis checks; use bare-metal analysis host |\n| No ransom note found | Check alternate locations: Desktop, every drive root, C:\\Users\\Public |\n| Encrypted file has zero additional bytes | Header-only encryption — may be partially recoverable |\n| Wiper disguised as ransomware | Check if decryption key actually exists; analyze if key is generated but never saved |\n| Double-encrypted (two families) | Decrypt in reverse order; identify families by extension stacking |\n| Network-propagating variant | Isolate host immediately; analyze SMB/RDP lateral movement code |\n| Key deleted from memory | Cold boot attack; check pagefile.sys, hiberfil.sys for key remnants |\n\n## Decision Gate\n\n```\nIF ransom note present:\n → Upload to ID Ransomware for family identification\n → Check NoMoreRansom / Emsisoft for free decryptor\n → Extract IOCs (onion URLs, BTC wallets, emails)\n → If decryptor exists → decrypt and recover\nIF no decryptor available:\n → Analyze encryption implementation in binary\n → Check for crypto weaknesses (hardcoded key, weak PRNG, implementation bugs)\n → Attempt memory forensics for key material\n → Check for intact shadow copies / backup catalog\nIF wiper suspected (no real key):\n → Document as destructive attack, not ransomware\n → Focus on IOC extraction and attribution\n → File recovery via disk carving (PhotoRec, foremost)\n```\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:70dacf84dbd440cf1f17a00cc99ff3bfb5b19b6e315f7bc6c0820e3e6f665eea', n.description = 'Ransomware family identification and analysis — encryption scheme identification, key recovery techniques, ransom note parsing, shadow copy/recovery inhibition analysis, decryptor availability check, and IOC extraction for common ransomware families.', n.mitre_attack_raw = ['T1486', 'T1490', 'T1489'], n.path = '/skills/standard/reverser/ransomware-analysis/SKILL.md', n.size_bytes = 12957, n.subdomain = 'reverse-engineering', n.tags_raw = ['ransomware', 'encryption', 'decryptor', 'key recovery', 'incident response'], n.upstream_ref_raw = '', n.when_to_use = 'ransomware encrypted files ransom note decrypt recovery key lockbit revil conti blackcat alphv encryption crypto aes rsa chacha payment bitcoin'; MERGE (n:Skill {name: 'reverser-rootkit-analysis'}) SET n.aatmf_tactic_raw = [], n.allowed_tools = ['Bash Read Write'], n.body = '# Rootkit Analysis\n\nDetect and analyze rootkits operating at kernel, boot, and firmware levels — from user-mode hiding techniques through UEFI implants and hypervisor-based rootkits.\n\n## Quick Reference\n\n```bash\n# Quick kernel rootkit scan (Windows, run as admin)\ngmer.exe /scan\n\n# System-wide hidden process / driver detection\nvolatility3 -f memory.raw windows.pslist vs windows.psscan\n# Processes in psscan but NOT in pslist → hidden by DKOM\n\n# UEFI firmware extraction and analysis\nchipsec_util.py spi dump firmware.bin\nUEFIExtract firmware.bin\n\n# Check for unsigned kernel drivers\nsigcheck -u -e C:\\Windows\\System32\\drivers\\*.sys\n\n# Scan for SSDT hooks\nvolatility3 -f memory.raw windows.ssdt\n```\n\n## MITRE ATT&CK Mapping\n\n| Technique | ID | How It Appears |\n|---|---|---|\n| System Firmware | T1542.001 | UEFI rootkit implanted in SPI flash (LoJax, MosaicRegressor, CosmicStrand) |\n| Bootkit | T1542.003 | MBR/VBR modification to load malicious code before OS (TDL4, Rovnix, ESPecter) |\n| Rootkit | T1014 | Kernel object manipulation to hide processes, files, registry keys |\n| Boot or Logon Autostart: Kernel Modules | T1547.006 | Malicious kernel driver loaded at boot via service registry key |\n| Exploitation for Defense Evasion | T1211 | Vulnerable driver exploit (BYOVD) to load unsigned kernel code |\n| Virtualization/Sandbox Evasion | T1497 | Hypervisor rootkit detecting/evading analysis environment |\n\n## 1. User-Mode Rootkit Detection\n\nDetect rootkits that hook user-mode APIs to hide artifacts.\n\n```bash\n# Compare API hook status — IAT/EAT/inline hooks\n# Use API Monitor or manually check ntdll.dll integrity\n\n# Volatility: detect IAT hooks in processes\nvol3 -f memory.raw windows.iat --pid \n\n# Check for LD_PRELOAD / dylib injection (Linux/macOS)\n# Linux:\ncat /proc//maps | grep -v "$(ls /lib/ /usr/lib/ 2>/dev/null | tr \'\\n\' \'|\')"\necho $LD_PRELOAD\ncat /etc/ld.so.preload\n\n# Windows: compare loaded DLLs against known-good baseline\nvol3 -f memory.raw windows.dlllist --pid \n# Look for: DLLs loaded from temp dirs, DLLs not on disk, unknown publishers\n\n# Cross-view detection: compare user-mode API results vs kernel data\n# If FindFirstFile misses files that raw NTFS parsing finds → user-mode hook\npython3 << \'EOF\'\nimport os, subprocess\n# User-mode listing\nuser_files = set(os.listdir("C:\\\\Windows\\\\System32\\\\drivers"))\n# Raw NTFS listing (bypasses API hooks)\nraw = subprocess.check_output(["rawcopy", "/listdir", "C:\\\\Windows\\\\System32\\\\drivers"])\nraw_files = set(raw.decode().strip().split(\'\\n\'))\nhidden = raw_files - user_files\nif hidden:\n print(f"[!] Hidden files detected: {hidden}")\nEOF\n```\n\n## 2. Kernel Rootkit Detection\n\nDetect DKOM, SSDT hooks, and malicious drivers.\n\n```bash\n# GMER scan (Windows — gold standard for kernel rootkit detection)\n# Run gmer.exe as Administrator\n# Checks: SSDT hooks, IDT hooks, IRP hooks, inline hooks,\n# hidden processes, hidden drivers, hidden files, hidden registry\n\n# Volatility cross-view process detection\nvol3 -f memory.raw windows.pslist > pslist.txt\nvol3 -f memory.raw windows.psscan > psscan.txt\n# Diff: processes in psscan but not pslist are DKOM-hidden\ncomm -23 <(sort psscan.txt) <(sort pslist.txt)\n\n# SSDT hook detection\nvol3 -f memory.raw windows.ssdt\n# All entries should point to ntoskrnl.exe or win32k.sys\n# Entries pointing elsewhere → hooked\n\n# Driver analysis\nvol3 -f memory.raw windows.drvscan\nvol3 -f memory.raw windows.modules\n# Look for:\n# - Drivers not on disk (loaded from memory only)\n# - Drivers loaded from unusual paths (temp, appdata)\n# - Drivers with no digital signature\n\n# IRP hook detection — malicious drivers hooking filesystem IRPs\nvol3 -f memory.raw windows.driverirp\n# NTFS driver IRP_MJ_DIRECTORY_CONTROL hooked → file hiding\n# NTFS driver IRP_MJ_CREATE hooked → file access interception\n\n# Linux kernel rootkit detection\n# Check loaded kernel modules\nlsmod\ncat /proc/modules\n# Verify module signature\nmodinfo | grep sig\n\n# Check syscall table integrity (requires kernel symbol access)\ncat /proc/kallsyms | grep sys_call_table\n# Compare function addresses against known-good System.map\n\n# chkrootkit / rkhunter (automated Linux rootkit scanners)\nchkrootkit\nrkhunter --check --skip-keypress\n```\n\n## 3. BYOVD (Bring Your Own Vulnerable Driver) Analysis\n\n```bash\n# Attackers load a signed-but-vulnerable driver to gain kernel access\n# Then use the driver\'s read/write primitives to disable security\n\n# Known vulnerable drivers database: loldrivers.io\n# Common BYOVD targets:\n# - RTCore64.sys (MSI Afterburner) — arbitrary physical memory R/W\n# - dbutil_2_3.sys (Dell) — arbitrary memory R/W\n# - gdrv.sys (GIGABYTE) — physical memory R/W\n# - capcom.sys — disables SMEP, executes user-mode code in ring 0\n\n# Detect BYOVD: check for known vulnerable driver hashes\npython3 << \'EOF\'\nimport hashlib, os, json\n\n# LOLDrivers hash list (download from loldrivers.io)\nvuln_hashes = set() # Populate from loldrivers.io API or CSV\n\ndrivers_dir = r"C:\\Windows\\System32\\drivers"\nfor f in os.listdir(drivers_dir):\n path = os.path.join(drivers_dir, f)\n if os.path.isfile(path) and f.endswith(\'.sys\'):\n h = hashlib.sha256(open(path, \'rb\').read()).hexdigest()\n if h in vuln_hashes:\n print(f"[!] VULNERABLE DRIVER: {f} — {h}")\nEOF\n\n# Check driver signature status\nsigcheck.exe -u -e C:\\Windows\\System32\\drivers\\*.sys\n# -u shows unsigned drivers; -e scans executables only\n\n# Volatility: find recently loaded drivers\nvol3 -f memory.raw windows.drvscan | sort -k3 -t\'|\'\n```\n\n## 4. Bootkit Analysis\n\nDetect and analyze MBR/VBR/ESP modifications.\n\n```bash\n# Dump MBR (first 512 bytes of disk)\ndd if=/dev/sda bs=512 count=1 of=mbr.bin 2>/dev/null\n# On Windows: use FTK Imager or dd for Windows\n\n# Analyze MBR\npython3 << \'EOF\'\ndata = open("mbr.bin", "rb").read()\n# Check boot signature\nif data[510:512] != b\'\\x55\\xAA\':\n print("[!] Invalid MBR signature — corrupted or wiped")\nelse:\n print("[+] MBR signature valid")\n\n# Check for known bootkit signatures\nknown_sigs = {\n b\'\\xEB\\x5A\\x90\': "Standard Windows MBR",\n b\'\\xEB\\x63\\x90\': "GRUB MBR",\n}\nsig = data[:3]\nprint(f"Boot code signature: {sig.hex()}")\nprint(f"Identified: {known_sigs.get(sig, \'UNKNOWN — possible bootkit\')}")\n\n# Dump partition table\nimport struct\nfor i in range(4):\n entry = data[446 + i*16 : 446 + (i+1)*16]\n status, ptype = entry[0], entry[4]\n lba = struct.unpack(\'/dev/null\n\n# UEFI boot analysis — check ESP (EFI System Partition)\n# Mount ESP and inspect bootloaders\nmountvol S: /s\ndir S:\\EFI\\\n# Verify bootloader integrity\nsigcheck.exe S:\\EFI\\Microsoft\\Boot\\bootmgfw.efi\nsigcheck.exe S:\\EFI\\Boot\\bootx64.efi\n# ESPecter bootkit replaces bootmgfw.efi with a patched version\n\n# Compare against known-good bootloader hash\nsha256sum S:\\EFI\\Microsoft\\Boot\\bootmgfw.efi\n# Cross-reference with Microsoft\'s signed bootloader hashes\n```\n\n## 5. UEFI Firmware Analysis\n\nDetect firmware-level implants that survive OS reinstallation.\n\n```bash\n# Dump SPI flash firmware using chipsec\npython chipsec_util.py spi dump firmware.bin\n# Alternative: use hardware programmer (CH341A) for offline dump\n\n# Check UEFI write protection\npython chipsec_main.py -m common.bios_wp\n# BIOS Write Protect (BIOSWE) should be LOCKED\n# If unlocked → firmware can be modified from OS (implant vector)\n\n# Check SPI flash lock\npython chipsec_main.py -m common.spi_lock\n# SPI Protected Ranges should cover full flash region\n\n# Check Secure Boot status\npython chipsec_main.py -m common.secureboot.variables\n# Secure Boot should be ENABLED with valid PK/KEK/db\n\n# Extract and analyze firmware volumes\nUEFIExtract firmware.bin\n# Inspect extracted DXE drivers and runtime services\n\n# Search for known UEFI rootkit indicators\npython3 << \'EOF\'\nimport os\nextracted_dir = "firmware.bin.dump"\nsuspicious = []\nfor root, dirs, files in os.walk(extracted_dir):\n for f in files:\n path = os.path.join(root, f)\n try:\n data = open(path, \'rb\').read()\n # Known UEFI implant indicators\n if b\'LoJax\' in data or b\'SedUploader\' in data:\n suspicious.append((path, "LoJax indicator"))\n if b\'MosaicRegressor\' in data or b\'IntelUpdate\' in data:\n suspicious.append((path, "MosaicRegressor indicator"))\n if b\'CosmicStrand\' in data:\n suspicious.append((path, "CosmicStrand indicator"))\n # Generic: look for HTTP URLs in firmware modules (unusual)\n import re\n urls = re.findall(rb\'https?://[^\\x00\\s]{8,}\', data)\n if urls:\n suspicious.append((path, f"URLs found: {urls[:3]}"))\n except: pass\nfor path, reason in suspicious:\n print(f"[!] {reason}: {path}")\nEOF\n\n# Firmware integrity verification against vendor baseline\n# Download original firmware from vendor site → binary diff\npython3 -c "\na=open(\'firmware_original.bin\',\'rb\').read()\nb=open(\'firmware.bin\',\'rb\').read()\ndiffs=[(i,a[i],b[i]) for i in range(min(len(a),len(b))) if a[i]!=b[i]]\nprint(f\'Differences: {len(diffs)} bytes\')\nfor off,orig,mod in diffs[:20]:\n print(f\' 0x{off:08x}: {orig:02x} → {mod:02x}\')\n"\n```\n\n## 6. Hypervisor / Ring -1 Rootkit Detection\n\n```bash\n# Hypervisor rootkits (Blue Pill concept) run below the OS\n# Detection is inherently difficult — timing-based side channels\n\n# Check if running under unexpected hypervisor\n# CPUID leaf 0x1: ECX bit 31 = hypervisor present\npython3 << \'EOF\'\nimport struct, ctypes\n# Check CPUID for hypervisor bit\n# This requires native code execution or WMI\nimport subprocess\nresult = subprocess.check_output(\n ["powershell", "-c",\n "Get-WmiObject -Class Win32_ComputerSystem | Select-Object HypervisorPresent,Model"],\n text=True\n)\nprint(result)\n# If HypervisorPresent=True but no known hypervisor installed → suspicious\nEOF\n\n# Timing-based detection (RDTSC anomalies)\n# Hypervisor causes VM exits that add measurable latency\n# CPUID instruction under hypervisor takes ~1000+ cycles vs ~100 native\n\n# Check for known hypervisor rootkit artifacts\nvol3 -f memory.raw windows.modules | grep -iE "vbox\\|vmware\\|hv\\|hyperv"\n# But a true ring-1 rootkit may not appear in module lists\n\n# Physical memory access test (blocked by hypervisor)\n# chipsec can detect unexpected EPT (Extended Page Tables)\npython chipsec_main.py -m common.cpu.cpu_info\n```\n\n## 7. Linux Kernel Rootkit Analysis\n\n```bash\n# Check for LKM (Loadable Kernel Module) rootkits\nlsmod | sort\ncat /proc/modules | sort\n# Compare: hidden modules appear in /proc but not lsmod, or vice versa\n\n# Verify syscall table integrity\ncat /proc/kallsyms | grep -E "sys_(read|write|open|getdents|kill)"\n# Compare addresses against clean System.map\n\n# Check for /dev anomalies (rootkits often create hidden devices)\nls -la /dev/ | grep -vE "^[bcdlps]"\n\n# Detect hidden files via direct inode enumeration\ndebugfs -R "ls -l /" /dev/sda1 2>/dev/null\n\n# Check for process hiding\nls /proc/ | grep -E "^[0-9]+$" | sort -n > proc_pids.txt\nps -eo pid --no-headers | sort -n > ps_pids.txt\ncomm -23 proc_pids.txt ps_pids.txt # In /proc but not ps → suspicious\ncomm -13 proc_pids.txt ps_pids.txt # In ps but not /proc → rootkit hiding\n\n# Network socket hiding detection\nss -tulnp > ss_output.txt\ncat /proc/net/tcp /proc/net/tcp6 > proc_net.txt\n# Compare: hidden connections appear in /proc/net but not ss\n\n# Volatility for Linux memory images\nvol3 -f memory.lime linux.bash\nvol3 -f memory.lime linux.check_modules\nvol3 -f memory.lime linux.hidden_modules\nvol3 -f memory.lime linux.check_syscall\n```\n\n## Tools & Resources\n\n| Tool | Purpose | Install |\n|---|---|---|\n| GMER | Windows kernel rootkit scanner (GUI) | gmer.net |\n| Volatility 3 | Memory forensics framework | github.com/volatilityfoundation/volatility3 |\n| chipsec | UEFI/firmware security assessment | github.com/chipsec/chipsec |\n| UEFITool | UEFI firmware image parser/editor | github.com/LongSoft/UEFITool |\n| UEFIExtract | CLI firmware volume extractor | github.com/LongSoft/UEFITool |\n| Sigcheck | Authenticode signature verification | Sysinternals |\n| rkhunter | Linux rootkit scanner | rkhunter.sourceforge.net |\n| chkrootkit | Linux rootkit checker | chkrootkit.org |\n| LOLDrivers | Vulnerable driver database | loldrivers.io |\n| ESET UEFI scanner | UEFI module whitelist checker | eset.com |\n\n## Detection Signatures\n\n| Indicator | Description | Detection |\n|---|---|---|\n| SSDT entry pointing outside ntoskrnl | Kernel function hook | Volatility windows.ssdt |\n| Process in psscan but not pslist | DKOM process hiding | Volatility cross-view comparison |\n| Driver loaded from \\Temp or \\AppData | Suspicious kernel driver | Volatility windows.drvscan + path check |\n| BIOSWE bit unlocked | Firmware writable from OS | chipsec common.bios_wp |\n| SPI region not locked | Flash can be modified | chipsec common.spi_lock |\n| ESP bootloader hash mismatch | Modified bootloader (bootkit) | Hash comparison with vendor baseline |\n| Unsigned .sys file in drivers directory | Potentially malicious driver | Sigcheck -u scan |\n| Known BYOVD driver hash | Vulnerable driver exploitation | LOLDrivers hash comparison |\n\n## Error Handling & Edge Cases\n\n| Issue | Resolution |\n|---|---|\n| GMER crashes or hangs | Rootkit actively fighting scanner; try from WinPE/safe mode boot |\n| chipsec requires kernel driver | Run on Linux live USB; Windows needs admin + test signing |\n| Firmware dump size mismatch | SPI flash layout varies; use `chipsec_util spi info` for correct regions |\n| Hypervisor rootkit undetectable | Physical hardware analysis required; use JTAG/SPI programmer for firmware dump |\n| UEFI Secure Boot prevents chipsec | Boot Linux with Secure Boot disabled or MOK-signed chipsec driver |\n| Memory image too large | Use targeted Volatility plugins; filter by PID or address range |\n| Rootkit patches Volatility output | Use multiple analysis tools; cross-reference with raw memory search |\n| Anti-forensics: cleared event logs | Carve deleted entries from raw disk; check alternate log locations |\n\n## Decision Gate\n\n```\nIF suspicious hidden processes/files/network connections:\n → Capture full memory dump\n → Run Volatility cross-view detection (pslist vs psscan)\n → Check SSDT/IDT/IRP hooks\n → Scan for known vulnerable drivers (BYOVD)\nIF boot-level persistence suspected:\n → Dump and verify MBR/VBR integrity\n → Mount ESP and hash-verify bootloaders\n → Check Secure Boot configuration\nIF firmware implant suspected:\n → Dump SPI flash with chipsec\n → Extract and analyze UEFI volumes\n → Compare against vendor baseline firmware\n → Check BIOS write protect and SPI lock status\nIF Linux system:\n → Run chkrootkit + rkhunter\n → Compare /proc PIDs vs ps output\n → Verify syscall table addresses\n → Check for hidden kernel modules\nELSE:\n → Start with GMER quick scan (Windows) or rkhunter (Linux)\n → Escalate to memory forensics if scanner detects anomalies\n```\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:931f8cf6a71d4a345e98b76b12caa8a9038fc36c7d43b6c69150d4d77bf1b44a', n.description = 'Rootkit detection and analysis — UEFI rootkits, kernel-level rootkits, bootkits, DKOM techniques, SSDT/IDT/IRP hooking, hypervisor rootkits, and firmware implant detection using GMER, Volatility, chipsec, and UEFITool.', n.mitre_attack_raw = ['T1542.001', 'T1542.003', 'T1014'], n.path = '/skills/standard/reverser/rootkit-analysis/SKILL.md', n.size_bytes = 15242, n.subdomain = 'reverse-engineering', n.tags_raw = ['rootkit', 'UEFI', 'bootkit', 'kernel', 'firmware', 'Volatility', 'chipsec'], n.upstream_ref_raw = '', n.when_to_use = 'rootkit bootkit uefi firmware kernel driver ssdt hook dkom hidden process hypervisor mbr vbr implant bios spi flash ring0 ring-1'; MERGE (n:Skill {name: 'reverser-triage'}) SET n.aatmf_tactic_raw = [], n.allowed_tools = [], n.body = '# Binary Triage (fast path)\n\n## 1. Identify\n```\nbin_identify(path="/workspace/target")\n```\nRecord: format, arch, bitness, NX, PIE, entry point. Add `file` node with these props.\n\n## 2. Mitigation check\n```bash\n# checksec for extra signals (stack canary, RELRO, fortify)\napt-get install -y checksec 2>/dev/null || true\nchecksec --file=/workspace/target --format=json\n```\nAdd any missing mitigation as a `vulnerability` node with severity=medium.\n\n## 3. String harvest (categorized)\n```\nbin_strings(path="/workspace/target", category_filter="secret")\nbin_strings(path="/workspace/target", category_filter="url")\nbin_strings(path="/workspace/target", category_filter="crypto")\nbin_strings(path="/workspace/target", category_filter="version")\n```\nEach hit in secret/crypto → add a `secret` node. Each url/ip → consider\nadding an `entrypoint` or C2 candidate.\n\n## 4. Imports risk\n```bash\nnm -D /workspace/target 2>/dev/null | awk \'$2=="U"{print $3}\' > /tmp/imports.txt\n# or\nobjdump -T /workspace/target | awk \'/\\*UND\\*/{print $NF}\' > /tmp/imports.txt\n```\nFeed the list to `bin_symbols_report`. Anything with risk_score > 10 =\nworth deeper look.\n\n## 5. Version → CVE\nFor every version string found in step 3, call `cve_lookup` with the\ncandidate CVE IDs from `cve_by_package` if it\'s a known library.\n\n## 6. Handoff\nIf NX/PIE are both off AND there are dangerous C imports → this is a\nprime exploit-dev target; load `/skills/standard/reverser/rop-chain/SKILL.md`.\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:25dd78a3f537c8e10f6dfff64447d73cb681ef4d7b0a9c4c5b807c58f6aa4b00', n.description = 'Fast-path binary triage — identify format/arch/mitigations, grab high-signal strings and imports in under a minute.', n.mitre_attack_raw = [], n.path = '/skills/standard/reverser/triage/SKILL.md', n.size_bytes = 1466, n.subdomain = 'reverse-engineering', n.tags_raw = [], n.upstream_ref_raw = 'Decepticon reverser triage playbook — file / readelf / checksec / strings / nm', n.when_to_use = 'binary triage format architecture mitigations strings imports nm objdump readelf checksec file'; +MERGE (n:Skill {name: 'reverser-virtualized-protectors'}) +SET n.aatmf_tactic_raw = [], n.allowed_tools = [], n.body = '# VMProtect / VMP2 / Themida Workflow\n\nUse this for VMProtect 2, VMP2 tooling questions, Themida, CodeVirtualizer,\nand other VM-based protectors. Do not promise automatic devirtualization.\nMost wins come from disciplined recovery of VM control-flow facts.\n\n## Source Guidance\n\nBack Engineering Labs\' public VMP2/Themida work points to one durable rule:\nAvoid brittle VM-handler pattern matching. Handler layouts, opcode tables,\nand dispatch glue change too easily. Prefer incremental lifting and control-flow recovery with as little VM-specific logic as possible.\n\n## Loop\n\n1. `bin_identify` and `bin_packer` first; record format, arch, entropy, and\n protector strings.\n2. Use `bin_r2_script` or Ghidra to locate VMEnter stubs, handler-table\n references, VM context/virtual stack sections, and suspicious indirect\n dispatch loops.\n3. Recover VIP movement. For VMProtect 2, track bytecode/module loads feeding\n the indirect jump. For Themida, expect branch state in VM context and trace\n the branch-taken flag through the VPC update.\n4. Classify VMEXIT behavior: return to native epilog, call-shaped exit, or\n unsupported-instruction exit. Record stack displacement evidence.\n5. Prefer trace/lift plans that run simple optimizations to convergence:\n constant promotion over VM-private ranges, constant folding, instruction\n combination, branch folding, dead-store/dead-dependency cleanup, and stack\n pointer rewrite.\n6. If using VMP2-style tooling, treat `vmemu`/`vmprofiler`/`vmprofiler-cli`\n outputs as evidence, not ground truth. Re-check recovered paths in r2/Ghidra.\n7. Write findings with exact addresses, section names, vmenter candidates,\n VMEXIT classification, VIP source, and unresolved symbolic branches.\n\n## Report Shape\n\n- Protector: VMProtect / VMProtect 2 / Themida / CodeVirtualizer / unknown\n- Entrypoints: candidate VMEnter addresses and why\n- Dispatch: handler-table location, decrypt/transform clues, indirect jump path\n- VIP: source load or Themida branch flag/VPC update evidence\n- VMEXIT: stack displacement and native continuation/call target\n- Recovery plan: trace, lift, or manual simplification; no fake devirt claims\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:122031633f33aab88d0b3e7f12940bd65cec5ca83cb93af937386a334edea2c5', n.description = 'VMProtect, VMP2, Themida, and CodeVirtualizer reversing workflow using Radare2/Ghidra facts and Back Engineering Labs research guidance.', n.mitre_attack_raw = [], n.path = '/skills/standard/reverser/virtualized-protectors/SKILL.md', n.size_bytes = 2167, n.subdomain = 'reverse-engineering', n.tags_raw = [], n.upstream_ref_raw = 'Back Engineering Labs vmp2, vmhook, and Static Devirtualization of Themida research', n.when_to_use = 'VMProtect VMP2 VMProtect2 Themida CodeVirtualizer virtualized protector devirtualization VMEnter VMEXIT VIP handler table'; MERGE (n:Skill {name: 'reverser-yara-hunting'}) SET n.aatmf_tactic_raw = [], n.allowed_tools = ['Bash Read Write'], n.body = '# YARA Rule Authoring + Hunting\n\n## Anatomy of a good rule\n\n```yara\nimport "pe"\nimport "hash"\n\nrule MalFamily_DropperVariant_Q4_2024 {\n meta:\n author = "you"\n date = "2024-11-20"\n description = "MalFamily dropper, observed Q4 2024 campaign"\n hash = "deadbeefdeadbeefdeadbeefdeadbeef"\n tlp = "amber"\n confidence = "high"\n false_positives = "low (test against AV vendor samples)"\n mitre = "T1055"\n\n strings:\n $magic = { 4D 5A 90 00 03 } // MZ + DOS header\n $config_marker = { 7A 89 ?? ?? 4F 8C } // 6-byte signature, 2 wildcards\n $string1 = "loader_stage_2" wide\n $string2 = "cmd.exe /c " ascii\n $api1 = "VirtualAllocEx"\n $api2 = "WriteProcessMemory"\n $api3 = "CreateRemoteThread"\n // Avoid generic strings — they cause FPs.\n\n condition:\n uint16(0) == 0x5A4D and // PE header\n filesize < 5MB and // bounded — avoids huge-file scans\n $config_marker and // unique marker\n 2 of ($api1, $api2, $api3) and // at least 2 of the 3 process-inject APIs\n any of ($string*) and\n pe.imports("ws2_32.dll", "WSAStartup") and // network capability\n not pe.is_signed // unsigned\n}\n```\n\n## The condition tools you need\n\n```yara\n// Filesize / offset reads (no module)\nfilesize > 100KB and filesize < 5MB\nuint32(0x3c) > 0 and uint32(uint32(0x3c)) == 0x4550 // valid PE\n\n// "for" loops over offsets / sections\nfor any i in (0..pe.number_of_sections - 1):\n (pe.sections[i].name == ".text" and pe.sections[i].entropy > 7.0)\n\n// "of"\n1 of them // any one string\nall of ($a*) // all $a*-prefix strings\n3 of ($s1, $s2, $s3, $s4, $s5) // at least 3 of 5\n\n// Hash module\nhash.sha256(0, filesize) == "..." // exact-match rule\nhash.imphash() == "..." // PE import hash (fragile but high-signal)\n```\n\n## High-leverage patterns\n\n### Family-stable byte patterns\nLook for unique byte sequences that survive obfuscation:\n- Crypto constants (SHA-256 IVs, AES sbox, RC4 init pattern)\n- Hard-coded SID strings\n- C2 packet headers (magic bytes, fixed offsets)\n- Reflective loader entry stub bytes (Donut, Cobalt Strike\'s PE loader)\n\n### Capability rules\nDon\'t fingerprint a sample — fingerprint a TECHNIQUE:\n\n```yara\nrule Suspicious_ProcessInjection {\n meta: description = "VirtualAlloc + WriteProcessMemory + CreateRemoteThread = injection"\n condition:\n pe.imports("kernel32.dll", "VirtualAllocEx") and\n pe.imports("kernel32.dll", "WriteProcessMemory") and\n pe.imports("kernel32.dll", "CreateRemoteThread")\n}\n```\n\nThis catches every injector regardless of family. False-positive: legitimate tools (Procmon, IDA, debuggers). Reduce via additional conditions (unsigned, in temp dir, etc.).\n\n### Anti-analysis fingerprinting\n```yara\nrule AntiVm_VMware_Strings {\n strings:\n $s1 = "VMware" ascii nocase\n $s2 = "VBoxService" ascii nocase\n $s3 = "qemu" ascii nocase\n $s4 = { 56 4D 58 68 } // "VMXh" — VMware backdoor port magic\n condition: 2 of them\n}\n```\n\n## Hunting at scale\n\n### VirusTotal Retrohunt (paid)\n```yara\n// Upload rule to VT — runs against the past 90 days of submissions\n// Outputs new hashes matching the signature\n```\n\n### MalwareBazaar (free)\n```bash\n# yarafs.io / abuse.ch — runs YARA against MB corpus\ncurl https://mb-api.abuse.ch/api/v1/ -X POST -d \'query=get_yara&yara_rule=YourRule\'\n```\n\n### Local corpus scan\n```bash\n# Recursive scan\nyara -r rules/ /samples/\n# JSON output for downstream processing\nyara -r rules/ /samples/ -m -p 4 # -m: metadata, -p: parallel threads\n\n# Fast pre-filter with capa first\ncapa /samples/sample.exe # extracts capabilities; rule-driven, JSON-output\n```\n\n## Common pitfalls\n\n| Pitfall | Fix |\n|---|---|\n| Too-short strings (4 bytes) → false positives | Use 8+ byte strings; require multiple matches |\n| Regex `/pattern/` with `[`, `?`, `*` → slow | Prefer literal strings; if regex needed, anchor with `^`/`\\b` |\n| No filesize bound → scans 5GB files | Always include `filesize < N` |\n| Multiple wildcards in one hex `??` → exponential matcher cost | Limit to 2-3 wildcards per byte string |\n| `condition: any of them` with one common string → constant FPs | Use `2 of them` minimum |\n| Rule name with version | Use a separate `meta.version` so rule survives bumps |\n\n## Mass-hunt workflow\n\n1. Triage a sample (see `reverser/malware-triage` skill)\n2. Extract unique byte patterns, strings, capabilities\n3. Write a YARA rule with `meta` for context + bounded `condition`\n4. Test against:\n - The known sample (must match)\n - A clean corpus (must NOT match — false positive rate)\n - A different malware family corpus (must NOT match)\n5. Submit to VT Retrohunt + MalwareBazaar\n6. Track results, iterate\n\n## Tooling\n\n- `yara` / `yara-python` (writing + running)\n- `yarGen` (auto-generate rules from a sample set)\n- `valhalla.nextron-systems.com` (commercial YARA feed by Florian Roth)\n- `Neo23x0/signature-base` (open-source canonical rule set)\n- `capa` (Mandiant — capability-level rules)\n\n## References\n\n- VirusTotal YARA guide\n- "YARA, the pattern matching swiss knife for malware researchers"\n- Florian Roth\'s blog (the canonical YARA author of our era)\n- "Practical YARA Rules" — Andre Tavares\n', n.built_at = '1970-01-01T00:00:00+00:00', n.commit_sha = '', n.content_sha256 = 'sha256:5fd7ce9a8311f165e158dd6d70aba952679a520b7d0a6d7b562cdd47607fb1a4', n.description = 'YARA rule authoring + hunting — `condition:` syntax, hex patterns with wildcards, `for`/`any of them`, PE module, ELF module, hash module, math module. Build per-family signatures, hunt at scale via VT/MalwareBazaar/Hybrid Analysis. Avoid common pitfalls (collisions, slow rules, regex traps).', n.mitre_attack_raw = ['T1518.001'], n.path = '/skills/standard/reverser/yara-hunting/SKILL.md', n.size_bytes = 5484, n.subdomain = 'reverse-engineering', n.tags_raw = ['yara', 'malware', 'hunting', 'signatures'], n.upstream_ref_raw = '', n.when_to_use = 'yara rule signature hunting virustotal vt malshare malwarebazaar hybrid analysis pe elf hash condition any all of'; MERGE (n:Skill {name: 'rmm-tool-abuse'}) @@ -10274,6 +10276,10 @@ MATCH (a:Skill {name: 'reverser-triage'}), (b:MoC {name: 'binary-re'}) MERGE (a)-[r:BELONGS_TO]->(b); MATCH (a:Skill {name: 'reverser-triage'}), (b:Phase {name: 'reverse-engineering'}) MERGE (a)-[r:IN_PHASE]->(b); +MATCH (a:Skill {name: 'reverser-virtualized-protectors'}), (b:MoC {name: 'binary-re'}) +MERGE (a)-[r:BELONGS_TO]->(b); +MATCH (a:Skill {name: 'reverser-virtualized-protectors'}), (b:Phase {name: 'reverse-engineering'}) +MERGE (a)-[r:IN_PHASE]->(b); MATCH (a:Skill {name: 'reverser-yara-hunting'}), (b:MoC {name: 'binary-re'}) MERGE (a)-[r:BELONGS_TO]->(b); MATCH (a:Skill {name: 'reverser-yara-hunting'}), (b:Technique {id: 'T1518.001'}) diff --git a/packages/decepticon/decepticon/skills/standard/reverser/SKILL.md b/packages/decepticon/decepticon/skills/standard/reverser/SKILL.md index 32faf38d2..526ee83f4 100644 --- a/packages/decepticon/decepticon/skills/standard/reverser/SKILL.md +++ b/packages/decepticon/decepticon/skills/standard/reverser/SKILL.md @@ -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 @@ -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 | @@ -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 diff --git a/packages/decepticon/decepticon/skills/standard/reverser/virtualized-protectors/SKILL.md b/packages/decepticon/decepticon/skills/standard/reverser/virtualized-protectors/SKILL.md new file mode 100644 index 000000000..c1011e764 --- /dev/null +++ b/packages/decepticon/decepticon/skills/standard/reverser/virtualized-protectors/SKILL.md @@ -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 diff --git a/packages/decepticon/tests/unit/agents/test_reverser_workload_fallback.py b/packages/decepticon/tests/unit/agents/test_reverser_workload_fallback.py new file mode 100644 index 000000000..7b656d5ae --- /dev/null +++ b/packages/decepticon/tests/unit/agents/test_reverser_workload_fallback.py @@ -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