
Obfuscateur PowerShell
Son objectif est de transformer le code pour entraver l'analyse et les signatures statiques, utile dans les laboratoires et les engagements autorisés de Red Team/Pentesting.
Il prend en charge 6 niveaux d'obfuscation plus une architecture de transformations/pipeline qui permet d'empiler des techniques telles que la tokenization de chaînes, le chiffrement léger de littéraux, le masquage de nombres, le morphing d'identifiants, le « jitter » de format, les cosmétiques de flux de contrôle, l'injection de code mort, les profils de fragmentation et les profils déterministes.
⚠️ Utilisation responsable : cet outil est destiné uniquement à la recherche et aux tests autorisés. Ne pas utiliser à des fins malveillantes.```powershell
./psobf -h
██████╗ ███████╗ ██████╗ ██████╗ ███████╗
██╔══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝
██████╔╝███████╗██║ ██║██████╔╝█████╗
██╔═══╝ ╚════██║██║ ██║██╔══██╗██╔══╝
██║ ███████║╚██████╔╝██████╔╝██║
╚═╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝
Omar Salazar
v.2.0.0
Usage: psobf -i -o -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:
psobf -i script.ps1 -o out.ps1 -level 2
psobf -i script.ps1 -o out.ps1 -level 6 -profile heavy
psobf -i script.ps1 -o out.ps1 -level 4 -pipeline "iden,alias,hexenc,antidebug,iexobf" -iden obf
psobf -i script.ps1 -o out.ps1 -level 4 -pipeline "strenc" -strenc rc4 -strkey 0011223344556677
## Fonctionnalités
<h1 align="center">
<img src="https://assets.kitploit.com/production/public/readmes/6598/11956ab9f65e8dc1b0460652042c601f70d28bb67e9fd6f0a89813ed5369997f.gif" alt="psobf" width="700px"></a>
<br>
</h1>
<h1 align="center">
<img src="https://raw.githubusercontent.com/taurusomar/psobf/HEAD/static/poc2.gif" alt="psobf" width="700px"></a>
<br>
</h1>
---
## Installation```bash
go install github.com/TaurusOmar/psobf/v2/cmd/[email protected]
psobf -i input.ps1 -o out.ps1 -level 1..6 [options] psobf -h # full help
## Fonctionnalités
<h1 align="center">
<img src="https://assets.kitploit.com/production/public/readmes/6598/11956ab9f65e8dc1b0460652042c601f70d28bb67e9fd6f0a89813ed5369997f.gif" alt="psobf" width="700px"></a>
<br>
</h1>
<h1 align="center">
<img src="https://raw.githubusercontent.com/taurusomar/psobf/HEAD/static/poc2.gif" alt="psobf" width="700px"></a>
<br>
</h1>
---
## Installation```bash
go install github.com/TaurusOmar/psobf/cmd/[email protected]
psobf -i input.ps1 -o out.ps1 -level 1..6 [options] psobf -h # full help
---
## Référence complète des drapeaux
| Drapeau | Type / Valeurs | Défaut | Description | Exemple | | |
| -------------- | ----------------- | --------------: | ------------------------------------------- | ------------------------------------------------------------ | ------------------------------- | ---------------------- |
| `-i` | string | — | Script PS1 en entrée (utilisez `-stdin` pour lire depuis un pipe) | `-i script.ps1` | | |
| `-o` | string | `obfuscated.ps1` | Sortie (utilisez `-stdout` pour écrire sur STDOUT) | `-o out.ps1` | | |
| `-level` | 1..6 | 1 | Packer final (voir Niveaux) | `-level 4` | | |
| `-noexec` | bool | false | Émettre uniquement la charge utile (sans `Invoke-Expression`) | `-noexec` | | |
| `-stdin` | bool | false | Lire le script depuis STDIN | `-stdin` | | |
| `-stdout` | bool | false | Écrire le résultat sur STDOUT | `-stdout` | | |
| `-seed` | int64 | random | Aléatoire reproductible | `-seed 42` | | |
| `-q` | bool | false | Silencieux (pas de bannière) | `-q` | | |
| `-pipeline` | csv | — | Transformations à appliquer dans l'ordre | `-pipeline "iden,strenc,stringdict,numenc,fmt,cf,dead,frag"` | | |
| `-iden` | `keep`/`obf` | `keep` | Morphing d'identifiants (variables et fonctions) | `-iden obf` | | |
| `-strenc` | `off`/`xor`/`rc4` | `off` | Chiffrement des littéraux de chaînes | `-strenc rc4` | | |
| `-strkey` | hex | — | Clé pour `-strenc` | `-strkey 0011223344556677` | | |
| `-stringdict` | 0..100 | 0 | Tokeniser les longues chaînes ; % de chance par littéral | `-stringdict 40` | | |
| `-numenc` | bool | false | Coder les nombres en expressions PS arithmétiques | `-numenc` | | |
| `-fmt` | `off`/`jitter` | `off` | Aléatoiriser les espaces/sauts de ligne | `-fmt jitter` | | |
| `-cf-opaque` | bool | false | Envelopper dans `if(1 -eq 1){...}` | `-cf-opaque` | | |
| `-cf-shuffle` | bool | false | Réorganiser les **blocs de fonctions** | `-cf-shuffle` | | |
| `-deadcode` | 0..100 | 0 | Probabilité d'injecter du code mort | `-deadcode 20` | | |
| `-frag` | \`profile=tight | medium | loose\` | — | Profil de fragmentation (niveau 5) | `-frag profile=medium` |
| `-minfrag` | int | 10 | Taille minimale de fragment (niveau 5) | `-minfrag 8` | | |
| `-maxfrag` | int | 20 | Taille maximale de fragment (niveau 5) | `-maxfrag 16` | | |
| `-profile` | \`light | balanced | heavy\` | — | Préréglages pour pipeline/seed/etc. | `-profile heavy` |
| `-fuzz` | int | 0 | Produire N variantes (graines différentes) | `-fuzz 5` | | |
| `-poly` | int | 0 | Variantes polymorphiques par transformation | `-poly 3` | | |
> Le **pipeline** s'exécute **avant** le paquetage final **`-level`**.
---
## Exemple de script d'entrée (sûr)
Pour garder les exemples inoffensifs, nous utiliserons :```powershell
Write-Host "Hello, World!"
$answer = 42
function Greet($name) { Write-Host ("Hi, " + $name) }
Greet "Ada"
Ce qui suit montre la forme des sorties (extraits). Les charges utiles réelles différeront.
psobf -i sample.ps1 -o out.ps1 -level 1
**Sortie (extrait) :**```powershell
$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
**Sortie (extrait) :**```powershell
$obfuscated = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('V3JpdGUtSG9zdCAiSGVsbG8sIFdvcmxkISIKJGFuc3dlciA9IDQyCmZ1bmN0aW9uIEdyZWV0KCRuYW1lKSB7IFdyaXRlLUhvc3QgKCJIaSwgIiArICRuYW1lKSB9CkdyZWV0ICJBZGEiCg==')); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 3
**Sortie (extrait):**```powershell
$e = [Convert]::FromBase64String('V3JpdGUtSG9zdCAiSGVsbG8sIFdvcmxkISIKJGFuc3dlciA9IDQyCmZ1bmN0aW9uIEdyZWV0KCRuYW1lKSB7IFdyaXRlLUhvc3QgKCJIaSwgIiArICRuYW1lKSB9CkdyZWV0ICJBZGEiCg=='); $obfuscated = [Text.Encoding]::UTF8.GetString($e); Invoke-Expression $obfuscated
psobf -i sample.ps1 -o out.ps1 -level 4
**Sortie (snippet):**```powershell
$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
**Sortie (extrait) :**```powershell
$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
**Sortie (extrait):**```powershell
$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
Utilisez
-noexecpour inspecter les payloads sans les exécuter.
-iden)__$.Commande```bash psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "iden" -iden obf -seed 11
**Sortie (extrait)**```powershell
$WguE = 42
function QhZy($Chx){ Write-Host ("Hi, " + $Chx) }
QhZy "Ada"
-strenc xor|rc4)Chiffre uniquement les littéraux de chaîne (pas de falsification d'API). Déchiffre juste-à-temps à l'exécution.
Indicateurs : -strenc xor|rc4, -strkey <hex>.
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "strenc" -strenc xor -strkey a1b2c3d4 -seed 42
**Sortie (extrait)**```powershell
$b=[Convert]::FromBase64String('EwAB...'); for($i=0;$i -lt $b.Length;$i++){$b[$i]=$b[$i] -bxor 0xA1}; [Text.Encoding]::UTF8.GetString($b)
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "strenc" -strenc rc4 -strkey 0011223344556677 -seed 7
**Sortie (extrait)**```powershell
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)Tokenise les longues chaînes en un tableau $D et les reconstruit à l'exécution. Réduit les signatures répétitives.
Option : -stringdict <0..100>
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 3 -pipeline "stringdict" -stringdict 40 -seed 1
**Sortie (snippet)**```powershell
$D=@('Hello',', World','!','Hi, ', 'Ada');
Write-Host ($D[0]+$D[1]+$D[2])
function Greet($name){ Write-Host ($D[3] + $name) }
Greet $D[4]
-numenc)Remplace les nombres bruts par des expressions arithmétiques/bit à bit équivalentes (en dehors des chaînes).
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "numenc" -numenc -seed 1337
**Sortie (extrait)**```powershell
$answer = ((0x2A -bxor 0x00)+0)
Attention : Les redirections comme 2>&1 doivent rester identiques. Si votre source contient des redirections non citées et que vous rencontrez des problèmes, désactivez -numenc ou encapsulez ces redirections dans des chaînes dans la source.
-fmt)Randomise les espaces et les sauts de ligne.
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "fmt" -fmt jitter -seed 20
**Sortie (extrait)**```powershell
Write-Host "Hello, World!"
$answer=42
function Greet($name) { Write-Host ("Hi, "+$name) }
Greet "Ada"
-cf-opaque, -cf-shuffle)-cf-opaque : encapsule l'ensemble du script dans une branche jamais fausse.-cf-shuffle : réorganise les blocs de fonctions (pas les instructions individuelles). Vous ne remarquerez des changements que si votre script définit des fonctions.Commande```bash psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "cf" -cf-opaque -cf-shuffle -seed 77
**Sortie (extrait)**```powershell
if(1 -eq 1){
function Greet($name){ Write-Host ("Hi, " + $name) }
Write-Host "Hello, World!"
$answer = 42
Greet "Ada"
}
-deadcode)Injecte des fonctions no-op, des boucles à 0 itération, des chaînes inoffensives, etc. Contrôlé par probabilité. Drapeau : -deadcode <0..100> (probabilité d'injection de snippet).
Commande```bash psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "dead" -deadcode 25 -seed 5
**Sortie (extrait)**```powershell
function __dummyzQJxJk { return }
for($i=0;$i -lt 0;$i++){Start-Sleep -Milliseconds 0}
$x='canary';$y=$x+$x|Out-Null
Write-Host "Hello, World!"
...
-frag, -minfrag, -maxfrag)Affecte seulement niveau 5 (string fragments + runtime join).
Profils :
profile=tight → petits chunks (≈6–10)profile=medium → chunks moyens (≈10–18)profile=loose → plus gros chunks (≈14–28)Ou ajuster avec -minfrag / -maxfrag.
Commandes```bash
psobf -i sample.ps1 -o out.ps1 -level 5 -frag profile=loose -seed 9
psobf -i sample.ps1 -o out.ps1 -level 5 -minfrag 8 -maxfrag 16 -seed 9
**Sortie (extrait)**```powershell
$fragments=@('Write-Host "Hello,',' World!"',"`n", '$answer = 42',"`n",'function Greet($','name){ Write-Host ("Hi, "+$name)}',"`n",'Greet "Ada"');
$script=$fragments -join ''; Invoke-Expression $script
hexenc)Encode les chaînes littérales en hexadécimal.```bash psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "hexenc" -seed 42
**Sortie (extrait):**```powershell
[Text.Encoding]:https://raw.githubusercontent.com/taurusomar/psobf/HEAD/:UTF8.GetString(%5BConvert%5D::FromHexString(%2748656C6C6F%27))
alias)Remplace les cmdlets PowerShell par leurs alias courts.```bash psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "alias" -seed 42
**Sortie (extrait):**```powershell
# Write-Host → echo/write/outright
# Get-ChildItem → dir/ls/gci
# ForEach-Object → foreach/%
unicode)Convertit les caractères de chaîne au format [char]0xNNNN.```bash
psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "unicode" -seed 42
**Sortie (extrait):**```powershell
"H[char]0x0065[char]0x006C[char]0x006Co"
antidebug)Injecte des extraits de détection de sandbox/VM/débogueur.```bash psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "antidebug" -seed 42
**Sortie (extrait) :**```powershell
if($env:COMPUTERNAME -match '^(SANDBOX|MALWARE|VIRUS)'){ exit }
if((Get-WmiObject Win32_ComputerSystem).Model -match '^(VirtualBox|VMware)'){ exit }
# ... original script ...
iexobf)Remplace Invoke-Expression par des formes alternatives.```bash
psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "iexobf" -seed 42
**Sortie (extrait) :**```powershell
# Invoke-Expression → IEX or .
. $code # instead of Invoke-Expression $code
Les préréglages sont des points de départ pratiques. Tout indicateur explicite que vous passez remplace le préréglage. Tout indicateur que vous passez explicitement prévaut sur le profil.
-seed N → sortie déterministe pour une configuration donnée.-seed → aléatoire ensemencé cryptographiquement.-fuzz N → produire N variantes (out.ps1.v1.ps1, out.ps1.v2.ps1, …), idéal pour les tests de diversité.Exemple```bash psobf -i sample.ps1 -o out.ps1 -level 4 -profile heavy -fuzz 3
---
## STDIN/STDOUT et `-noexec`
* **Pipe in / out** ```bash
cat sample.ps1 | psobf -stdin -stdout -level 2 > out.ps1
L'objectif est de diversifier les artefacts et de réduire les signatures stables pour la recherche dans des environnements autorisés.
2. **Diversité maximale (format + fragmentation + code mort)**```bash
psobf -i sample.ps1 -o out.ps1 -level 5 \
-pipeline "fmt,frag,dead" -fmt jitter -frag profile=loose -deadcode 15 \
-fuzz 5
4. **Réduire les IOCs statiques (nombres + dictionnaire)**```bash
psobf -i sample.ps1 -o out.ps1 -level 2 -pipeline "numenc,stringdict" -numenc -stringdict 35 -seed 9
---
## Bonnes pratiques et notes défensives
* Changez **`-strkey`** et **`-seed`** à chaque build.
* Privilégiez la combinaison de couches : `-strenc` + `-stringdict` + `-fmt jitter` + fragmentation.
* Utilisez `-fuzz` pour générer des familles de variantes pour les tests de détection.
* Conservez une base de référence propre et bénigne et vérifiez l'équivalence fonctionnelle sous sandbox avant et après les transformations.
* Si votre script repose sur une syntaxe PS délicate (par exemple, les redirections), gardez-les entre guillemets ou désactivez `-numenc`.
---
## Schéma d'architecture```
┌──────────────┐
│ input.ps1 │
└──────┬───────┘
│ read (-i / -stdin)
▼
┌──────────────┐
│ Pipeline │ order you choose
│ iden │ rename vars/funcs
│ strenc │ XOR/RC4 literals
│ stringdict │ tokenize + rejoin
│ numenc │ numeric masking
│ fmt │ whitespace jitter
│ cf │ opaque/shuffle
│ dead │ harmless noise
└──────┬───────┘
│ mutated script
▼
┌──────────────┐
│ Level 1..5 │ final packing
└──────┬───────┘
│ + Invoke-Expression (unless -noexec)
▼
┌──────────────┐
│ out.ps1 │
└──────────────┘
psobf -i sample.ps1 -o out.ps1 -level 2 -seed 123
psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "strenc" -strenc rc4 -strkey 0011223344556677
psobf -i sample.ps1 -o out.ps1 -level 3 -pipeline "stringdict,numenc" -stringdict 40 -numenc
psobf -i sample.ps1 -o out.ps1 -level 5
-pipeline "iden,strenc,stringdict,numenc,fmt,cf,dead,frag"
-iden obf -strenc xor -strkey a1b2c3d4 -stringdict 35 -numenc
-fmt jitter -cf-opaque -deadcode 15 -frag profile=medium -seed 777
psobf -i sample.ps1 -o out.ps1 -level 6 -profile heavy -seed 999
psobf -i sample.ps1 -o out.ps1 -level 4 -pipeline "antidebug,alias,hexenc" -seed 42
psobf -i sample.ps1 -o out.ps1 -level 6
-pipeline "iden,alias,hexenc,unicode,antidebug,iexobf,strenc"
-iden obf -strenc rc4 -strkey 0011223344556677 -seed 42
psobf -i sample.ps1 -o payload.txt -level 4 -noexec
---
## Légal
Ce projet est destiné uniquement à des tests **éducatifs** et **autorisés**. Vous êtes seul responsable de votre utilisation. Les auteurs et contributeurs déclinent toute responsabilité pour les dommages directs ou indirects.