disrobe: a universal decompiler, deobfuscator, and unpacker
One tool to decompile, deobfuscate, and unpack almost anything, deterministically, in a single Rust binary.
disrobe is a universal multi-language decompiler and deobfuscator. It decompiles Python .pyc bytecode, unpacks PyArmor and PyInstaller, reads Nuitka-compiled binaries, decompiles WebAssembly, deobfuscates JavaScript, decompiles .NET / CIL and JVM / Java, recovers Android DEX, and unwraps native PE / ELF / Mach-O packers, all from one static binary built for malware analysis and reverse engineering.
Try it in your browser: the disrobe playground. Decompile a
.pyc, scan a pickle for malicious reduce callables, and summarize a.wasmmodule, all client-side, with the core passes compiled to WebAssembly. Nothing is uploaded.
disrobe reverses the bytecode, packers, freezers, and protectors layered onto compiled and frozen software across 20+ ecosystems: Python, JavaScript/TypeScript, WebAssembly, JVM and Android, .NET, native PE/ELF/Mach-O, Go, Lua, PHP, Ruby, Erlang/Elixir (BEAM), Swift/Objective-C, ActionScript 3, React Native Hermes, Flutter Dart AOT, and the native packer tier layered on top of them (UPX, MPRESS, NSPack, FSG, kkrunchy, MEW, ASPack, PECompact, Petite, Yoda's Crypter). It ships as a single static Rust binary.
Built for forensic and recovery work where reproducibility matters:
- Deterministic. No model anywhere in the decompile path. The same input produces byte-identical output on every machine and every run, usable as evidence and as a diff baseline.
- Single static binary. No JVM, no Python runtime, no Docker image required to run the core. Builds from one
cargo build --release. Drops into CI headlessly. - Content-addressed. Every recovered artifact persists as a
.drenvelope: an rkyv hot payload plus a postcard cold sidecar, rooted by a BLAKE3 hash. Cache hits are byte-identical and chains compose offline. - Honest. Every Python decompile is recompiled on the matching interpreter and compared opcode-for-opcode: 92.76% per-code-object equivalence on the CPython 3.14 stdlib (5831 of 6286), measured against the interpreter, not the tool's own output. Recovery that is not perfect is labelled
SEMANTIC,PARTIAL, orSKELETONrather than presented as ground truth. Commercial-tier packers that disrobe cannot fully unpack are reported as detect-only by design, never faked.
Who this is for
- Malware analysts and incident responders who receive a packed, frozen, or obfuscated sample and need to read what it does, without executing it.
- Security researchers auditing a closed binary for interoperability or vulnerability research.
- Developers recovering their own lost source from a shipped
.pyc,.jar,.dll, or bundled.js. - Coding agents. Every pass can emit a structured metadata sidecar (
--llm) carrying the call graph, type signatures, control-flow shape, capability surface, and decompile provenance, so an LLM can reason about recovered code without re-deriving its structure.
What makes it different
disrobe ships passes for every ecosystem above from a single binary. Where mature FOSS already exists (CFR, Vineflower, jadx, ILSpy, JPEXS, unluac, hermes-dec, Ghidra), disrobe wraps it headlessly behind a unified CLI and adds chain auto-detection, deterministic .dr envelopes, and round-trip verification. Where FOSS coverage is thin (PyArmor v9-pro, the native packer tier, Hermes against a live bundle, Flutter Dart AOT, MicroPython .mpy, PEP 750 t-strings), it is among the few tools handling these statically and offline. Where the field is dominant (Ghidra/IDA/Binary Ninja for native decompilation), disrobe is the unpack, symbol-recovery, and chain-detect layer that feeds them cleaner input.
Measured recovery
Every figure below is produced by a committed test gate or a local measurement harness graded against an independent oracle, never the tool's own output. The full per-value sourcing lives in xtask/data/recovery.json.
| Ecosystem | Measured | Oracle |
|---|---|---|
| Python bytecode | 92.76% per-code-object equivalence on the CPython 3.14 stdlib (5831 of 6286) | recompile on CPython 3.14.5, opcode diff |
| CPython legacy 1.0-3.7 | 152 of 191 proven-correct (CI floor); 166 of 191 measured locally | recompile-equivalence or structural token-match |
| WebAssembly | 100% op-coverage on the 30 parseable corpus modules; 24 of 24 execution-eligible functions equivalent | execution differential under wasmtime |
| JVM classfile | 93.1% of methods recompile error-free (122 of 131 floor; 128 measured) | real javac |
| Android (Dalvik) | 99% of verifiable classes pass the JVM verifier (102 of 103) | -Xverify:all over assembled jar |
| Ruby YARV | greeter 100%, megafile 85% opcode-multiset equivalence | recompile on MRI |
| PyArmor | 72 of 72 real-corpus samples recovered | plaintext-absent oracle |
| Containers | 98 formats detected, 98 extracted in-tree | per-format byte length |
The numbers that are not perfect are labelled SEMANTIC, PARTIAL, or SKELETON, and the information-theoretic walls (native-virtualized code, runtime-only keys, RSA-wrapped capsule keys) are reported as detect-only by design.
How to read these docs
- New here? Start with Installation and Quickstart.
- Want to understand the design? Read the Architecture overview, then the five-rung IR ladder.
- Looking for a specific language? Jump to its language guide.
- Need an exact command or flag? See the CLI command reference.
- Running disrobe against untrusted samples? Read Forensics and malware-safety posture first.