---
name: rt-scenario-d002
description: "D-002: .NET Application dnSpy → License Crack + Credential Extraction. Domain: desktop. Attack chain: run dnSpy on .exe → decompile to C# source → find license check method → patch JUMP instruction → find hardcoded admin credentials in decompiled code. MITRE: T1588 → T1552.001. Real example: Enterprise software: dnSpy reveals license validation bypass + hardcoded DB password in connection string"
---

# D-002: .NET Application dnSpy — License Crack + Credential Extraction

## Overview

| Field | Value |
|---|---|
| Attack Objective | Bypass software license validation and extract hardcoded credentials from a .NET application binary |
| Required Access Level | None (physical or remote access to the binary file is sufficient) |
| Estimated Time | 30–90 minutes depending on binary size and obfuscation level |
| Detection Risk | Low (static analysis; no network traffic, no process injection by default) |

The target is any .NET executable (.exe or .dll) whose license check is implemented in managed code. Because .NET IL (Intermediate Language) compiles to a format that is trivially decompiled back to near-original C#, the license validation routine can be identified, patched at the IL level, and the modified binary saved — all without access to source code. As a bonus, hardcoded connection strings, API keys, or credentials embedded in the binary are fully visible in the decompiled output.

---

## Prerequisites

### Required Tools

| Tool | Purpose | Install |
|---|---|---|
| dnSpy | .NET decompiler and IL-level debugger/patcher | Download release ZIP from https://github.com/dnSpyEx/dnSpy/releases — extract, run `dnSpy.exe` (no install needed) |
| .NET SDK (optional) | Compile test harnesses | `winget install Microsoft.DotNet.SDK.8` |
| de4dot (optional) | Deobfuscation if the binary is packed/obfuscated | Download from https://github.com/de4dot/de4dot/releases |
| HxD or 010 Editor (optional) | Hex-level verification of patched bytes | `winget install MHNexus.HxD` |
| Strings / FLOSS (optional) | Quick pre-scan for embedded credentials | `winget install BinaryAnalysis.FLOSS` |

### Required Access or Conditions

- Read access to the target `.exe` or `.dll` file.
- Write access to the same directory (or any writable directory) to save the patched binary.
- The application must be compiled as managed .NET (Framework 2.0–4.8, .NET Core, .NET 5+). Confirm with: `file target.exe` or open in dnSpy — if the assembly tree populates, it is managed .NET.

### Skill Level

**INTERMEDIATE** — Requires familiarity with C# syntax and basic understanding of IL opcodes (`brfalse`, `brtrue`, `ret`). No prior reverse engineering experience is mandatory, but the ability to read decompiled C# and trace control flow is essential.

---

## Attack Chain

```
[Target .exe]
      |
      v
[1] Run dnSpy on .exe
      |
      v
[2] Decompile to C# source (automatic in dnSpy)
      |
      v
[3] Locate license check method (search for "license", "valid", "trial", "expire", "isRegistered")
      |
      v
[4] Switch to IL view — identify the conditional JUMP (brfalse / brtrue)
      |
      v
[5] Edit IL — patch JUMP to unconditional branch or NOP; or change return value to always-true
      |
      v
[6] Save patched module (dnSpy File > Save Module)
      |
      v
[7] Search decompiled code for connection strings, passwords, API keys
      |
      v
[8] Extract and document all found credentials
```

### MITRE ATT&CK Summary

- **T1588.002** — Obtain Capabilities: Tool (dnSpy acquisition)
- **T1552.001** — Unsecured Credentials: Credentials in Files (hardcoded strings in binary)
- **T1027** — Obfuscated Files or Information (encountered if packer is present; de4dot used to counter)
- **T1600** — Weaken Encryption (license bypass modifies application logic)

---

## Step-by-Step Execution

### Step 1 — Pre-scan for Low-Hanging Credentials (Optional but Recommended)

Run FLOSS or Strings against the binary before opening dnSpy to get a fast list of interesting strings.

```powershell
# Using Sysinternals Strings
strings64.exe -n 8 C:\path\to\target.exe | Select-String -Pattern "password|pwd|connstr|secret|api_key|token|jdbc|Server=" -CaseSensitive:$false
```

**Expected output:**
```
Server=prod-db01;Database=AppDB;User Id=sa;Password=Sup3rS3cr3t!;
ApiKey=sk-live-XXXXXXXXXXXXXXXXXXXX
```

If credentials appear here, document them and proceed to Step 7 for confirmation via decompiled source.

**Fallback:** If Strings is unavailable, skip to Step 2 — dnSpy will reveal the same data.

---

### Step 2 — Open the Binary in dnSpy

1. Launch `dnSpy.exe`.
2. Drag and drop `target.exe` onto the dnSpy Assembly Explorer panel, or use **File > Open** (`Ctrl+O`).
3. The assembly tree expands: `target.exe > {namespace} > {classes}`.

**Expected output:** The left panel shows a tree of namespaces, classes, and methods. The main panel displays decompiled C# automatically when you click a method.

**Fallback — Obfuscated binary:** If the tree shows garbled names (e.g., ``) or dnSpy shows IL errors, run de4dot first:
```powershell
de4dot.exe C:\path\to\target.exe -o C:\path\to\target_clean.exe
```
Then open `target_clean.exe` in dnSpy.

---

### Step 3 — Locate the License Check Method

Use dnSpy's search to find the validation routine.

1. Press **Ctrl+Shift+F** (Search Assemblies) or use **Edit > Search Assemblies**.
2. Set search type to **Member (methods, fields, properties)**.
3. Search for each of the following terms in sequence until results appear:
   - `license`
   - `isValid`
   - `isRegistered`
   - `CheckLicense`
   - `Validate`
   - `trial`
   - `expire`
   - `activate`

**Expected output:** A list of matching methods. Double-click the most relevant result (e.g., `bool CheckLicense(string key)` or `bool IsLicenseValid()`).

**Alternative — Search string literals:**
1. Search type: **String**.
2. Search: `"trial expired"` or `"invalid license"` or `"please register"`.
3. Double-click the result — dnSpy navigates to the method that references the string, revealing the license check in context.

**Fallback:** If no obvious method is found, browse the class containing the application's startup logic (`Program.Main`) and trace calls step by step.

---

### Step 4 — Analyze the License Check in C# View

With the license method open, read the decompiled C# to understand the logic. A typical pattern:

```csharp
private bool IsLicenseValid(string licenseKey)
{
    if (string.IsNullOrEmpty(licenseKey))
        return false;

    string expected = ComputeHash(licenseKey + this.machineId);
    return expected == this.storedHash;  // <-- this comparison controls access
}
```

Note the return path. The goal is to make this method always return `true`.

---

### Step 5 — Switch to IL View and Identify the JUMP Instruction

1. Right-click inside the method body > **Edit IL Instructions** (or press **Ctrl+Shift+E** in some builds; alternatively use the **Edit Method Body** option).
2. Alternatively, right-click the method in the Assembly Explorer > **Edit IL Instructions**.

The IL for the method above typically looks like:

```
IL_0000: ldarg.1
IL_0001: brfalse.s  IL_0020        ; if (licenseKey == null) jump to return false
...
IL_001A: call       bool [mscorlib]System.String::op_Equality(string, string)
IL_001F: brfalse.s  IL_0027        ; if strings not equal, jump to return false
IL_0021: ldc.i4.1                  ; push true
IL_0022: ret
IL_0023: ldc.i4.0                  ; push false  <-- we want to eliminate this path
IL_0024: ret
```

**Identify the key instruction:** The `brfalse.s` or `brtrue.s` that sends control to the `ldc.i4.0 / ret` (return false) block.

---

### Step 6 — Patch the IL Instruction

**Option A — Change `brfalse` to `pop` + `nop` (cleanest patch):**

In the IL editor, locate the problematic `brfalse.s IL_XXXX` that leads to the false return. Change the opcode:

- Change `brfalse.s` → `pop` (removes the boolean from the stack without branching)
- The execution then falls through to `ldc.i4.1 / ret` (returns true).

Or more aggressively:

**Option B — Replace entire method body with `ldc.i4.1 / ret`:**

Clear all IL instructions and replace with:
```
IL_0000: ldc.i4.1
IL_0001: ret
```

This makes the method unconditionally return `true`.

**Option C — Patch a single byte in hex (advanced):**

In HxD, search for the byte sequence corresponding to `brfalse.s` (`0x2C`) followed by the offset. Change `0x2C` to `0x2A` (`pop`) or `0x00` (`nop`... though nop does not consume stack — use with care).

**After editing in dnSpy IL editor:** Click **OK** to confirm changes.

---

### Step 7 — Save the Patched Module

1. In dnSpy, go to **File > Save Module** (`Ctrl+Shift+S`).
2. Choose the output path (recommend saving as `target_patched.exe` to preserve the original).
3. Click **OK** — dnSpy recompiles the modified IL to a valid PE binary.

**Expected output:** A new `.exe` file at the specified path. Launch it and verify the license check is bypassed (the application opens without requesting a license key or displaying a trial warning).

**Fallback — Save fails with error:** If dnSpy reports a metadata error, use **File > Save All** or try right-clicking the module in Assembly Explorer > **Save Module**. If the binary is strongly named (signed), dnSpy will warn that the signature is invalid — the patched binary will run but Windows may warn. Remove the strong name requirement by patching the PE header bit, or use `ildasm` + `ilasm` to recompile.

---

### Step 8 — Extract Hardcoded Credentials from Decompiled Source

With the binary open in dnSpy, search for credential patterns:

1. **Ctrl+Shift+F** > Search type: **String**.
2. Search each term:
   - `password`
   - `Password`
   - `pwd`
   - `Server=`
   - `Data Source=`
   - `apikey`
   - `secret`
   - `Bearer`
   - `token`
   - `connectionString`

3. For each result, double-click to navigate to the context. Read the full string literal and the surrounding variable/field assignment to understand what the credential is used for.

**Expected output — connection string example:**
```csharp
// Found in DatabaseManager.cs > Initialize()
private static string connStr = "Server=prod-db01;Database=AppDB;User Id=sa;Password=Sup3rS3cr3t!;Encrypt=False;";
```

**Expected output — API key example:**
```csharp
// Found in ApiClient.cs > constructor
private const string ApiKey = "sk-live-4f8a2b9c1d3e7f6a0b5c8d2e1f4a7b3c";
```

Document all findings with:
- Class name and method name where found
- The full string value
- The apparent purpose (DB credential, API key, admin password, etc.)

---

## Real-World Reference

**Enterprise Resource Planning (ERP) Software — Composite Finding:**

During an authorized engagement against an on-premise ERP application, dnSpy analysis of the primary executable revealed two critical issues simultaneously:

1. **License Bypass:** The `LicenseManager.Validate()` method performed an HMAC comparison. Patching a single `brfalse.s` to `pop` eliminated the check entirely. The patched binary loaded all premium modules without any license key.

2. **Hardcoded Database Credential:** A `static readonly` field in `DataAccess.ConnectionFactory` contained the literal string:
   ```
   Server=erp-sql-prod;Database=ERPDB;User Id=sa;Password=ERP@dm1n2019!;
   ```
   The `sa` (SQL Server system administrator) account was active on the production SQL Server instance. Using these credentials, full database access — including all customer records, payroll data, and user account tables — was obtained without any additional exploitation.

**Lesson:** A single .NET binary can simultaneously expose both application-layer access control weaknesses and privileged infrastructure credentials, requiring a combined remediation response (license logic moved server-side; credentials externalized to a secrets manager).

---

## MITRE ATT&CK Mapping

| Step | Tactic | Technique ID | Technique Name | Sub-technique |
|---|---|---|---|---|
| 1 — Acquire dnSpy | Resource Development | T1588.002 | Obtain Capabilities: Tool | — |
| 2 — Open binary in dnSpy | Collection | T1005 | Data from Local System | — |
| 3-4 — Decompile .NET to C# | Defense Evasion | T1027 | Obfuscated Files or Information (countering) | T1027.002 — Software Packing |
| 5-6 — Patch IL JUMP instruction | Defense Evasion / Impact | T1562 | Impair Defenses | T1562.001 — Disable or Modify Tools (license control) |
| 7 — Save patched binary | Persistence | T1574 | Hijack Execution Flow | T1574.001 — DLL Search Order (analog: modified binary) |
| 8 — Extract credentials from decompiled code | Credential Access | T1552.001 | Unsecured Credentials: Credentials in Files | — |
| Post — Use extracted DB credentials | Lateral Movement | T1021.002 | Remote Services: SMB/Windows Admin Shares | — |
| Post — Query database with stolen creds | Collection | T1213 | Data from Information Repositories | — |

---

## Detection and OPSEC

### How This Attack Is Detected

| Detection Point | Mechanism |
|---|---|
| dnSpy process execution | EDR/AV flagging `dnSpy.exe` by name or hash; parent-child process anomalies |
| Modified binary execution | File hash mismatch detected by application whitelisting (AppLocker, WDAC) or integrity monitoring (Tripwire, Wazuh FIM) |
| Anomalous DB login | SQL Server audit log: `sa` login from unusual host or at unusual time; failed login attempts before success |
| Network connection string use | DLP or network monitoring detecting outbound SQL traffic from unexpected source |
| Binary diff | If original binary is stored in version control or on a software distribution server, a hash comparison reveals modification |

### How to Reduce Detection Risk During Authorized Engagement

1. **Work on a copy:** Never modify the original binary in-place. Work on a copy in a designated engagement workspace.
2. **Run dnSpy on an isolated analysis workstation:** Avoid running reverse engineering tools on the production environment or on a system with EDR that will alert on tool execution.
3. **Do not execute the patched binary in production:** Verify the patch works in an isolated lab environment that mirrors production.
4. **Use a dedicated engagement account:** If testing extracted credentials, use a monitored test account or coordinate with the blue team so that legitimate credential testing does not trigger incident response.
5. **Coordinate timing:** If the engagement includes active monitoring tests, notify the SOC before using extracted credentials so alerts are acknowledged, not escalated.
6. **Minimize credential use:** Test extracted credentials once to confirm validity; document and stop. Do not perform bulk data extraction unless explicitly in scope.

### Artifacts Left Behind

| Artifact | Location | Notes |
|---|---|---|
| dnSpy recent files list | `%AppData%\dnSpy\dnSpy.xml` | Contains paths to analyzed binaries |
| Patched binary file | Wherever saved by analyst | Identifiable by modified hash |
| dnSpy crash logs | `%AppData%\dnSpy\` | May contain method names and paths |
| Windows Prefetch for dnSpy | `C:\Windows\Prefetch\DNSPY.EXE-*.pf` | Records that dnSpy was executed |
| SQL Server audit log | SQL Server instance | Login events from credential test |
| PowerShell history | `%AppData%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt` | Any commands run during pre-scan |

---

## Cleanup

Perform all cleanup steps after engagement is complete and findings are documented.

```powershell
# 1. Remove patched binary
Remove-Item "C:\path\to\target_patched.exe" -Force

# 2. Remove de4dot output if used
Remove-Item "C:\path\to\target_clean.exe" -Force

# 3. Clear dnSpy recent files (close dnSpy first)
Remove-Item "$env:AppData\dnSpy\dnSpy.xml" -Force -ErrorAction SilentlyContinue

# 4. Clear dnSpy logs
Remove-Item "$env:AppData\dnSpy\*.log" -Force -ErrorAction SilentlyContinue

# 5. Clear PowerShell history
Remove-Item "$env:AppData\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" -Force -ErrorAction SilentlyContinue

# 6. Clear Windows Prefetch entry (requires admin)
Remove-Item "C:\Windows\Prefetch\DNSPY.EXE-*.pf" -Force -ErrorAction SilentlyContinue

# 7. Clear Strings/FLOSS output if saved to file
Remove-Item "C:\path\to\strings_output.txt" -Force -ErrorAction SilentlyContinue

# 8. Verify no patched binary remains
Get-ChildItem "C:\path\to\engagement-folder\" | Select-Object Name, LastWriteTime
```

**SQL Server credential test cleanup:**
- Coordinate with the DBA or system owner to review and purge SQL Server audit logs for the engagement window, per the rules of engagement.
- If a test account was used, disable or delete it after the engagement.

**Document everything before cleanup:** Ensure all screenshots, dnSpy exports, and credential findings are captured in the engagement report before removing artifacts.

---

## References

| Resource | URL / Details |
|---|---|
| dnSpy (maintained fork) | https://github.com/dnSpyEx/dnSpy |
| de4dot .NET deobfuscator | https://github.com/de4dot/de4dot |
| FLOSS (string extraction) | https://github.com/mandiant/flare-floss |
| .NET IL Opcodes reference | https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes |
| MITRE T1552.001 — Credentials in Files | https://attack.mitre.org/techniques/T1552/001/ |
| MITRE T1588.002 — Obtain Capabilities: Tool | https://attack.mitre.org/techniques/T1588/002/ |
| MITRE T1027 — Obfuscated Files or Information | https://attack.mitre.org/techniques/T1027/ |
| SharpLab (.NET IL explorer, online) | https://sharplab.io |
| .NET Reflector (commercial alternative to dnSpy) | https://www.red-gate.com/products/reflector/ |
| ILSpy (open-source alternative) | https://github.com/icsharpcode/ILSpy |
| OWASP — Insecure Storage of Sensitive Information | https://owasp.org/www-community/vulnerabilities/Insecure_Storage |
| CWE-312 — Cleartext Storage of Sensitive Information | https://cwe.mitre.org/data/definitions/312.html |
| CWE-259 — Use of Hard-coded Password | https://cwe.mitre.org/data/definitions/259.html |
