97 lines
3.2 KiB
Markdown
97 lines
3.2 KiB
Markdown
---
|
|
name: analyzing-dotnet-malware-internals
|
|
description: 'Reverses .NET/managed malware: decompiling MSIL back to C#, defeating common
|
|
.NET protectors and string encryptors, and tracing reflection-based loaders to recover the
|
|
real payload. Activates for requests to analyze a .NET sample, decompile MSIL, or unpack a
|
|
managed loader.'
|
|
domain: cybersecurity
|
|
subdomain: reverse-engineering
|
|
tags:
|
|
- reverse-engineering
|
|
- dotnet
|
|
- msil
|
|
- decompilation
|
|
- deobfuscation
|
|
- managed-code
|
|
version: 1.0.0
|
|
author: analyst-ai-pack
|
|
license: Apache-2.0
|
|
mitre_attack:
|
|
- T1027
|
|
- T1620
|
|
- T1055
|
|
d3fend:
|
|
- D3-FCR
|
|
- D3-DA
|
|
references:
|
|
- 'ECMA-335 Common Language Infrastructure (CIL/MSIL) — https://ecma-international.org/publications-and-standards/standards/ecma-335/'
|
|
- 'ILSpy decompiler — https://github.com/icsharpcode/ILSpy'
|
|
---
|
|
|
|
# Analyzing .NET Malware Internals
|
|
|
|
## When to Use
|
|
|
|
- A sample is a managed (.NET) assembly — confirmed by a CLR header / `mscoree` import or
|
|
`BSJB` metadata signature.
|
|
- You need readable C# from MSIL and want to defeat .NET-specific obfuscation.
|
|
- A loader uses reflection (`Assembly.Load`) to run an in-memory payload you must recover.
|
|
|
|
**Do not use** native disassembly workflows (Ghidra for x86) as the primary tool — managed
|
|
code decompiles far more cleanly with a .NET decompiler.
|
|
|
|
## Prerequisites
|
|
|
|
- ILSpy / dnSpyEx for decompilation and (with dnSpyEx) managed debugging.
|
|
- de4dot or equivalent for known protectors; familiarity with common .NET obfuscators.
|
|
|
|
## Workflow
|
|
|
|
### Step 1: Confirm it is managed
|
|
|
|
Check for the CLR runtime header and the `BSJB` metadata magic:
|
|
|
|
```bash
|
|
python scripts/analyst.py identify sample.exe
|
|
```
|
|
|
|
### Step 2: Decompile
|
|
|
|
Open in ILSpy/dnSpyEx and review the entry point, `Main`, and module initializer
|
|
(`<Module>.cctor`), which protectors often abuse.
|
|
|
|
### Step 3: Handle obfuscation
|
|
|
|
Recognize and undo common schemes:
|
|
|
|
- **String encryption** — a decryptor method called everywhere; run/trace it to recover
|
|
plaintext (de4dot can often static-decrypt).
|
|
- **Control-flow flattening** — follow the dispatcher state machine.
|
|
- **Proxy methods / renaming** — rely on decompiler analysis rather than names.
|
|
|
|
### Step 4: Trace reflection loaders
|
|
|
|
Find `Assembly.Load(byte[])` / `Activator.CreateInstance`; dump the byte array argument at
|
|
runtime (managed debugger breakpoint) to recover the real second-stage assembly, then recurse.
|
|
|
|
### Step 5: Analyze the payload
|
|
|
|
Decompile the recovered stage; extract C2, configuration, and capabilities for the report.
|
|
|
|
## Validation
|
|
|
|
- Decompiled C# is coherent (named or recovered) and the entry path is traced.
|
|
- Encrypted strings are recovered to plaintext.
|
|
- The reflection-loaded stage is dumped and itself decompiles.
|
|
|
|
## Pitfalls
|
|
|
|
- Treating the loader as the payload — managed malware is frequently multi-stage.
|
|
- Ignoring the module initializer where protectors install hooks.
|
|
- Static-decrypting strings when the scheme is runtime-keyed; debug and dump instead.
|
|
|
|
## References
|
|
|
|
- See [`references/api-reference.md`](references/api-reference.md) for the CLR identifier.
|
|
- ECMA-335 and ILSpy (linked in frontmatter).
|