Claude
Skills
Sign in
Back

reverse-engineering-dotnet-malware-with-dnspy

Included with Lifetime
$97 forever

Reverse engineers .NET malware using dnSpy decompiler and debugger to analyze C#/VB.NET source code, identify obfuscation techniques, extract configurations, and understand malicious functionality including stealers, RATs, and loaders. Activates for requests involving .NET malware analysis, C# malware decompilation, managed code reverse engineering, or .NET obfuscation analysis.

Generalmalwaredotnetreverse-engineeringdnSpydecompilationscripts

What this skill does


# Reverse Engineering .NET Malware with dnSpy

## When to Use

- A malware sample is identified as a .NET assembly (C#, VB.NET, F#) requiring decompilation
- Analyzing .NET-based malware families (AgentTesla, AsyncRAT, RedLine Stealer, Quasar RAT)
- Deobfuscating .NET code protected by ConfuserEx, SmartAssembly, or custom obfuscators
- Extracting hardcoded C2 configurations, encryption keys, and credentials from managed assemblies
- Debugging .NET malware at runtime to observe decryption routines and dynamic behavior

**Do not use** for native (unmanaged) PE binaries; use Ghidra or IDA for native code analysis.

## Prerequisites

- dnSpy or dnSpyEx installed (https://github.com/dnSpyEx/dnSpy - community maintained fork)
- de4dot for automated .NET deobfuscation (`https://github.com/de4dot/de4dot`)
- ILSpy as an alternative decompiler for cross-validation
- .NET SDK installed for recompiling modified assemblies during analysis
- Isolated Windows VM for running dnSpy debugger on live malware
- Detect It Easy (DIE) for identifying the .NET obfuscator used

## Workflow

### Step 1: Identify .NET Assembly and Obfuscator

Verify the sample is a .NET binary and detect protection:

```bash
# Check if file is .NET assembly
file suspect.exe
# Output should contain "PE32 executable" with .NET metadata

# Detect obfuscator with Detect It Easy
diec suspect.exe

# Python-based .NET detection
python3 << 'PYEOF'
import pefile

pe = pefile.PE("suspect.exe")

# Check for .NET COM descriptor
if hasattr(pe, 'DIRECTORY_ENTRY_COM_DESCRIPTOR'):
    print("[*] .NET assembly detected")
    print(f"    Runtime version: {pe.DIRECTORY_ENTRY_COM_DESCRIPTOR}")
else:
    # Check for mscoree.dll import (alternative detection)
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        if entry.dll.decode().lower() == "mscoree.dll":
            print("[*] .NET assembly detected (mscoree.dll import)")
            break
    else:
        print("[!] Not a .NET assembly")

# Check section names for .NET indicators
for section in pe.sections:
    name = section.Name.decode().rstrip('\x00')
    if name in ['.text', '.rsrc', '.reloc']:
        print(f"    Section: {name} (typical .NET)")
PYEOF
```

### Step 2: Deobfuscate with de4dot

Remove common .NET obfuscation before manual analysis:

```bash
# Run de4dot to identify and remove obfuscation
de4dot suspect.exe -o suspect_cleaned.exe

# Force specific deobfuscator
de4dot suspect.exe -p cf  # ConfuserEx
de4dot suspect.exe -p sa  # SmartAssembly
de4dot suspect.exe -p dr  # Dotfuscator
de4dot suspect.exe -p rv  # Reactor
de4dot suspect.exe -p bl  # Babel.NET

# Verbose output for debugging
de4dot -v suspect.exe -o suspect_cleaned.exe

# Handle multi-file assemblies
de4dot suspect.exe suspect_helper.dll -o cleaned/
```

```
Common .NET Obfuscators:
━━━━━━━━━━━━━━━━━━━━━━━
ConfuserEx:      String encryption, control flow, anti-debug, anti-tamper
SmartAssembly:   String encoding, flow obfuscation, pruning
Dotfuscator:     Renaming, string encryption, control flow
.NET Reactor:    Native code generation, necrobit, anti-debug
Babel.NET:       String encryption, resource encryption, code virtualization
Crypto Obfuscator: String encryption, anti-debug, watermarking
Custom:          Malware-specific obfuscation (manual de4dot configuration needed)
```

### Step 3: Open in dnSpy and Analyze Code

Load the deobfuscated assembly in dnSpy for source-level analysis:

```
dnSpy Analysis Workflow:
━━━━━━━━━━━━━━━━━━━━━━━
1. File -> Open -> Select cleaned assembly
2. Navigate to the entry point:
   - Assembly Explorer -> <namespace> -> Program class -> Main method
   - Or: Right-click assembly -> Go to Entry Point

3. Key areas to examine:
   - Entry point (Main) for initialization and execution flow
   - Form classes for UI-based malware (RATs, stealers)
   - Network/HTTP classes for C2 communication
   - Crypto/encryption classes for data protection
   - Resource access for embedded payloads
   - Timer/Thread classes for persistence and scheduling

4. Navigation shortcuts:
   Ctrl+G       - Go to token/address
   Ctrl+Shift+K - Search assemblies
   F12          - Go to definition
   Ctrl+R       - Analyze (find usages)
   F5           - Start debugging
   F9           - Toggle breakpoint
```

### Step 4: Extract Configuration and C2 Data

Locate hardcoded configuration in the decompiled source:

```csharp
// Common .NET malware configuration patterns:

// Pattern 1: Static class with hardcoded values
public static class Config {
    public static string Host = "185.220.101.42";
    public static int Port = 4782;
    public static string Key = "GhOsT_RaT_2025";
    public static string Mutex = "AsyncMutex_6SI8OkPnk";
    public static bool Install = true;
    public static string InstallFolder = "%AppData%";
}

// Pattern 2: Encrypted strings decrypted at runtime
public static string Decrypt(string input) {
    byte[] data = Convert.FromBase64String(input);
    byte[] key = Encoding.UTF8.GetBytes("SecretKey123");
    for (int i = 0; i < data.Length; i++) {
        data[i] ^= key[i % key.Length];
    }
    return Encoding.UTF8.GetString(data);
}

// Pattern 3: Resource-embedded configuration
byte[] configData = Properties.Resources.config;
string config = AES.Decrypt(configData, derivedKey);
```

```python
# Python script to extract .NET resource strings
import subprocess
import re
import base64

# Use monodis (Mono) or ildasm (.NET SDK) to dump IL
result = subprocess.run(
    ["monodis", "--output=il_dump.il", "suspect_cleaned.exe"],
    capture_output=True, text=True
)

# Search for string literals in IL dump
with open("il_dump.il", errors="ignore") as f:
    il_code = f.read()

# Find ldstr (load string) instructions
strings = re.findall(r'ldstr\s+"([^"]+)"', il_code)
for s in strings:
    # Check for Base64 encoded strings
    try:
        decoded = base64.b64decode(s).decode('utf-8', errors='ignore')
        if len(decoded) > 3 and decoded.isprintable():
            print(f"  Base64: {s[:40]}... -> {decoded[:100]}")
    except:
        pass
    # Check for URLs/IPs
    if re.match(r'https?://', s) or re.match(r'\d+\.\d+\.\d+\.\d+', s):
        print(f"  Network: {s}")
```

### Step 5: Debug with dnSpy

Set breakpoints and debug the malware to observe runtime behavior:

```
dnSpy Debugging Workflow:
━━━━━━━━━━━━━━━━━━━━━━━
1. Set breakpoints on key methods:
   - String decryption functions (to capture decrypted values)
   - Network connection methods (to capture C2 URLs)
   - File write operations (to see what is dropped)
   - Registry modification methods (to see persistence)

2. Debug -> Start Debugging (F5)
   - Select the assembly to debug
   - Set command-line arguments if needed
   - Configure exception handling (break on all CLR exceptions)

3. At each breakpoint:
   - Inspect local variables (Locals window)
   - Evaluate expressions (Immediate window)
   - View call stack to understand execution context
   - Step over (F10) / Step into (F11) / Step out (Shift+F11)

4. Capture decrypted strings:
   - Set breakpoint after decryption function returns
   - Read the return value from the Locals window
   - Document all decrypted configuration values
```

### Step 6: Document Findings

Compile analysis results into a structured report:

```
Analysis documentation should include:
- .NET assembly metadata (CLR version, target framework, compilation info)
- Obfuscator identified and deobfuscation method used
- Complete C2 configuration (hosts, ports, encryption keys, mutex names)
- Malware capabilities (keylogging, screen capture, file theft, etc.)
- Persistence mechanisms (registry, scheduled tasks, startup folder)
- Anti-analysis techniques (VM detection, debugger detection, sandbox evasion)
- Extracted IOCs (C2 IPs/domains, file hashes, mutex names, registry keys)
- YARA rule based on unique code patterns or strings
```

## Key Concepts

| Term | Definition |
|------|------------|
| **CIL/MSIL** | Common Intermediate Language; the bytecode format .NET ass

Related in General