.NET恶意程序逆向分析
用于.NET恶意程序集分析,重点检查元数据、IL代码、混淆器、字符串与资源、反射加载、配置解密和网络通信逻辑,并结合反编译工具还原高层代码。适合C#远控、窃密、Loader等样本,可较快定位配置对象、C2地址、加密参数、插件体系和关键业务函数。
在 AI 中使用此 Skill将本页链接复制给 AI,即可让 AI 获取完整 Skill 内容并按此执行
安全提示: 本站 Skill 均经 ChatGPT 最新模型扫描,未发现恶意脚本及危险指令、未检出已知恶意行为特征,但不保证绝对安全,使用即表示接受此风险
Skill 文件
版本 20260301 · 0ff90e4c4dc25065c2b7606d36b67fbe
SKILL.md
---
name: dotnet-malware-analysis
description: >
Analyze .NET (managed-code) malware — identify obfuscators, deobfuscate
with de4dot, decompile to C# with dnSpy/ILSpy, inspect IL metadata tables,
extract embedded resources and staged payloads, map capabilities via P/Invoke
and reflection patterns, and extract configurations and IOCs. Covers pure IL
assemblies, mixed-mode binaries, and single-file .NET deployments across
.NET Framework 2.0–4.8 and .NET 5–8.
---
# .NET Malware Analysis
Analyze Windows malware written in C#, VB.NET, or F# targeting the Common
Language Runtime. .NET malware is the dominant platform for commodity threats
— AgentTesla, AsyncRAT, RedLine, NjRAT, QuasarRAT, and many more — because
it compiles to high-level Intermediate Language (IL) that is trivially
decompilable unless protected by an obfuscator. This skill provides the
foundational .NET reverse-engineering workflow that family-specific skills
(infostealer-analysis, rat-analysis, etc.) build on.
## Prerequisites
- **de4dot** — .NET deobfuscator/unpacker
```bash
# Pre-built release or build from source
git clone https://github.com/de4dot/de4dot && cd de4dot && dotnet build
```
- **dnSpy** (or dnSpyEx fork) — .NET debugger and assembly editor (GUI)
```bash
# Download from https://github.com/dnSpyEx/dnSpy/releases
```
- **ILSpy** / **ilspycmd** — .NET decompiler (CLI-friendly)
```bash
dotnet tool install -g ilspycmd
```
- **Python 3.10+** with optional packages:
```bash
pip install pefile dnfile
```
- **monodis** (Mono) or **ildasm** (.NET SDK) — IL disassembler for raw IL
inspection
- **7-Zip** or **binwalk** — for extracting resources from single-file bundles
- **YARA / YARA-X** — for obfuscator signature scanning (optional)
## Step-by-Step Instructions
### Step 1: Confirm .NET Assembly and Classify Binary Type
Before applying .NET-specific tools, verify the sample is actually a managed
assembly and determine its variant:
1. **Check for mscoree.dll import** — all .NET executables import
`_CorExeMain` from `mscoree.dll` (or `_CorDllMain` for DLLs):
```bash
python scripts/dotnet_analyzer.py --input sample.exe --format json
```
The script checks the PE import table for the `mscoree.dll` reference and
parses the CLI header.
2. **Identify the .NET version**:
- **.NET Framework 2.0–4.8**: `TargetFramework` attribute or metadata
version string (`v4.0.30319`)
- **.NET 5+/Core**: `System.Runtime` assembly reference, deps.json bundle
- **Mixed-mode**: Contains both IL and native code sections (C++/CLI)
3. **Detect single-file bundles** — .NET 5+ can bundle all dependencies into
one executable. Look for the bundle signature (`\x00\x64\x6E\x00`) near
the end of the file. Extract with `dotnet-bundle-extract` or 7-Zip.
4. **Record key metadata** from the CLI header:
- Entry point token (e.g., `0x06000001`)
- Metadata RVA and size
- Flags: `ILONLY`, `32BITREQUIRED`, `STRONGNAMESIGNED`
- Target CPU: AnyCPU, x86, x64
### Step 2: Inspect .NET Metadata Tables
.NET metadata is the richest source of structural information — far more
descriptive than native PE imports:
1. **Assembly-level metadata**:
- `Assembly` table: name, version, culture, public key token
- `AssemblyRef` table: referenced assemblies (reveals framework targets and
third-party dependencies)
- `Module` table: MVID (Module Version ID, useful as a tracking hash)
2. **Type system**:
- `TypeDef` table: all classes/structs/enums defined in the assembly —
enumerate namespaces and class names for reconnaissance
- `TypeRef` table: external types referenced — reveals API surface used
- `MethodDef` table: all methods with signatures — look for suspicious
names, P/Invoke entry points, and the module entry point
3. **Special tables to inspect**:
- `ImplMap` (P/Invoke): native function imports — `VirtualAlloc`,
`WriteProcessMemory`, `CreateRemoteThread` indicate injection
- `ManifestResource`: embedded resources with names and offsets
- `CustomAttribute`: obfuscator watermarks, `DebuggerHidden`,
`SuppressUnmanagedCodeSecurity` attributes
Use `dnfile` in Python for programmatic access:
```python
import dnfile
dn = dnfile.dnPE("sample.exe")
for row in dn.net.mdtables.ImplMap:
print(f"P/Invoke: {row.ImportName} from {row.ImportScope.row.Name}")
```
Or use `monodis --typedef sample.exe` for quick CLI enumeration.
### Step 3: Detect Obfuscator and Protection
Most .NET malware uses commercial or open-source obfuscators. Identifying the
protector determines which deobfuscation approach to use:
1. **Automated detection** with de4dot:
```bash
de4dot --detect-only sample.exe
```
This prints the detected obfuscator name and confidence.
2. **Common obfuscators and their signatures**:
| Obfuscator | Telltale Signs |
|-----------|----------------|
| **ConfuserEx** | `ConfuserEx` in module attributes, `koi` resource name pattern, control flow with `switch` on computed index |
| **.NET Reactor** | `__` prefixed type names, `{GUID}` resource names, embedded native stub in `.rsrc` |
| **SmartAssembly** | `SmartAssembly.Attributes` namespace, `{GUID}.resources` entries, PoweredBy attribute |
| **Crypto Obfuscator** | `CryptoObfuscator` attribute, `costura` or guid-named resources |
| **Eazfuscator.NET** | Single class with many static fields of type `Dictionary<int,string>`, eval-stack based string decryption |
| **Babel Obfuscator** | `Babel.Licensing` attribute, scrambled metadata tokens |
| **Dotfuscator** | Preserved type names but renamed members to `a`, `b`, `c`; `DotfuscatorAttribute` |
| **Agile.NET (CliSecure)** | `<AgileDotNetRT>` or `AgileDotNet.Licenser` attribute |
3. **Manual indicators** when automated detection fails:
- Module-level `CustomAttribute` entries with obfuscator names
- Abnormally large `#Strings` or `#Blob` heap sizes
- Methods with no meaningful names (single chars or unicode garbage)
- Delegate-based proxy calls replacing direct method calls
- `Module.cctor` (module initializer) with decryption/unpacking logic
### Step 4: Deobfuscate
Apply the appropriate deobfuscation strategy based on Step 3:
1. **de4dot automated cleaning** (handles most common obfuscators):
```bash
# Auto-detect and clean
de4dot sample.exe -o sample_clean.exe
# Force a specific obfuscator if auto-detect is wrong
de4dot sample.exe -p cr -o sample_clean.exe # Crypto Obfuscator
de4dot sample.exe -p cf -o sample_clean.exe # ConfuserEx
de4dot sample.exe -p sa -o sample_clean.exe # SmartAssembly
de4dot sample.exe -p dr -o sample_clean.exe # .NET Reactor
```
2. **Verify deobfuscation succeeded**:
- Re-run `de4dot --detect-only sample_clean.exe` — should report
"Unknown/not obfuscated"
- Compare TypeDef counts before and after — names should be readable
- Check that string decryption methods have been inlined
3. **Handle deobfuscation failures**:
- **Anti-tamper protection**: some obfuscators verify assembly integrity
at runtime — patch the check or use dnSpy's debugger to dump after
initialization
- **Control flow flattening**: if de4dot cannot simplify, use
dnSpy debugger to trace actual execution paths
- **Virtualized code**: .NET Reactor and Agile.NET can convert IL to a
custom VM — requires dedicated devirtualizers or dynamic analysis
- **Native unpacking stubs**: mixed-mode assemblies may unpack the IL at
runtime — attach dnSpy debugger and dump `Assembly.Load` calls
4. **Iterative cleaning** — run de4dot multiple times if layers of obfuscation
are nested (common in loaders that unpack a second-stage .NET assembly):
```bash
de4dot stage1.exe -o stage1_clean.exe
# Extract embedded stage2 from resources (see Step 6)
de4dot stage2.dll -o stage2_clean.dll
```
### Step 5: Decompile and Analyze IL
With a clean (or cleaner) assembly, recover C# source code:
1. **Full project decompilation** with ilspycmd:
```bash
ilspycmd sample_clean.exe -o ./decompiled/ -p
```
This creates a `.csproj` and `.cs` files — open in any editor or IDE.
2. **Interactive analysis** with dnSpy:
- Load `sample_clean.exe` — navigate the type tree
- Right-click methods → "Edit IL Instructions" for raw IL view
- Set breakpoints on interesting methods and debug under dnSpy
3. **Key IL patterns to watch for**:
- **Reflection-based loading** — `Assembly.Load(byte[])` or
`Assembly.LoadFrom()` indicate runtime assembly loading (second-stage
payloads):
```csharp
Assembly asm = Assembly.Load(decryptedBytes);
asm.EntryPoint.Invoke(null, null);
```
- **P/Invoke declarations** — native API calls for process injection,
privilege escalation, or anti-analysis:
```csharp
[DllImport("kernel32.dll")]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, ...);
```
- **Dynamic method generation** — `System.Reflection.Emit` namespace usage
indicates runtime code generation to evade static analysis
- **Unsafe code blocks** — `unsafe` keyword with pointer manipulation,
unusual for managed malware and may indicate shellcode injection
- **WMI queries** — `ManagementObjectSearcher` with queries like
`SELECT * FROM Win32_Processor` for VM detection
- **Delegate/proxy patterns** — obfuscators replace direct calls with
delegate invocations; trace the delegate initialization in `.cctor`
4. **Compare decompiled output with raw IL** when the decompiler produces
suspicious or incorrect code — use `ildasm` or `monodis` for ground truth:
```bash
monodis --method "Namespace.Class::MethodName" sample_clean.exe
```
### Step 6: Extract Embedded Resources and Payloads
.NET malware frequently stores encrypted payloads, configuration data, or
second-stage assemblies as embedded resources:
1. **Enumerate resources**:
```bash
monodis --manifest sample_clean.exe
```
Or use dnSpy: expand Resources node in the assembly tree.
2. **Common resource patterns**:
- **Encrypted payloads**: high-entropy resources (> 7.0 bits/byte) named
with GUIDs or random strings — find the decryption routine by searching
for `ResourceManager` or `GetManifestResourceStream` calls
- **Serialized configs**: XML or binary-formatted resources containing C2
URLs, keys, and options
- **Embedded DLLs**: Costura.Fody-packed dependencies stored as compressed
resources prefixed with `costura.`
- **Icon/bitmap steganography**: payloads hidden in image resources using
LSB encoding
3. **Extract and analyze**:
```python
import dnfile
dn = dnfile.dnPE("sample_clean.exe")
for resource in dn.net.resources:
print(f"{resource.name}: offset={resource.offset}, size={resource.size}")
# Dump resource bytes for entropy analysis and decryption
```
4. **Trace the loading code** — search decompiled source for:
- `Assembly.GetManifestResourceStream("resource_name")`
- `ResourceManager.GetObject("key")`
- `Properties.Resources.ResourceName`
- Any method that reads from resources, decrypts, and calls
`Assembly.Load()`
5. **Single-file .NET bundles** (.NET 5+): use `dotnet-bundle-extract` or
search for the bundle header signature to extract all bundled assemblies
and analyze each one individually.
### Step 7: Map Capabilities and Behavior
Enumerate what the malware can do by analyzing API usage patterns unique to
the .NET ecosystem:
1. **Process injection** (via P/Invoke):
- `VirtualAllocEx` + `WriteProcessMemory` + `CreateRemoteThread`
- `NtCreateSection` + `NtMapViewOfSection` (process hollowing)
- `QueueUserAPC` (APC injection)
2. **Credential theft** (managed APIs):
- `System.Security.Cryptography.ProtectedData.Unprotect()` (DPAPI)
- `Microsoft.Win32.Registry` reads from browser credential paths
- `System.Net.NetworkCredential` and `CredentialCache`
- SQLite database reads (browser password stores)
3. **Persistence mechanisms**:
- `Microsoft.Win32.Registry.SetValue()` for Run/RunOnce keys
- `System.IO.File.Copy()` to startup folders
- `System.ServiceProcess.ServiceInstaller` for service creation
- `TaskScheduler` COM interop for scheduled tasks
4. **Network communication**:
- `System.Net.Http.HttpClient` or `WebClient` for C2 beaconing
- `System.Net.Sockets.TcpClient` for raw TCP
- `System.Net.Mail.SmtpClient` for SMTP exfiltration (AgentTesla pattern)
- `System.Net.FtpWebRequest` for FTP exfiltration
5. **Anti-analysis** (.NET-specific patterns):
- `System.Diagnostics.Debugger.IsAttached` check
- `Environment.GetEnvironmentVariable("USERNAME")` sandbox detection
- `ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem")` for
VM manufacturer detection
- `DateTime` comparisons for time-based evasion
- `Thread.Sleep()` with long delays for sandbox timeout
6. **Data collection**:
- `System.Windows.Forms.Clipboard` for clipboard monitoring
- `SetWindowsHookEx` P/Invoke for keylogging
- `Screen.CaptureScreen()` or GDI+ `Graphics.CopyFromScreen()` for
screenshots
- `System.IO.Directory.GetFiles()` recursive scans for document theft
### Step 8: Extract Configuration and IOCs
Most .NET malware families store configuration in predictable locations:
1. **Hardcoded fields** — search decompiled classes for:
- Static string fields with Base64 or hex-encoded values
- Classes named `Config`, `Settings`, `Options`, `Vars`, or single-letter
names with many string fields
- Constructor methods that initialize connection parameters
2. **Encrypted configuration** — common patterns:
- **AES/Rijndael**: `RijndaelManaged` or `Aes.Create()` with hardcoded
key/IV (often derived from a password via `Rfc2898DeriveBytes`)
- **XOR**: byte array XOR with a single-byte or multi-byte key
- **Base64 + AES**: `Convert.FromBase64String()` → AES decrypt
- **DES/3DES**: older families still use `DESCryptoServiceProvider`
3. **Configuration fields to extract**:
- C2 server URLs/IPs and ports
- Encryption keys (AES key, XOR key, RSA public key)
- Mutex names (for single-instance enforcement)
- Installation paths and filenames
- Persistence registry keys
- Campaign ID / bot ID / group tags
- Exfiltration credentials (SMTP user/pass, FTP credentials, Telegram
bot tokens)
- Kill switch domains or dates
4. **Automated extraction** with the included script:
```bash
python scripts/dotnet_analyzer.py --input sample_clean.exe --format json
```
5. **Map findings to MITRE ATT&CK**:
- Process injection → T1055
- Registry Run keys → T1547.001
- Credentials from browsers → T1555.003
- Keylogging → T1056.001
- Screen capture → T1113
- SMTP exfiltration → T1048.003
- Scheduled task → T1053.005
## Output Format
The `dotnet_analyzer.py` script produces a structured JSON report:
```json
{
"sample": "/samples/sample.exe",
"hashes": {
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"sha1": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"is_dotnet": true,
"dotnet_info": {
"clr_version": "v4.0.30319",
"target_framework": ".NETFramework,Version=v4.8",
"flags": ["ILONLY", "32BITREQUIRED"],
"entry_point_token": "0x06000001",
"mvid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"is_mixed_mode": false
},
"obfuscator": {
"name": "ConfuserEx",
"confidence": 0.95,
"indicators": [
"ConfuserEx watermark in module attributes",
"Control flow flattening in entry point"
]
},
"metadata": {
"assembly_name": "WindowsService",
"assembly_version": "1.0.0.0",
"referenced_assemblies": [
"mscorlib, Version=4.0.0.0",
"System.Net.Http, Version=4.0.0.0"
],
"type_count": 42,
"method_count": 187,
"pinvoke_imports": [
{"method": "VirtualAllocEx", "module": "kernel32.dll"},
{"method": "WriteProcessMemory", "module": "kernel32.dll"}
]
},
"resources": [
{
"name": "payload",
"size": 45312,
"entropy": 7.89,
"likely_encrypted": true
}
],
"capabilities": {
"process_injection": true,
"credential_theft": true,
"keylogging": false,
"screen_capture": true,
"persistence": true,
"network_communication": true,
"anti_analysis": true
},
"strings_of_interest": [
"http://evil.example.com/gate.php",
"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run",
"smtp.example.com"
],
"mitre_attack": [
{"technique": "T1055", "name": "Process Injection"},
{"technique": "T1547.001", "name": "Registry Run Keys"},
{"technique": "T1113", "name": "Screen Capture"}
],
"timestamp": "2026-03-24T12:00:00Z"
}
```
## Tips
- **Always deobfuscate before decompiling** — readable names and simplified
control flow make the decompiled C# dramatically easier to understand
- **Check Module.cctor first** — the module initializer (`.cctor` of
`<Module>`) often contains decryption, anti-tamper, or unpacking logic that
runs before `Main()`
- **Use MVID for tracking** — the Module Version ID is a unique GUID per
compilation; it clusters related samples even when hashes differ due to
minor config changes
- **P/Invoke is the attack surface** — most dangerous capabilities require
native API calls; enumerate `ImplMap` entries for a quick threat assessment
- **Watch for Assembly.Load** — dynamic assembly loading is the primary
mechanism for multi-stage .NET malware; set breakpoints on it in dnSpy
- **Resources are payloads until proven otherwise** — high-entropy embedded
resources in .NET malware are almost always encrypted second-stage payloads
or configuration blobs
- **De4dot is not perfect** — it struggles with heavily customized ConfuserEx
forks and .NET Reactor's native code protection; fall back to dnSpy
debugging for runtime unpacking
- **Single-file bundles need extraction first** — .NET 5+ single-file apps
look like native executables but contain bundled managed assemblies; extract
before analyzing
- **Mixed-mode is rare but dangerous** — C++/CLI assemblies combine native and
managed code; analyze both the IL and native sections
- **Combine with family-specific skills**:
- `infostealer-analysis` — AgentTesla, RedLine config extraction workflows
- `rat-analysis` — AsyncRAT, NjRAT, QuasarRAT protocol analysis
- `string-decryption` — .NET string decryption automation
- `malware-deobfuscation` — advanced deobfuscation techniques
- `config-extraction` — standardized config extraction patterns
此文件不提供内嵌文本预览
请使用左侧文件行末尾的外链图标打开原始文件。
