-
-
Notifications
You must be signed in to change notification settings - Fork 0
anti analysis
disrobe is a static, deterministic analyzer that never runs the sample on the default path. It recognizes the standard anti-static-analysis arsenal and recovers what is statically recoverable, stating a wall where the data is genuinely absent rather than fabricating past it.
Identification never trusts a single magic byte. A zeroed or flipped magic, renamed UPX0/UPX1 sections, or a corrupt UPX! marker is re-identified from internal self-consistency:
-
PE through
e_lfanewto the COFF and optional headers. - ELF / Mach-O by header offsets that close against the file length.
- ZIP by its end-of-central-directory anchor.
- DEX by section-offset consistency.
- Classfile by a constant-pool walk.
- wasm by the LEB section stream.
A real UPX executable with a flipped MZ and renamed sections still unpacks byte-identically, because the structural PackHeader (method id, self-consistent compressed and uncompressed lengths, plausible version) is the signal a scrambler cannot remove without breaking the stub's own ability to self-extract.
| Scheme | What disrobe does |
|---|---|
| Single-byte XOR stack strings | Recovers them with English-likeness key detection, on native via the in-house x86 emulator driving each decoder-shaped function. |
| Per-family keyed strings | Mirai, Dridex, and Trickbot keyed-string schemes decoded from their known transforms. |
| JVM string encryption | Emulates the in-class decrypt(String) / decrypt(int, String) method over the encrypted constants, running <clinit> for a static key or constructing the receiver for an instance key. |
| .NET constant decryption | ConfuserEx2 constants reversed on a real committed sample by emulating the in-assembly decryptor. |
| JS string-array rotation | The rotated string array is rebuilt and call sites inlined. |
Python exec/eval/compile payloads |
Unwrapped through base64/85/16/32 and zlib/lzma decode chains. |
Runtime-keyed schemes (a key from a system property, the environment, the clock, a secure random, or a live cross-class table) are flagged as walls, not guessed.
-
BlackObfuscator DEX flattening is deflattened: the
String.hashCode()-keyed dispatcher is recognized, each block'sconst-stringname is matched to its switch case, and the original linear block order is recovered and annotated in the output. - OLLVM-style control-flow flattening, bogus control flow, and instruction substitution are reversed on native.
- Obfuscator-planted out-of-range exception entries that poison the JVM control-flow graph are dropped before structuring.
- Flattened JS dispatchers are collapsed back to structured control flow.
The JVM, Dalvik, and CIL decoders tolerate broken StackMapTable, fake exception ranges, and illegal-but-verifiable bytecode. On native, jump-into-the-middle desync, overlapping instructions, and opaque predicates are resolved in-tree. A mixed-boolean-arithmetic simplifier, wired through the JS and WebAssembly decoders as well, collapses MBA expressions back to their algebraic form.
| Target | Status |
|---|---|
| Lua (IronBrew2 2.7.0) | Devirtualized in standard and MAX mode, graded by a real-lua execution differential. |
| Native generic VM |
disrobe native devirt locates the interpreter, fingerprints each handler's micro-op behaviorally through the in-tree x86 emulator, and lifts to a re-executable IR plus pseudo-code, validated end-to-end on a self-authored Tigress-shape VM (the recovered IR re-executes byte-identically from machine code alone). |
| VMProtect / Themida / Enigma front-ends | Extended from published RE write-ups, not a running commercial sample. A per-machine-keyed handler stream is the residual wall. |
The PE overlay carve computes the true end of the executable image and isolates any trailing archive (gzip, xz, zstd, bzip2, tar, 7z, cab, rar) into its own segment, so padding cannot mask an appended payload.
ProGuard/R8 names are restored from mapping.txt (overload-correct), Go type and stdlib names are recovered from pclntab/moduledata on stripped binaries, Rust/C++/Swift/Itanium symbols are demangled, and structure is recovered from DWARF. garble name-hashing (HMAC-SHA256 over an absent build seed) is a wall, but structure, types, and control flow recover regardless.
PyArmor v6-v9 static decryption succeeds when the pyarmor_runtime is supplied. With no runtime, the verdict routes to the dynamic-capture path (opt-in, sandboxed) rather than emitting fabricated plaintext. ionCube, SourceGuardian, Zend Guard, ILProtector, and MaxToCode derive their key in a native loader or live process absent from the artifact, so they are walled and reported absent.
See the forensics and malware-safety posture for how the default static path stays safe on untrusted input.
This wiki is generated from docs/src in the disrobe repository by scripts/wiki_sync.py. Edit the docs there, not the wiki pages here.
Getting started
Architecture
- Overview
- The five-rung IR ladder
- Passes and the capability model
- The chain runner
- The .dr envelope
- LLM sidecar and provenance
Reverse-engineering toolkit
Language and format guides
- Python
- JavaScript / TypeScript
- WebAssembly
- JVM and Android
- .NET / CIL
- Native (PE / ELF / Mach-O)
- Go
- Lua
- PHP
- Ruby
- BEAM (Erlang / Elixir)
- Swift / Objective-C
- ActionScript 3 / Flash
- Mobile (Hermes / Flutter)
- Python pickle
- Shell / PowerShell
- Containers and archives
Reference
- CLI overview
- Global flags
- Command reference
- Project configuration
- Batch directory processing
- Run reports
- Analysis-depth commands
- Diff and guard tooling
- The daemon: HTTP, gRPC, LSP, MCP
- Use it as a library
- Python bindings
- The browser playground
- Forensics and malware-safety posture
- Threat model
Integrations
Project