ps-parser
A PowerShell parser written in Rust. Parse, evaluate and deobfuscate PowerShell scripts with idiomatic Rust types.
Goal
Malicious scripts typically use "safe" operations to obfuscate "unsafe" ones. For example, arithmetic operations are used to obfuscate function arguments.
The goal of this parser is to combat obfuscation in PowerShell by evaluating everything that is "safe" but not anything that is "unsafe". Ps-parser deliver also possibility to get script "tokens"
Features
- PowerShell script parsing using pest grammar
- Value types for PowerShell objects (
String
,Int
,HashTable
,ScriptBlock
, etc.) - Arithmetic, logical, and string operations
- Script block evaluation and variable management
- HashTable and Array support
- Extensible for custom PowerShell types
Installation
Add this to your Cargo.toml
:
[]
= "0.1.1"
Usage
Parse a PowerShell script and eval only "safe" operations
use PowerShellSession;
let mut ps = new;
let script = r#"
$arg = 20MB*2/4
# Get-Process is not "safe" to evaluate, so Where-Object is also not evaluated
Get-Process | Where-Object WorkingSet -GT $arg
$evenNumbers = 1..10 | Where-Object { $_ % 2 -eq 0 } # Where-Object is evaluated, because 1..10 is "safe"
"#;
let result = ps.parse_input?.deobfuscated;
println!;
Output:
$arg = 10485760
Get-Process | Where-Object WorkingSet -GT $arg
$evennumbers = @(2,4,6,8,10)
Evaluate arithmetic and variables
use ;
let mut ps = new;
let script = r#"
$x = 5; $y = $x * 8/2; $y%=3;$y
"#;
let result = ps.parse_input?.deobfuscated;
println!;
Output:
$x = 5
$y = 20
$y = 2
Work with arrays and hashtables
use PowerShellSession;
let mut ps = new;
let script = r#"
$a = @('a', 'b', 'c');$b=$a[2];$b
"#;
let script_result = ps.parse_input?;
println!;
println!;
Output:
Deobfuscated:
$a = @('a','b','c')
$b = 'c'
Result:
c
Deal with simple deobfuscation
use ps_parser::PowerShellSession;
let mut ps = PowerShellSession::new();
let script = r#"
$ilryNQSTt="System.$([cHAR]([ByTE]0x4d)+[ChAR]([byte]0x61)+[chAr](110)+[cHar]([byTE]0x61)+[cHaR](103)+[cHar](101*64/64)+[chaR]([byTE]0x6d)+[cHAr](101)+[CHAr]([byTE]0x6e)+[Char](116*103/103)).$([Char]([ByTe]0x41)+[Char](117+70-70)+[CHAr]([ByTE]0x74)+[CHar]([bYte]0x6f)+[CHar]([bytE]0x6d)+[ChaR]([ByTe]0x61)+[CHar]([bYte]0x74)+[CHAR]([byte]0x69)+[Char](111*26/26)+[chAr]([BYTe]0x6e)).$(('Ârmí'+'Ùtìl'+'s').NORmalizE([ChAR](44+26)+[chAR](111*9/9)+[cHar](82+32)+[ChaR](109*34/34)+[cHaR](68+24-24)) -replace [ChAr](92)+[CHaR]([BYTe]0x70)+[Char]([BytE]0x7b)+[CHaR]([BYTe]0x4d)+[chAR](110)+[ChAr](15+110))";$ilryNQSTt
"#;
let script_result = ps.parse_input(script)?;
println!("{}", script_result.deobfuscated());
Output:
$ilrynqstt = 'System.Management.Automation.ArmiUtils'
Deal with encoding deobfuscation
use PowerShellSession;
let mut ps = new;
let script_printed_to_output = r#"
[syStem.texT.EncoDInG]::unIcoDe.geTstRiNg([SYSTem.cOnVERT]::froMbasE64striNg("ZABlAGMAbwBkAGUAZAA="))
"#;
let script_result = ps.parse_input?;
println!;
let script_assigned_to_variable = r#"
$encoded = [syStem.texT.EncoDInG]::unIcoDe.geTstRiNg([SYSTem.cOnVERT]::froMbasE64striNg("ZABlAGMAbwBkAGUAZAA="));
"#;
let script_result = ps.parse_input?;
println!;
Output:
Output:
decoded
Deobfuscated:
$encoded = 'decoded'
Work environmental variables
use PowerShellSession;
let mut ps = new.with_variables;
let input = r#"$env:programfiles"#;
let script_result = ps.parse_input?;
println!;
Output:
C:\Program Files
Check errors
use PowerShellSession;
let mut ps = new;
let input = r#"
$var = 1 + "Hello, World!" # Powershell cannot cast string to int
"#;
let script_result = ps.parse_input?;
println!;
Output:
Get tokens
use PowerShellSession;
let mut ps = new;
let input = r#"
$a = 5
$b = $a * 2
Write-Output "Addition: $($a + $b)"
"#;
let script_result = ps.parse_input?;
println!;
println!;
Output:
StringExpandable
Expression
Future plans
- add "scopes" to evaluation
- eval "if" and "switch" statements
- eval "function" and "script_block" statements
- parse "enum" and "class" statements
Documentation
License
Licensed under MIT or Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE for details.