.NET / CIL
disrobe parses the full .NET PE + CLR metadata surface, decompiles CIL to C#, F#, and VB pseudo-source, detects 19 protectors, and handles ReadyToRun and Native AOT images. In-house static recovery reverses ConfuserEx2 constant decryption on a real committed sample (its encrypted-resource layer is carved byte-exact but walled on the runtime key, and full ConfuserEx2 cleanup delegates to --backend de4dot); the Eazfuscator VM-tier is devirtualized at 57 of 57 instructions against an in-repo EazVM virtualizer of our own (the committed assembly is encoded by that virtualizer, not the shipping Eazfuscator.NET product), and ILProtector and MaxToCode are detected and structurally enumerated with their method bodies walled on the native-runtime key (derived in the loader, absent from the assembly), never fabricated. The rest are detected with watermark-strip and encrypted-resource classification.
Decompiling
disrobe dotnet decompile App.dll --backend ilspy --out src/
disrobe dotnet decompile App.exe --backend dnspyex --out src/
disrobe dotnet decompile App.dll --backend de4dot --out src/
Routes a .NET PE (.dll / .exe) through ILSpy, dnSpy, dnSpyEx, or de4dot. disrobe owns the in-house CIL disassembler (full opcode table) and the CIL-to-C#/F#/VB lowering, so the structural recovery is its own even when a rendering backend is used.
Static analysis
disrobe dotnet analyze App.dll
disrobe dotnet backends # report available .NET backends on PATH
analyze reports the PE header, CLR metadata, protector detection, and probes for ReadyToRun (R2R) and Native AOT images, with symbol recovery on AOT builds.
Obfuscator reversal
disrobe detects 19 protector families. Recovery depth varies by protector and by what is statically present in the artifact. The model for in-house recovery is the same one used by the JVM and Lua passes: locate the decryptor method or key inside the assembly and emulate it over the encrypted data through the in-house CIL stack-machine, never a re-derived or hard-coded key.
Reversed on a real committed sample (plaintext recovered from the artifact, plaintext-absent oracle):
- ConfuserEx2: in-house recovery reverses the constants protection (the documented FOSS "Ki.Constants" block-XOR / LZMA-validated algorithm) on a real committed
SampleConstants.confuserex2.dll, with a test whose fixture holds only ciphertext plus the real decryptor and asserts plaintext not present anywhere in it. The encrypted-resource layer is carved byte-exact but walled on the runtime key. Full deobfuscation (control-flow flattening, runtime-VM string decryption, anti-tamper) is delegated to de4dot viadisrobe auto/--backend de4dot; disrobe does not reimplement that tier in-house.
In-assembly-decryptor recovery, graded by round-trip against the pre-encryption original:
- Eazfuscator.NET: locates the static
char[]/byte[]string-decryptor method and emulates its CIL over the encrypted#USliteral table to recover the pre-VM plaintext strings. The VM-tier is devirtualized against an in-repo EazVM virtualizer of our own: the committed assembly is encoded by that virtualizer, not the shipping Eazfuscator.NET product. disrobe reads the embedded resource, recovers the per-build opcode map from the in-assembly dispatch table by fingerprinting each handler, decrypts the position-keyed instruction stream, and lifts every virtualized method body back to CIL, then grades that CIL against the clean DLL. The grade is an ordered instruction comparison (opcode and operand, with branch targets resolved to instruction index, not raw token): 57 of 57 instructions match in sequence across the five bodies (100%). A second gate rebuilds a runnable assembly from the recovered CIL and asserts its stdout is byte-identical to the clean baseline (run wherever a .NET runtime is onPATH). Per-build randomization is fully recovered; only a runtime-only homomorphic key, not present statically, would bound a given build. - ILProtector / MaxToCode: classified by Invoke-stub and zero-RVA method enumeration, runtime-resource and
.mtc/.text1section location, and container-framing parse. Real builds derive the per-method key inside the native loader (Protect32/64.dll) at run time, not in the managed assembly, so the encrypted bodies are walled and reported absent, never fabricated. - KoiVM (ConfuserEx VM): detected by
#Koistream andVMDispatchermethod markers; the VM-dispatch handler table is fingerprinted and the virtualized methods are lifted back to CIL through the same in-house CIL stack-machine used for Eazfuscator.
Doable now with an in-assembly key (detected + classified today; a per-protector decryptor emulation can be added on the same model when a real sample is available to fix the exact algorithm against):
- SmartAssembly, .NET Reactor, Babel, Dotfuscator (Pro), Skater, Goliath, DeepSea: the string key (per-string XOR lane, AES/Rijndael resource key, RC4(SHA1(resource)), or single-byte XOR) is embedded in the assembly, so the data is present and not a wall. These are currently detected with watermark-strip, identifier, and encrypted-resource classification, and the generic static-decoder opportunistically recovers in-lined integer/string constants where the decoder is a pure transform. Confirming a per-build algorithm needs a real protected sample (flagged for consent; nothing is downloaded).
- CryptoObfuscator, Agile.NET (CV tier): 3DES/Rijndael string keys are in-assembly; same status as above.
Needs a real sample to build against (flagged for consent, never downloaded): Spices.Net (Cyrillic-homoglyph + ROT-N per-method scramble), and the commercial protectors above whose exact per-build transform is not documented.
Genuine walls (the key or the original code is not in the static artifact):
- Themida / .NET wrapper: managed methods are lifted into the Oreans native VM; per project policy disrobe does not ship a native-VM devirtualizer.
- ArmDot: custom per-method VM with LCG-encrypted opcodes; static devirtualization is not performed.
- ILProtector / MaxToCode native-keyed configurations: when the per-method key is computed inside the native stub, the original CIL is not statically present.
Other:
- Obfuscar: dedicated in-house peeler (NameMaker odometer classification; rename-only metadata, so there is no byte rewrite and no embedded name map to recover).
Grey-zone commercial protectors are gated behind --i-have-authorization.
Chaining
disrobe auto App.exe --out recovered/ # ConfuserEx2 PE -> de4dot -> ILSpy -> C#