计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
analyzing-dotnet-assemblies使用 dnSpyEx、ILSpy 和 de4dot 对 .NET 程序集和可执行文件进行逆向工程 — 识别和拆解混淆器与打包器,去除控制流混淆和字符串加密,处理单文件和 NativeAOT 发布,并修补 IL。适用于二进制为托管 PE、ILSpy 显示混乱的名称或空方法体时,或在分析 .NE…
AI 翻译
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
.NET compiles to IL with full metadata, so an unobfuscated assembly decompiles to near-original C#. That makes the interesting question not "how do I read this" but "what is in the way" — a packer, an obfuscator, a runtime loader, or a publish mode that hides the managed code inside a native host.
file reports a PE and the binary contains a CLR header or mscoree.dllreversing-unity-il2cpp; the C# is compiled to nativereversing-xamarin-maui for the container
extraction, then come back here for the DLLsanalyzing-malware first# Linux
file target.exe # "Mono/.Net assembly"
rg -a -o 'BSJB|mscoree\.dll|System\.Private\.CoreLib' target.exe | head
# Windows
dotnet-tool: ildasm /text target.exe | head
# CLR header presence in the optional header data directory 14 is the real test
| What you find | Publish mode | Approach |
|---|---|---|
Managed PE, references mscorlib/System.Private.CoreLib | Framework-dependent | Decompile directly |
| Native PE containing an embedded managed bundle | Single-file publish | Extract the bundle first |
| Native PE, no IL metadata at all | NativeAOT | Not .NET RE — use analyzing-binaries |
.dll with no entry point | Library | Decompile; find callers elsewhere |
# Single-file publish: the managed assemblies are appended as a bundle
# Extract with a bundle extractor, or carve on the manifest header
ilspycmd --list-bundle target.exe 2>/dev/null || binwalk -Me target.exe
ilspycmd target.dll -o ./decompiled -p # -p writes a project structure
# GUI: dnSpyEx (maintained fork), ILSpy, dotPeek
# dnSpyEx also debugs and edits assemblies, which is why it is the default choice
rg -n 'https?://|ConnectionString|ApiKey|password|Convert\.FromBase64String' ./decompiled | head -40
Read in this order: the entry point, then any type named for the product, then
anything referencing networking, crypto, or process APIs. Skip generated types
(<>c__DisplayClass, <Module>) unless following a specific closure.
Deobfuscation is cheap when you name the tool and expensive when you do not.
detect-it-easy target.exe # or DIE's CLI: diec
# Look for: ConfuserEx, .NET Reactor, SmartAssembly, Eazfuscator, Babel,
# Dotfuscator, Agile.NET, Obfuscar
rg -a -o 'ConfuserEx|Reactor|SmartAssembly|Eazfuscator|Babel|DotNetPatcher' target.exe
# de4dot handles the classic obfuscators automatically
de4dot target.exe -o cleaned.exe
de4dot -p cr target.exe # force a specific profile when detection fails
# ConfuserEx specifically: use a maintained unpacker fork, since de4dot's
# built-in support predates later ConfuserEx versions
What each obfuscation layer looks like, and how to handle it:
| Layer | Appearance | Handling |
|---|---|---|
| Name mangling | Class1.method_3, unicode/invisible names | Cosmetic — rename as you read; de4dot restores some |
| String encryption | <Module>.Decrypt(12345) everywhere | Run the decryptor: de4dot, or invoke the method dynamically |
| Control flow flattening | Giant switch on a state variable | de4dot's CFG cleanup, or read dynamically |
| Proxy/delegate calls | Every call goes through a helper | de4dot devirtualization |
| Anti-tamper / anti-debug | Fails under dnSpy debugger | Patch the check, or dump after decryption |
| Virtualization (Agile.NET, KoiVM) | No recognizable IL at all | Devirtualizer (OldRod for KoiVM) or dynamic analysis |
| Native stub wrapping IL | Managed code decrypted at runtime | Dump from memory — the reliable answer |
Dumping from memory is the general escape hatch. Whatever the packer, the CLR must eventually hold real IL to execute it. Let it load, then dump:
# MegaDumper / ExtremeDumper / pe-sieve — dump loaded managed modules from a
# running process, then decompile the dump
pe-sieve /pid <pid> /dmode 3
This is the same "let it unpack itself" pattern as packed native malware; see
analyzing-malware for containment before running anything hostile.
# dnSpyEx: set breakpoints in decompiled C#, inspect locals, edit method bodies,
# and save the modified assembly. This is unusually powerful — you can change a
# license check or a validation result and immediately re-run.
Patching workflow: edit the IL or the decompiled C# in dnSpyEx, save the assembly, and re-run. Strong-name signatures break on save — either remove the strong name requirement, or if the app verifies its own hash, hook the check. For a signed assembly the app itself verifies, patching is usually the wrong tool; hook at runtime instead.
.NET is heavily used for loaders and commodity RATs, and the artifacts are distinctive:
Assembly.Load(byte[]) — a second stage decrypted in memory. Find the
decryption routine, run it offline, and analyze the resulting assembly. This
is the single most common .NET malware pattern.Activator.CreateInstance plus reflection to hide the real call graph.AmsiScanBuffer / EtwEventWrite patching — search for the byte
patterns or the GetProcAddress calls that precede them.[DllImport]) reveal native capability:
VirtualAlloc, CreateRemoteThread, SetWindowsHookEx.rg -n 'DllImport|Assembly\.Load|CreateInstance|FromBase64String|RijndaelManaged|AmsiScanBuffer' ./decompiled | head -30
ilspycmd --list-resources target.dll
Extracted second stages go back through this skill; capability and IOC output
goes to analyzing-malware, detection content to engineering-detections.
Assembly.Load and recover what it loads.analyzing-malware — containment, capability model, and IOCsanalyzing-binaries — NativeAOT and native stub analysisreversing-unity-il2cpp, reversing-xamarin-maui — the other .NET containersengineering-detections — turning recovered behaviour into rules