Home - 1-3-7/disrobe GitHub Wiki

disrobe: static software recovery

disrobe

Recover source, unpack payloads, and trace the evidence back to the input.

disrobe is a Rust command-line suite for examining compiled and obfuscated software. It decompiles supported bytecode and native code, reverses source obfuscation, extracts frozen applications and embedded frontends, and unpacks supported packers, archives, and firmware containers. Recovery reports identify the input, the stages that ran, the artifacts they produced, and the conditions that prevented further recovery.

The default path never executes the sample. One opt-in PyArmor v6/v7 fallback does execute it and requires --allow-dynamic; use that path only inside an isolated sandbox. Start with the forensics and malware-safety posture when handling untrusted material.

Watch the Disrobe walkthrough

Watch or download the full video · Read the transcript. Twenty CLI commands cover unpacking, source recovery, Android resources, indicators, reports, artifact verification, and project setup in 2 minutes 12 seconds. The transcript includes the recorded build's complete command inventory.

Try it in your browser: the disrobe playground. Decompile a .pyc, scan a pickle for malicious reduce callables, and summarize a .wasm module, all client-side, with the core passes compiled to WebAssembly. Nothing is uploaded.

The live catalog spans 15 ecosystems: Python, JavaScript and TypeScript, WebAssembly, JVM and Android, .NET, native PE/ELF/Mach-O/NE, Go, Lua, PHP, Ruby, Erlang and Elixir on BEAM, Swift and Objective-C, ActionScript 3, mobile runtimes, and shell languages. The implemented native-packer tier currently lists Donut, sRDI, UPX, ASPack, Petite, MPRESS, FSG, PECompact, Yoda's Crypter, NSPack, MEW, kkrunchy. Run disrobe catalog [ecosystem] for the per-family recovery tier compiled into your binary.

Guarantees and boundaries

  • No model runs in the recovery path. Metadata bundles are deterministic structured data for downstream tools.
  • Output ordering and serialization are checked by hashing three fixture recoveries across Linux, macOS, Windows, and batch runs with one and four workers.
  • The main CLI ships as one Rust binary. In-house paths launch no external program. Commands with optional backends invoke an installed tool only when you name it with --backend.
  • The shared artifact layer can store recovered state in a content-addressed .dr envelope with an rkyv payload, postcard sidecar, and BLAKE3 root. Chain runs record topology and per-stage provenance separately.
  • Python's normalized opcode-structure agreement is 96.5% on a fixed 574-module CPython 3.14 core population (17637 of 18276 code objects), excluding idlelib and turtledemo. Its pinned 200-module subset reaches 97.66% (6139 of 6286). See the comparison rules.

Start with an artifact

After installation, replace sample.bin with the file you want to examine:

disrobe auto sample.bin --out recovered/ --capture-stages
disrobe context --out recovered/
disrobe report recovered/ --format html > recovery.html

auto selects recovery passes from the file's bytes. context summarizes their outcomes; the HTML report brings the artifact inventory, evidence, and recovery limits into one document. --capture-stages retains intermediate bytes so you can inspect a layer or pass it to another tool. The quickstart explains the output and direct commands.

Your task Useful starting point
Triage a packed or obfuscated sample Automatic recovery, followed by recon and indicator extraction over the recovered files
Document a forensic examination Run reports with input digests, artifact locations, stage outcomes, and provenance
Audit a closed binary or investigate a vulnerability Native recovery, queryable IR and capabilities, and the relevant language guide
Recover your own lost source Direct decompilation of a supported .pyc, .jar, .dll, or JavaScript bundle; start with the quickstart examples
Build recovery into another tool Rust library APIs, typed Python bindings, daemon protocols, metadata bundles, or the browser playground

Choose a recovery path

disrobe passes lists direct command families and the automatic recovery routes included in the current build, with their ecosystems and support tiers. Direct commands also provide recon, taint analysis, and optional external decompilers. Electron and Tauri frontends use webview.carve; Wails can use Go's embedded-filesystem route. Use disrobe --help for commands and disrobe catalog [ecosystem] for recognized families.

In-house recovery remains available without optional toolchains. JVM, Android, .NET, and native commands can also use installed tools such as CFR, Vineflower, jadx, ILSpy, de4dot, or Ghidra where their command-specific backend policy allows it. disrobe doctor reports what is installed; it does not make an unavailable backend part of an in-house result.

Measured recovery

Every figure below comes from a committed test gate or a local measurement harness. strong figures are graded against an independent oracle; coverage-self-reported figures state the inspected population and count disrobe's own output. The full per-value sourcing lives in xtask/data/recovery.json.

Measured recovery by ecosystem

Open the full-size recovery chart.

Colour identifies the comparison method. A filled mark denotes a CI gate on its documented triggers; a hollow mark denotes a local measurement. Each descriptor lists its inputs and prerequisites.

Ecosystem Measured Oracle
Python bytecode Normalized opcode-structure agreement: 96.5% across the fixed 574-module core population (17637 of 18276); 97.66% on the pinned corpus (6139 of 6286). Whole-module agreement, where every compared code object matches: 130 of 200 modules CPython 3.14.5 compilation and normalized opcode comparison; jump targets and most operands are excluded
WebAssembly 1034 of 1034 opcodes lowered across the 38 parseable modules (133 of 133 functions), counted against an inventory wasm-tools produced rather than one disrobe produced; 57 of 57 execution-eligible functions equivalent external opcode inventory for coverage, wasmtime differential for execution
JVM classfile 131 of 131 methods recompile error-free real javac
Android (Dalvik) 100% of the verifier-presented classes in the committed dex corpus pass the JVM verifier (118 of 118). A further 37 of 155 classes are link-skipped and never reach the verifier, because they reference supertypes the harness does not bundle, so those are ungraded rather than passing -Xverify:all over assembled jar
Ruby YARV greeter 100%, megafile 100% original opcode-name multiset recall recompile on MRI; ordering, operands and additional opcodes are excluded from this score
PyArmor 72 / 72 manifest-named v8/v9 default-trial wrappers decrypt and decode one complete header-anchored root CodeObject self-reported structural check; no source, emitted .pyc, semantic, execution, or external comparison
Containers 105 formats detected, including a bounded raw-volume-key LUKS1 route; 30 generic routes write member bytes from a committed input that match an independent extractor extraction over the committed corpus, pinned per format; tracked LUKS1 plaintext comparison

Recovery reports include support tiers and per-stage outcomes. Native-virtualized code, runtime-only keys, and RSA-wrapped capsule keys remain detect-only when the required information is absent from the input.

Recovery limits

An unsupported instruction, ambiguous signature, or unavailable runtime key can stop a recovery stage. Its report names the reason and retains the recovered structure available up to that point. Supplying additional artifacts, such as a matching runtime or name map, can make another recovery path available. See reading a result for outcomes and error codes.

Where to start

⚠️ **GitHub.com Fallback** ⚠️