reverse-eng-dotnet
Reverse a .NET / C# assembly — decompile, deobfuscate, debug, and IL-patch managed binaries. Load when the target is a managed PE (.exe/.dll with a CLR header), a Sharp* red-team tool (Rubeus, SharpHo
Files2
View on GitHubSKILL.mdskills/reverse-engineering/reverse-eng-dotnet/SKILL.md
GitHub.NET / C# assembly reversing
When it applies
The binary is a managed .NET assembly — a thick client, a Sharp* red-team tool, or a captured
loader/stealer — and it is in scope (tradecraft-scope-roe, scope.txt). Pure-native PEs, IL2CPP
Unity builds, and NativeAOT output carry no CLR metadata: those go to reverse-eng-binary-triage.
dnSpyEx (the only GUI with an IL editor and debugger) is Windows-only; on Linux/macOS use
ilspycmd + dotnet de4dot.dll and accept that interactive patching/debugging needs Windows.
Why it works
Managed assemblies keep near-complete metadata — type, method, and field names survive
compilation, so decompilation recovers close-to-source C#. Obfuscators only scramble names,
strings, and control flow, and de4dot automates the unscrambling for the common families. The IL
instruction stream is the ground truth: the C# view loses or distorts compiler-generated code
(async state machines, closures, yield), so decisive reads and every patch happen at IL level.
Method
Obfuscator identification table, de4dot options, anti-tamper bypasses, and IL patch recipes: see
cheatsheet.mdnext to this file.
- Confirm managed, not native.
strings target.exe | grep -iE "mscoree|_CorExeMain|mscorlib|System\."anddiec target.exe(Detect It Easy reports .NET plus the obfuscator). Hard proof: PE data directory 14 (CLR header) non-zero,_CorExeMainentry,#~/#Stringsmetadata streams. - Detect and strip obfuscation.
de4dot --detect target.exeto identify the family, thende4dot target.exe -o target-clean.exe. Keep the original untouched — all further analysis runs on the clean copy. - Static in dnSpyEx. C# view for fast browsing and string search (
password,verify,encrypt,http,Config), IL view for anything decisive. Read order:<module>.cctor(deobfuscator/anti-tamper init runs first) →Main→ the method that uses your located string (find via cross-references). - Dynamic with the dnSpyEx debugger when strings or config decrypt only at runtime: break on
the decryptor's return and read the plaintext from Locals/Watch. Set exception breakpoints —
obfuscators hide real control flow in
try/catchdispatch, and breaking the thrown exception type shows the actual path. .NET debugging exposes object values directly; prefer it over grinding static IL. - Patch via Edit IL, never Edit Method (C#). Flip
ldc.i4.0→ldc.i4.1, replace a check withldc.i4.1; ret, ornopout a validation block, then File → Save Module as a new file. C# recompile fails constantly on state machines and lambdas; IL edits are instruction-exact.
Gotchas
- IL2CPP / NativeAOT look like .NET but are native —
System.Private.CoreLibstrings, no CLR header. Route toreverse-eng-binary-triage. - New ConfuserEx anti-tamper / .NET Reactor necrobit defeat de4dot — patch the integrity check
to
retfirst, or run the binary and dump the decrypted assembly from memory (MegaDumper / ExtremeDumper), then clean the dump with de4dot. Themida .NET is a native virtualizing wrapper — de4dot cannot touch it; dump and go native. - Exception-driven control flow can't be
nop'd — trace thecatchdispatch to find the real logic. - Keep artifacts on disk: original,
*-clean.exe, patched copy, and the extracted config/C2/keys — the findings folder needs reproducible evidence. - Run unknown samples in an isolated VM — executing a managed binary runs its
.cctor.
Verify success
You can name the obfuscator (or confirm none), show decrypted strings/config/C2 or a patched binary whose behavior provably changed, and the original sample is preserved for the diff.
References
dnSpyEx, de4dot, and dnlib documentation; Washi's "Misconceptions about .NET reversing" (IL over C# decompiler output).
Portions adapted from reverse-skill by zhaoxuya520, MIT License.