Its goal is to transform code to hinder analysis and static signatures, useful in labs and authorized Red Team/Pentesting engagements.
Supports 6 levels of obfuscation plus a transforms/pipeline architecture that allows stacking techniques such as string tokenization, light literal encryption, number masking, identifier morphing, format "jitter," control-flow cosmetics, dead code injection, fragmentation profiles, and deterministic profiles.
⚠️ Responsible use: this tool is intended for research and authorized testing only. Do not use for malicious purposes.
./psobf -h
██████╗ ███████╗ ██████╗ ██████╗ ███████╗
██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝
██████╔╝███████╗██║ ██║██████╔╝█████╗
██╔═══╝ ╚════██║██║ ██║██╔══██╗██╔══╝
██║ ███████║╚██████╔╝██████╔╝██║
╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝
Omar Salazar
v.2.0.0
Usage: psobf -i <inputFile> -o <outputFile> -level <1|2|3|4|5|6> [options]
Obfuscation Levels:
1 - Char join encoding
2 - Base64 encoding
3 - Base64 encoding (alternate)
4 - GZip + Base64 compression
5 - Script fragmentation
6 - AES-256 CTR encryption (NEW in 2.0.0)
Transform Pipeline Options (use with -pipeline):
iden - Identifier morphing (use with -iden obf)
strenc - String encryption (use with -strenc xor|rc4)
stringdict - String tokenization (use with -stringdict N)
numenc - Number encoding
fmt - Format jitter (use with -fmt jitter)
cf - Control flow obfuscation (use with -cf-opaque, -cf-shuffle)
dead - Dead code injection (use with -deadcode N)
hexenc - Hex string encoding (NEW)
alias - Cmdlet alias substitution (NEW)
unicode - Unicode character encoding (NEW)
antidebug - Anti-debugging/VM detection (NEW)
iexobf - Invoke-Expression obfuscation (NEW)
Examples:
# Simple obfuscation
psobf -i script.ps1 -o out.ps1 -level 2
# AES encryption with heavy profile
psobf -i script.ps1 -o out.ps1 -level 6 -profile heavy
# All new transforms
psobf -i script.ps1 -o out.ps1 -level 4 -pipeline "iden,alias,hexenc,antidebug,iexobf" -iden obf
# RC4 string encryption
psobf -i script.ps1 -o out.ps1 -level 4 -pipeline "strenc" -strenc rc4 -strkey 0011223344556677
go install github.com/TaurusOmar/psobf/v2/cmd/psobf@v2.0.1
psobf -i input.ps1 -o out.ps1 -level 1..6 [options]
psobf -h # full help
go install github.com/TaurusOmar/psobf/cmd/psobf@v2.0.1
psobf -i input.ps1 -o out.ps1 -level 1..6 [options]
psobf -h # full help
| Flag | Type / Values | Default | Description | Example | ||
|---|---|---|---|---|---|---|
-i |
string | — | Input PS1 (use -stdin to read from pipe) |
-i script.ps1 |
||
-o |
string | obfuscated.ps1 |
Output (use -stdout to write to STDOUT) |
-o out.ps1 |
||
-level |
1..6 | 1 | Final packer (see Levels) | -level 4 |
||
-noexec |
bool | false | Emit only payload (no Invoke-Expression) |
-noexec |
||
-stdin |
bool | false | Read PS from STDIN | -stdin |
||
-stdout |
bool | false | Write result to STDOUT | -stdout |
||
-seed |
int64 | random | Reproducible randomness | -seed 42 |
||
-q |
bool | false | Quiet (no banner) | -q |
||
-pipeline |
csv | — | Transforms to apply in order | -pipeline "iden,strenc,stringdict,numenc,fmt,cf,dead,frag" |
||
-iden |
keep/obf |
keep |
Identifier morphing (vars & funcs) | -iden obf |
||
-strenc |
off/xor/rc4 |
off |
String literal encryption | -strenc rc4 |
||
-strkey |
hex | — | Key for -strenc |
-strkey 0011223344556677 |
||
-stringdict |
0..100 | 0 | Tokenize long strings; % chance per literal | -stringdict 40 |
||
-numenc |
bool | false | Encode numbers as arithmetic PS expressions | -numenc |
||
-fmt |
off/jitter |
off |
Randomize whitespace/line breaks | -fmt jitter |
||
-cf-opaque |
bool | false | Wrap in if(1 -eq 1){...} |
-cf-opaque |
||
-cf-shuffle |
bool | false | Reorder function blocks | -cf-shuffle |
||
-deadcode |
0..100 | 0 | Probability to inject dead code | -deadcode 20 |
||
-frag |
`profile=tight | medium | loose` | — | Fragmentation profile (level 5) | -frag profile=medium |
-minfrag |
int | 10 | Min fragment size (level 5) | -minfrag 8 |
||
-maxfrag |
int | 20 | Max fragment size (level 5) | -maxfrag 16 |
||
-profile |
`light | balanced | heavy` | — | Presets for pipeline/seed/etc. | -profile heavy |
-fuzz |
int | 0 | Produce N variants (different seeds) | -fuzz 5 |
||
-poly |
int | 0 | Polymorphic variants per transformation | -poly 3 |
The pipeline runs before the final
-levelpacking.
To keep examples benign, we’ll use:
Write-Host "Hello, World!"
$answer = 42
function Greet($name) { Write-Host ("Hi, " + $name) }
Greet "Ada"
The following show the shape of outputs (snippets). Actual payloads will differ.
psobf -i sample.ps1 -o out.ps1 -level 1
Output (snippet):
$obfuscated = $([char[]](87,114,105,116,101,45,72,111,115,116,32,34,72,101,108,108,111,44,32,87,111,114,108,100,33,34,10,36,97,110,115,119,101,114,32,61,32,52,50,10,102,117,110,99,116,105,111,110,32,71,114,101,101,116,40,36,110,97,109,101,41,32,123,32,87,114,105,116,101,45,72,111,115,116,32,40,34,72,105,44,32,34,32,43,32,36,110,97,109,101,41,32,125,10,71,114,101,101,116,32,34,65,100,97,34,10) -join ''); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 2
Output (snippet):
$obfuscated = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('V3JpdGUtSG9zdCAiSGVsbG8sIFdvcmxkISIKJGFuc3dlciA9IDQyCmZ1bmN0aW9uIEdyZWV0KCRuYW1lKSB7IFdyaXRlLUhvc3QgKCJIaSwgIiArICRuYW1lKSB9CkdyZWV0ICJBZGEiCg==')); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 3
Output (snippet):
$e = [Convert]::FromBase64String('V3JpdGUtSG9zdCAiSGVsbG8sIFdvcmxkISIKJGFuc3dlciA9IDQyCmZ1bmN0aW9uIEdyZWV0KCRuYW1lKSB7IFdyaXRlLUhvc3QgKCJIaSwgIiArICRuYW1lKSB9CkdyZWV0ICJBZGEiCg=='); $obfuscated = [Text.Encoding]::UTF8.GetString($e); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 4
Output (snippet):
$compressed = 'H4sIAAAAAAAA/wovyixJ1fXILy5RUPJIzcnJ11EIzy/KSVFU4lJJzCsuTy1SsFUwMeJKK81LLsnMz1NwL0pNLdFQyUvMTdVUqFZA0q+h5JGpo6CkoK0Ala3lAitWUHJMSVTiAgQAAP//m+Ey2GoAAAA='; $bytes = [Convert]::FromBase64String($compressed); $ms = New-Object IO.MemoryStream(,$bytes); $gz = New-Object IO.Compression.GzipStream($ms,[IO.Compression.CompressionMode]::Decompress); $sr = New-Object IO.StreamReader($gz); $obfuscated = $sr.ReadToEnd(); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 5
Output (snippet):
$fragments = @('Write-Host "Hello',', World!"
$','answer = 42','
function G','reet($name)',' { Write-Ho','st ("Hi, " ','+ $name) }
','Greet "Ada"','
'); $script = $fragments -join ''; Invoke-Expression $script
psobf -i sample.ps1 -o out.ps1 -level 6
Output (snippet):
$k=[Convert]::FromBase64String('...base64key...');$iv=[Convert]::FromBase64String('...base64iv...');$e=[Convert]::FromBase64String('...base64ciphertext...');$a=New-Object Security.Cryptography.AesManaged;$a.Key=$k;$a.IV=$iv;$d=$a.CreateDecryptor();$bytes=$d.TransformFinalBlock($e,0,$e.Length);$dec=[Text.Encoding]::UTF8.GetString($bytes);Invoke-Expression $dec
Use
-noexecto inspect payloads without executing.
-iden)__$.Command
psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "iden" -iden obf -seed 11
Output (snippet)
$WguE = 42
function QhZy($Chx){ Write-Host ("Hi, " + $Chx) }
QhZy "Ada"
-strenc xor|rc4)Encrypts string literals only (no API tampering). Decrypts just-in-time at runtime. Flags: -strenc xor|rc4, -strkey .
Command
psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "strenc" -strenc xor -strkey a1b2c3d4 -seed 42
Output (snippet)
$b=[Convert]::FromBase64String('EwAB...'); for($i=0;$i -lt $b.Length;$i++){$b[$i]=$b[$i] -bxor 0xA1}; [Text.Encoding]::UTF8.GetString($b)
Command
psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "strenc" -strenc rc4 -strkey 0011223344556677 -seed 7
Output (snippet)
function __decGWREVT($k,[byte[]]$d){ $s=0..255; $j=0; for($i=0;$i -lt 256;$i++){ $j=($j+$s[$i]+$k[$i%$k.Length])%256; $t=$s[$i];$s[$i]=$s[$j];$s[$j]=$t } $i=0;$j=0; for($x=0;$x -lt $d.Length;$x++){ $i=($i+1)%256;$j=($j+$s[$i])%256; $t=$s[$i];$s[$i]=$s[$j];$s[$j]=$t; $d[$x]=$d[$x] -bxor $s[($s[$i]+$s[$j])%256] } [Text.Encoding]::UTF8.GetString($d) }
...
( __decGWREVT ([byte[]](0..(8-1)|%{[Convert]::ToByte('0011223344556677'.Substring($_*2,2),16)})) ([Convert]::FromBase64String('m7m7...')) )
-stringdict)Tokenizes long strings into a $D array and rebuilds them at runtime. Reduces repetitive signatures.
Flag: -stringdict <0..100>
Command
```bash psobf -i sample.ps1 -o out.ps1 -level 3 -pipeline "stringdic
$ claude mcp add psobf \
-- python -m otcore.mcp_server <graph>