Passes and the capability model

A pass is the unit of work in disrobe. Each pass lives in its own crate, implements a shared trait, and declares the capabilities it requires and produces. The capability resolver is what allows arbitrary passes to chain.

Registered passes

Run disrobe passes for the live list. As of the current release:

PassCapability summary
pyarmorPyArmor v6 / v7 (dynamic-hook) + v8 / v9-pro static unpack.
pyinstallerPyInstaller 2.x-6.20+ extract + AES-CTR / CFB decrypt.
pyfreezecx_Freeze / py2exe / shiv / pex / PyOxidizer / Briefcase detect + extract.
nuitka--onefile payload extract (zstd) + symbol / constants scan.
pyDeobfuscate (peel + cleanup) / disassemble / decompile / extract / SourceDefender decrypt.
jsDeobfuscate (string-array + unminify + scope-aware rename) / unbundle 11 bundlers.
wasmAnalyze / decompile (JSON / Rust / TypeScript / WAT / C) / reverse 4 obfuscator families (plus wasm-name-obfuscator detect + classify).
envelope.dr create / inspect / verify / diff / migrate-check.
queryQuery a Disasm-rung .dr IR: functions / calls-to / xrefs-to / string-decoders / complexity-over / capability sites.
capabilitiesMatch a binary against built-in capability rules with evidence addresses and MITRE ATT&CK / MBC tags.
nativeGhidra-headless decompile / symbol dump / unpack / devirt / entropy / crypto signatures / disasm / callgraph / patch / sigmaker / diff.
jvmClassfile / .jar / .dex / .apk decompile via CFR / Vineflower / Procyon / JADX.
apkAndroidManifest.xml decode + resource id-to-name map + signer-cert SHA-256.
dotnet.NET PE decompile via ILSpy / dnSpyEx / de4dot + protector detection.
hermesReact Native Hermes bundle disasm + JS surface lift.
machoMach-O / fat / .ipa dump + ObjC + Swift class-dump.
luaLua 5.1-5.4 / LuaJIT / Luau / GLua decompile + obfuscator peel.
phpEncoder decode (phar / ionCube / SourceGuardian / ZendGuard) + eval-chain peel.
rubyMRI / YARV / mruby / JRuby / TruffleRuby / Ruby2Exe / Ocra analysis.
beam.beam IFF parse + Core Erlang lift + Code chunk disasm.
pickleDisasm + decompile + safety + symbolic trace + polyglot + ML model detect.
gopclntab + moduledata + garble report + embed.FS extraction.
swiftSwift / ObjC class-dump + SwiftShield undo + Confidential XOR-decrypt.
as3ActionScript 3 .swf DoABC tag disasm.
flutterDart AOT / libapp.so dump + obfuscation_map parse.
chainExplicit pass pipeline orchestrator.
serveHTTP daemon + WebSocket stream + LSP-stdio + gRPC + MCP.

The capability resolver

Rather than hard-coding which pass follows which, each pass declares:

  • Requires: the capabilities and IR rung its input envelope must already carry.
  • Produces: the capabilities and IR rung its output envelope will carry.

When the chain runner picks the next pass, it matches the current envelope's produced capabilities against each candidate pass's requirements. A pass only runs if its requirements are satisfiable. This is why disrobe auto can detect that a PyInstaller archive contains a PyArmor-protected module and route it through the unpack-then-decompile chain without any per-combination glue code.

Capabilities are versioned. A pass can require, for example, "a CPython 3.12 code object at the Disasm rung," and the resolver will refuse to feed it a 2.7 object. This keeps the chain sound across the wide version ranges disrobe supports.

Standardized emits

Every pass exposes the same twelve emit kinds:

source  disasm  ast  cfg  ir  manifest  sourcemap  symbols  strings  imports  signatures  report

Pass --emit source,disasm,report (comma-separated) to select a subset, or --all-emits on passes that support it to write every kind. A pass that cannot produce a given emit writes an explicit stub:

{
  "schema": "disrobe.emit.stub/v0",
  "pass": "pyarmor",
  "emit_kind": "source",
  "applicable": false,
  "error_code": "DR-IR-NotApplicable",
  "reason": "pyarmor pass does not produce source; chain with disrobe py decompile"
}

A downstream tool can request any emit from any pass and get a well-formed, self-describing answer: either the artifact or a "not applicable here, chain with X."

Error codes

Every failure carries a DR-<DOMAIN>-<NNNN> code rendered through miette diagnostics. Look any code up with:

disrobe explain DR-PYARM-0050
disrobe explain CLI-1            # short form also works