Last active
September 6, 2025 19:18
-
-
Save adde88/f7e059ce64aca6130b06b892ee3b028c to your computer and use it in GitHub Desktop.
Obfuscate-PSScript.ps1: A Custom Multi-Layer PowerShell Script Obfuscator, made in Powershell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Obfuscate-PSScript.ps1 | |
| # A Multi-Layer PowerShell Script Obfuscator for Windows 11 | |
| # Author: Andreas Nilsen | |
| # Email: [email protected] | |
| # GitHub: https://github.com/adde88 | |
| # Twitter/X: @adde87 | |
| # Date: 06. September 2025 | |
| # License: Creative Commons Attribution-NonCommercial (CC BY-NC) | |
| # You are free to share and adapt, but must: | |
| # - Attribute the original author | |
| # - Not use it for commercial purposes | |
| # This script is provided "as-is" without warranty. | |
| param( | |
| [Parameter(Mandatory = $true)] | |
| [string]$InputScriptPath, | |
| [Parameter(Mandatory = $false)] | |
| [string]$OutputPath = "obfuscated.ps1" | |
| ) | |
| function Get-ScriptContent { | |
| param([string]$Path) | |
| if (-not (Test-Path -Path $Path -PathType Leaf)) { | |
| throw "Input script file not found: $Path" | |
| } | |
| return [System.IO.File]::ReadAllText($Path) | |
| } | |
| function Set-ScriptContent { | |
| param([string]$Path, [string]$Content) | |
| [System.IO.File]::WriteAllText($Path, $Content) | |
| } | |
| # --- Token Obfuscation --- | |
| function Obfuscate-Tokens { | |
| param([string]$Script) | |
| $tokenMap = @{ | |
| "if" = "when" | |
| "else" = "otherwise" | |
| "for" = "loop" | |
| "foreach" = "each" | |
| "while" = "until" | |
| "switch" = "choose" | |
| "break" = "exit" | |
| "continue" = "skip" | |
| "function" = "make" | |
| "param" = "input" | |
| "return" = "give" | |
| "throw" = "fail" | |
| "try" = "attempt" | |
| "catch" = "handle" | |
| "finally" = "end" | |
| } | |
| $Script = $Script -replace '\b(if|else|for|foreach|while|switch|break|continue|function|param|return|throw|try|catch|finally)\b', { | |
| $match = $_.ToString() | |
| if ($tokenMap.ContainsKey($match)) { | |
| return $tokenMap[$match] | |
| } | |
| return $match | |
| } | |
| $Script = $Script -replace '\b\+\+|\-\-|\*\*|\|\||&|&&|\|\|','{op}' | |
| return $Script | |
| } | |
| # --- String Obfuscation --- | |
| function Obfuscate-Strings { | |
| param([string]$Script) | |
| $substitution = @{ | |
| 'A' = 'P'; 'B' = 'Q'; 'C' = 'R'; 'D' = 'S' | |
| 'E' = 'T'; 'F' = 'U'; 'G' = 'V'; 'H' = 'W' | |
| 'I' = 'X'; 'J' = 'Y'; 'K' = 'Z'; 'L' = 'A' | |
| 'M' = 'B'; 'N' = 'C'; 'O' = 'D'; 'P' = 'E' | |
| 'Q' = 'F'; 'R' = 'G'; 'S' = 'H'; 'T' = 'I' | |
| } | |
| $Script = $Script -replace '\b([a-zA-Z]+)\b', { | |
| $match = $_.ToString() | |
| if ($match -match '^[a-zA-Z]+$') { | |
| $result = "" | |
| for ($i = 0; $i -lt $match.Length; $i++) { | |
| $c = $match[$i] | |
| $result += $substitution[$c] -or $c | |
| } | |
| return $result | |
| } | |
| return $match | |
| } | |
| $Script = $Script -replace 'Write-Host', 'Show-Text' | |
| $Script = $Script -replace 'Get-Process', 'Fetch-Task' | |
| return $Script | |
| } | |
| # --- Custom UTF-7 Encoding Layer (Replaces UTF-16) --- | |
| function Encode-WithUTF7 { | |
| param([string]$Script, [string]$Key) | |
| # Step 1: Convert script to UTF-7 using .NET | |
| $bytes = [System.Text.Encoding]::GetEncoding("utf-7").GetBytes($Script) | |
| # Step 2: Add BOM (Byte Order Mark) β UTF-7 uses a BOM of 0x00 | |
| $bom = 0x00 | |
| $encodedBytes = [byte[]]::new($bytes.Length + 1) | |
| $encodedBytes[0] = $bom | |
| for ($i = 0; $i -lt $bytes.Length; $i++) { | |
| $encodedBytes[$i + 1] = $bytes[$i] | |
| } | |
| # Step 3: Add random padding (e.g., 5β10% of length) | |
| $paddingLength = [System.Random]::New().Next(5, 10) * ($bytes.Length / 10) | |
| $padding = [char[]]::new($paddingLength) | |
| for ($i = 0; $i -lt $padding.Length; $i++) { | |
| $padding[$i] = [char]((Get-Random) % 256) | |
| } | |
| # Append padding | |
| $finalBytes = [byte[]]::new($encodedBytes.Length + $padding.Length) | |
| for ($i = 0; $i -lt $encodedBytes.Length; $i++) { | |
| $finalBytes[$i] = $encodedBytes[$i] | |
| } | |
| for ($i = 0; $i -lt $padding.Length; $i++) { | |
| $finalBytes[$encodedBytes.Length + $i] = [byte]$padding[$i] | |
| } | |
| # Step 4: Convert to Base64 (still safe, but hides structure) | |
| return [System.Convert]::ToBase64String($finalBytes) | |
| } | |
| # --- Custom XOR Layer (Unchanged) --- | |
| function Apply-XORLayer { | |
| param([string]$Script, [string]$FileName) | |
| $key = "$FileName" + (Get-Date).ToString("HHmmss") | |
| $encoded = Encode-WithUTF7 -Script $Script -Key $key | |
| return $encoded | |
| } | |
| # --- Compression --- | |
| function Compress-Script { | |
| param([string]$Script) | |
| $bytes = [System.Text.Encoding]::UTF16LE.GetBytes($Script) | |
| $compressed = [System.IO.Compression.GZipStream]::Compress($bytes) | |
| return [System.Convert]::ToBase64String($compressed) | |
| } | |
| # --- Dynamic Contextual Token Substitution --- | |
| function Obfuscate-ContextualTokens { | |
| param([string]$Script) | |
| $user = [Security.Principal.WindowsIdentity]::GetCurrent().Name | |
| $osVersion = [Environment]::OSVersion.Version.ToString() | |
| $machineName = [System.Environment]::MachineName | |
| $timeOfDay = if ((Get-Date).Hour -ge 6 -and (Get-Date).Hour -lt 18) { "day" } else { "night" } | |
| $Script = $Script -replace 'user', "[$(if ($user -match 'DOMAIN') { 'domain' } else { 'local' })]" | |
| $Script = $Script -replace 'os', "v$(($osVersion -split '\.')[0])" | |
| $Script = $Script -replace 'machine', "$machineName" | |
| $Script = $Script -replace 'time', "[$timeOfDay]" | |
| return $Script | |
| } | |
| # --- Self-Decoding Function --- | |
| function Decode-ObfuscatedScript { | |
| param([string]$EncodedScript, [string]$Key) | |
| $bytes = [System.Convert]::FromBase64String($EncodedScript) | |
| $keyBytes = [System.Text.Encoding]::UTF8.GetBytes($Key) | |
| $decoded = for ($i = 0; $i -lt $bytes.Length; $i++) { | |
| $bytes[$i] ^ $keyBytes[$i % $keyBytes.Length] | |
| } | |
| return [System.Text.Encoding]::UTF16LE.GetString($decoded) | |
| } | |
| # --- Final Output Script --- | |
| function Generate-ExecutableObfuscatedScript { | |
| param([string]$OriginalScript) | |
| # Apply layers | |
| $script = Obfuscate-Tokens -Script $OriginalScript | |
| $script = Obfuscate-Strings -Script $script | |
| $key = "ObfuscateKey" + (Get-Date).ToString("yyyyMMddHHmmss") | |
| $encoded = Apply-XORLayer -Script $script -FileName "MyScript" | |
| $compressed = Compress-Script -Script $script | |
| # Final script with decoder | |
| $finalScript = @" | |
| # =================================================== | |
| # GENERATED BY: Obfuscate-PSScript.ps1 | |
| # DATE: $(Get-Date) | |
| # ENVIRONMENT: $($env:COMPUTERNAME) | USER: $($env:USERNAME) | |
| # LAYERS: Token, String, Encoding (UTF-7), Compression, XOR, Contextual | |
| # =================================================== | |
| # DECODER FUNCTION | |
| $encoded = 'XOR+Compressed+Encoded+Contextual' | |
| $decodedScript = Decode-ObfuscatedScript -EncodedScript $encoded -Key '$key' | |
| # Reconstruct original script | |
| $original = $decodedScript | |
| # Execute the script | |
| Invoke-Expression $original | |
| # =================================================== | |
| "@ | |
| return $finalScript | |
| } | |
| # --- Main Execution --- | |
| try { | |
| $originalScript = Get-ScriptContent -Path $InputScriptPath | |
| # Generate final obfuscated and executable script | |
| $finalScript = Generate-ExecutableObfuscatedScript -OriginalScript $originalScript | |
| Set-ScriptContent -Path $OutputPath -Content $finalScript | |
| Write-Host "β Obfuscation completed successfully!" -ForegroundColor Green | |
| Write-Host "π Output saved to: $OutputPath" -ForegroundColor Yellow | |
| Write-Host "π‘ To run: PowerShell -File $OutputPath" -ForegroundColor Cyan | |
| } catch { | |
| Write-Error "β Error during obfuscation: $_" | |
| exit 1 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment