PowerShell Token Substitution Loader
A PowerShell anti-static technique in which the script source is encoded with a single-character token-substitution cipher, then fragmented across multiple SET variable assignments in a parent batch file. The tokens are replaced at runtime by batch variable-expansion or by PowerShell string replacement, producing a fully functional reflective .NET assembly loader.
Pipeline
Stage 1 — Token mapping
The attacker defines a fixed mapping of multi-character tokens to single PowerShell characters:
| Token | Replacement |
|---|---|
#O |
( |
#Q |
) |
#R |
$ |
#W |
newline (\n) |
#X |
[ |
#E |
] |
#D |
. |
#U |
- |
#I |
(deleted / no-op) |
Stage 2 — Fragmentation across batch variables
The encoded payload is split across dozens of batch SET variables, each holding a fragment of the encoded string. For example:
set "_xzg=$ErrorActionPreference=#O#ISt#I#U#Iop#I#Q#W$_iv=#O#IInv#I#U#Ioke#I#Q"
set "_vmg=$_tspm=#O#O#Is#I#U#Iyst#I#Q#U#O#I{0}{1}#I-f#Iem#I,#I.nume#I#Q"
^[/intel/analyses/c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36.html]
Stage 3 — Assembly and decode
The batch script concatenates the variables into a single PowerShell command line, or passes them as an -EncodedCommand / -Command argument. The resulting PowerShell script then performs string replacement to restore the original tokens:
$script = $script.Replace('#O','(').Replace('#Q',')').Replace('#R','$').Replace('#W','`n')
Stage 4 — Reflective execution
The decoded PowerShell:
- Validates
ExecutionContext.SessionState.LanguageModeisFullLanguage - Forces TLS 1.2 via
System.Net.ServicePointManager - Loads
System.Net.HttpforHttpClient - Reads the parent batch file from disk
- Extracts Base64-encoded .NET assembly from
REMlines - Loads the assembly via
[Reflection.Assembly]::Load - Invokes the entry point via reflection (
GetMethod+Invoke) ^[/intel/analyses/c96b83fa0b190f3e09f92507e04b821b98eeed9f51a866a75c188ff08baa3b36.html]
Detection / Triage
- Batch variables with
#tokens:grep -E 'set.*#[OQRWXEUDI]'in a batch file is a strong signal. - Long SET lines:
awk 'length > 500 && /^set/'will surface the encoded fragments. - PowerShell language-mode gate:
FullLanguagecheck is a reliable behavioural indicator. - Reflection assembly load:
[Reflection.Assembly]::Loadinside a batch-launched PowerShell is always suspicious.
Related
- batch-smokescreen-label-obfuscation — Often paired with this technique to hide the batch layer.
- unclassified-batch-powershell-dropper — Family that uses both techniques.
- reflective-assembly-delegate-execution — Generic .NET reflective-loading pattern.