├── Demo ├── demo.evtx ├── DBOdemo1.ps1 └── DBOdemo2.ps1 ├── Whitelist ├── Scripts_To_Whitelist │ ├── D12C22D4523D9B0891F6F6AAB28B0B9CB31484C7209F4F7777424616E3FAAC6D.ps1 │ └── 68DE420A4E77FCC92F46920D3097E5277DDABFB88D9438D014AEEB62735BED79.ps1 ├── Regex_To_Whitelist.txt └── Strings_To_Whitelist.txt ├── Requirements ├── CommandLine │ ├── ConvertToPowerShellExpression.exe │ ├── Convert-PowerShellCommandLine.ps1 │ └── ConvertToPowerShellExpression.cs └── RevokeObfuscationHelpers.cs ├── Checks ├── AST_Member_Character_Distribution.cs ├── AST_Array_Elements_Character_Distribution.cs ├── AST_Member_Argument_Character_Distribution.cs ├── AST_Invocation_Operator_Invoked_Object_Character_Distribution.cs ├── AST_Variable_Name_Character_Distribution.cs ├── AST_Command_Parameter_Name_Character_Distribution.cs ├── AST_Integer_And_Double_Character_Distribution.cs ├── AST_Assignment_Statement_Operators.cs ├── AST_Type_Constraint_Character_Distribution.cs ├── AST_Function_Name_Character_Distribution.cs ├── AST_Type_Expression_Character_Distribution.cs ├── AST_String_Character_Distribution.cs ├── AST_Convert_Expression_Character_Distribution.cs ├── AST_Array_Element_Count_Ranges.cs ├── AST_Line_By_Line_Character_Distribution.cs ├── AST_Cmdlet_Character_Distribution.cs ├── AST_Unary_Expression_Operators.cs ├── AST_Comment_Character_Distribution.cs ├── AST_Binary_Expression_Operators.cs └── AST_Ast_Types.cs ├── format.ps1xml ├── DataScience ├── Start-LabelSession.ps1 ├── README.md ├── Invoke-TrainingProcess.ps1 ├── InvokeCradleCrafter-obfuscation-labeledData.csv ├── ModelTrainer │ └── ModelTrainer.cs └── UnderhandedPowerShell-obfuscation-labeledData.csv ├── Revoke-Obfuscation.psd1 ├── README.md └── LICENSE /Demo/demo.evtx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbohannon/Revoke-Obfuscation/HEAD/Demo/demo.evtx -------------------------------------------------------------------------------- /Demo/DBOdemo1.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbohannon/Revoke-Obfuscation/HEAD/Demo/DBOdemo1.ps1 -------------------------------------------------------------------------------- /Demo/DBOdemo2.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbohannon/Revoke-Obfuscation/HEAD/Demo/DBOdemo2.ps1 -------------------------------------------------------------------------------- /Whitelist/Scripts_To_Whitelist/D12C22D4523D9B0891F6F6AAB28B0B9CB31484C7209F4F7777424616E3FAAC6D.ps1: -------------------------------------------------------------------------------- 1 | { $_.Length -ne 0 } -------------------------------------------------------------------------------- /Whitelist/Scripts_To_Whitelist/68DE420A4E77FCC92F46920D3097E5277DDABFB88D9438D014AEEB62735BED79.ps1: -------------------------------------------------------------------------------- 1 | { $_.GetMethod("AnalyzeAst") } -------------------------------------------------------------------------------- /Requirements/CommandLine/ConvertToPowerShellExpression.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielbohannon/Revoke-Obfuscation/HEAD/Requirements/CommandLine/ConvertToPowerShellExpression.exe -------------------------------------------------------------------------------- /Whitelist/Regex_To_Whitelist.txt: -------------------------------------------------------------------------------- 1 | #Tag Name/Reason,Single Line of Regex To Whitelist [BE CAREFUL WHAT YOU WHITELIST HERE! PREFERABLY WHITELIST ENTIRE COMMAND/SCRIPT BY HASH BY PLACING IT IN Scripts_To_Whitelist DIRECTORY] 2 | -------------------------------------------------------------------------------- /Whitelist/Strings_To_Whitelist.txt: -------------------------------------------------------------------------------- 1 | #Tag Name/Reason,Single Line of Content (as a string) To Whitelist [BE CAREFUL WHAT YOU WHITELIST HERE! PREFERABLY WHITELIST ENTIRE COMMAND/SCRIPT BY HASH BY PLACING IT IN Scripts_To_Whitelist DIRECTORY] 2 | -------------------------------------------------------------------------------- /Requirements/CommandLine/Convert-PowerShellCommandLine.ps1: -------------------------------------------------------------------------------- 1 | param($OriginalInput) 2 | 3 | $startInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{ RedirectStandardOutput = $true; FileName = "$PSScriptRoot\ConvertToPowerShellExpression.exe"; Arguments = $OriginalInput; UseShellExecute = $false } 4 | $process = [System.Diagnostics.Process]::Start($startInfo) 5 | $process.StandardOutput.ReadToEnd().Trim() -------------------------------------------------------------------------------- /Checks/AST_Member_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class MemberMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(MemberExpressionAst targetAst in ast.FindAll( testAst => testAst is MemberExpressionAst, true )) 15 | { 16 | // Extract the AST object value. 17 | stringList.Add(targetAst.Member.Extent.Text); 18 | } 19 | 20 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 21 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstMemberMetrics"); 22 | } 23 | } -------------------------------------------------------------------------------- /Checks/AST_Array_Elements_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class ArrayElementMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(Ast targetAst in ast.FindAll( testAst => testAst is ArrayLiteralAst, true )) 15 | { 16 | // Extract the AST object value. 17 | String targetName = targetAst.Extent.Text; 18 | 19 | stringList.Add(targetName); 20 | } 21 | 22 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 23 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstArrayElementMetrics"); 24 | } 25 | } -------------------------------------------------------------------------------- /Checks/AST_Member_Argument_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class MemberArgumentMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(InvokeMemberExpressionAst targetAst in ast.FindAll( testAst => testAst is InvokeMemberExpressionAst, true )) 15 | { 16 | if(targetAst.Arguments != null) 17 | { 18 | // Extract the AST object value. 19 | stringList.Add(String.Join(",", targetAst.Arguments)); 20 | } 21 | } 22 | 23 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 24 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstMemberArgumentMetrics"); 25 | } 26 | } -------------------------------------------------------------------------------- /Checks/AST_Invocation_Operator_Invoked_Object_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class InvocationOperatorInvokedObjectMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(CommandAst targetAst in ast.FindAll( testAst => testAst is CommandAst, true )) 15 | { 16 | // Extract the AST object value. 17 | if(targetAst.InvocationOperator.ToString() != "Unknown") 18 | { 19 | stringList.Add(targetAst.CommandElements[0].ToString()); 20 | } 21 | } 22 | 23 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 24 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstInvocationOperatorInvokedObjectMetrics"); 25 | } 26 | } -------------------------------------------------------------------------------- /Checks/AST_Variable_Name_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class VariableNameMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(Ast targetAst in ast.FindAll( testAst => testAst is VariableExpressionAst, true )) 15 | { 16 | // Extract the AST object value. 17 | String targetName = targetAst.Extent.Text; 18 | 19 | // Trim off the single leading "$" in the variable name. 20 | if(targetName.Length > 0) 21 | { 22 | stringList.Add(targetName.Substring(1,targetName.Length-1)); 23 | } 24 | } 25 | 26 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 27 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstVariableNameMetrics"); 28 | } 29 | } -------------------------------------------------------------------------------- /format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RevokeObfuscationResult 5 | 6 | RevokeObfuscation.Result 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Hash 19 | 20 | 21 | Obfuscated 22 | 23 | 24 | Source 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /DataScience/Start-LabelSession.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [ScriptBlock] $ScriptBlock 3 | ) 4 | 5 | $reviewed = [Ordered] @{} 6 | if(Test-Path labeledData.csv) 7 | { 8 | Import-Csv labeledData.csv -Header Path,Label | % { $reviewed[$_.Path] = $_.Label } 9 | } 10 | 11 | Write-Progress "Preparing for dataset review of $pwd" 12 | $allFiles = dir *.ps1,*.psm1 -rec | ? { -not $reviewed.Contains( (Resolve-Path $_.FullName -Relative).Substring(2) ) } 13 | 14 | $processed = $reviewed.Count 15 | $total = $allFiles.Count + $reviewed.Count 16 | 17 | $allFiles | % { 18 | Write-Progress "Processing $processed of $total" -PercentComplete ($processed / $total * 100) 19 | 20 | if($ScriptBlock) 21 | { 22 | $label = & $ScriptBlock $_ 23 | } 24 | else 25 | { 26 | notepad $_.FullName | Out-Null 27 | $label = Read-Host "$(Resolve-Path -Relative $_.FullName)`r`nEnter Label. Any input (followed by ENTER) = Obfuscated. ENTER alone = Clean" 28 | } 29 | 30 | if($label -ne '?') 31 | { 32 | if($label) 33 | { 34 | $label = "1" 35 | } 36 | else 37 | { 38 | $label = "0" 39 | } 40 | 41 | $newPath = (Resolve-Path $_.FullName -Relative).Substring(2) 42 | ([PSCustomObject] @{ Path = $newPath; Label = $label }) | Export-Csv labeledData.csv -Append -NoTypeInformation 43 | } 44 | 45 | $processed++ 46 | } -------------------------------------------------------------------------------- /Checks/AST_Command_Parameter_Name_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class CommandParameterNameMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(CommandParameterAst targetAst in ast.FindAll( testAst => testAst is CommandParameterAst, true )) 15 | { 16 | // Extract the AST object value. 17 | string targetName = targetAst.Extent.Text; 18 | 19 | // Trim off the single leading dash of the Command Parameter value. 20 | if(targetName.Length > 1) 21 | { 22 | stringList.Add(targetName.Substring(1,targetName.Length-1)); 23 | } 24 | else 25 | { 26 | stringList.Add(targetName); 27 | } 28 | } 29 | 30 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 31 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstCommandParameterNameMetrics"); 32 | } 33 | } -------------------------------------------------------------------------------- /Checks/AST_Integer_And_Double_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class IntegerAndDoubleMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(ConstantExpressionAst targetAst in ast.FindAll( testAst => testAst is ConstantExpressionAst, true )) 15 | { 16 | // Extract the AST object value. 17 | // If StaticType name starts with "Int" or "Double" then extract the value for metrics. 18 | // We use .Extent.Text for value since it preserves non-numerical representations (e.g., 0x001F0FFF --> 2035711, 0x52 --> 82). 19 | if((targetAst.StaticType.Name.Substring(0,3) == "Int") || (targetAst.StaticType.Name.Substring(0,6) == "Double")) 20 | { 21 | stringList.Add(targetAst.Extent.Text); 22 | } 23 | } 24 | 25 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 26 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstIntegerAndDoubleMetrics"); 27 | } 28 | } -------------------------------------------------------------------------------- /Checks/AST_Assignment_Statement_Operators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class GroupedAssignmentStatements 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Initialize Dictionary with common array counts initialized to 0. 12 | Dictionary astGroupedAssignmentStatementsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 13 | 14 | astGroupedAssignmentStatementsDictionary["Equals"] = 0; 15 | astGroupedAssignmentStatementsDictionary["PlusEquals"] = 0; 16 | astGroupedAssignmentStatementsDictionary["MinusEquals"] = 0; 17 | astGroupedAssignmentStatementsDictionary["MultiplyEquals"] = 0; 18 | astGroupedAssignmentStatementsDictionary["DivideEquals"] = 0; 19 | astGroupedAssignmentStatementsDictionary["RemainderEquals"] = 0; 20 | astGroupedAssignmentStatementsDictionary["UNKNOWN"] = 0; 21 | 22 | // Return all targeted AST objects by Count and Percent across the entire input AST object. 23 | return RevokeObfuscationHelpers.AstValueGrouper(ast, typeof(AssignmentStatementAst), astGroupedAssignmentStatementsDictionary, "AstGroupedAssignmentStatements", 24 | targetAst => { return ((AssignmentStatementAst) targetAst).Operator.ToString(); } ); 25 | } 26 | } -------------------------------------------------------------------------------- /Checks/AST_Type_Constraint_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class TypeConstraintMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(TypeConstraintAst targetAst in ast.FindAll( testAst => testAst is TypeConstraintAst, true )) 15 | { 16 | // Extract the AST object value. 17 | string targetName = targetAst.Extent.Text; 18 | 19 | // Trim off the single leading and trailing square brackets of the Convert Expression type value. 20 | // Avoid using Trim so that square brackets will remain in select situations (e.g. [Char[]] --> Char[]). 21 | if(targetName.Length > 2) 22 | { 23 | stringList.Add(targetName.Substring(1,targetName.Length-2)); 24 | } 25 | else 26 | { 27 | stringList.Add(targetName); 28 | } 29 | } 30 | 31 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 32 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstTypeConstraintMetrics"); 33 | } 34 | } -------------------------------------------------------------------------------- /Checks/AST_Function_Name_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class FunctionNameMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | // Create required variables for tokenization. 15 | Token[] tokens = new Token[]{}; 16 | ParseError[] parseErrors = new ParseError[]{}; 17 | 18 | // Tokenize the entire input script. We must tokenize (instead of AST) to retrieve function values with obfuscation (like ticks) intact. 19 | Parser.ParseInput( ast.Extent.Text, out tokens, out parseErrors ); 20 | 21 | // Iterate each token returned from above tokenization. 22 | foreach(Token token in tokens) 23 | { 24 | // If token is a function name then add to stringList. 25 | if( (token.Kind.ToString() == "Generic") && (token.TokenFlags.ToString() == "None") ) 26 | { 27 | stringList.Add(token.Text); 28 | } 29 | } 30 | 31 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 32 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstFunctionNameMetrics"); 33 | } 34 | } -------------------------------------------------------------------------------- /Checks/AST_Type_Expression_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class TypeExpressionMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(TypeExpressionAst targetAst in ast.FindAll( testAst => testAst is TypeExpressionAst, true )) 15 | { 16 | // Extract the AST object value. 17 | string targetName = targetAst.Extent.Text; 18 | 19 | // Trim off the single leading and trailing square brackets of the Convert Expression type value. 20 | // Avoid using Trim so that square brackets will remain in select situations (e.g. [Char[]] --> Char[]). 21 | if(targetName.Length > 2) 22 | { 23 | stringList.Add(targetName.Substring(1,targetName.Length-2)); 24 | } 25 | else 26 | { 27 | stringList.Add(targetName); 28 | } 29 | } 30 | 31 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 32 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstTypeExpressionMetrics"); 33 | } 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Checks/AST_String_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class StringMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(StringConstantExpressionAst targetAst in ast.FindAll( testAst => testAst is StringConstantExpressionAst, true )) 15 | { 16 | if(targetAst.StringConstantType.ToString() != "BareWord") 17 | { 18 | // Extract the AST object value. 19 | String targetName = targetAst.Extent.Text.Replace("\n",""); 20 | 21 | // Trim off the leading and trailing single- or double-quote for the current string. 22 | if(targetName.Length > 2) 23 | { 24 | stringList.Add(targetName.Substring(1,targetName.Length-2)); 25 | } 26 | else 27 | { 28 | stringList.Add(targetName); 29 | } 30 | } 31 | } 32 | 33 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 34 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstStringMetrics"); 35 | } 36 | } -------------------------------------------------------------------------------- /Checks/AST_Convert_Expression_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class ConvertExpressionMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(ConvertExpressionAst targetAst in ast.FindAll( testAst => testAst is ConvertExpressionAst, true )) 15 | { 16 | // Extract the AST object value. 17 | string targetName = targetAst.Type.Extent.Text; 18 | 19 | // Trim off the single leading and trailing square brackets of the Convert Expression type value. 20 | // Avoid using Trim so that square brackets will remain in select situations (e.g. [Char[]] --> Char[]). 21 | if(targetName.Length > 2) 22 | { 23 | stringList.Add(targetName.Substring(1,targetName.Length-2)); 24 | } 25 | else 26 | { 27 | stringList.Add(targetName); 28 | } 29 | } 30 | 31 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 32 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstConvertExpressionMetrics"); 33 | } 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Checks/AST_Array_Element_Count_Ranges.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class GroupedArrayElementRangeCounts 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Initialize Dictionary with common array count ranges initialized to 0. 12 | Dictionary astArrayElementCountsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 13 | 14 | astArrayElementCountsDictionary["0-10"] = 0; 15 | astArrayElementCountsDictionary["10-20"] = 0; 16 | astArrayElementCountsDictionary["20-30"] = 0; 17 | astArrayElementCountsDictionary["30-40"] = 0; 18 | astArrayElementCountsDictionary["40-50"] = 0; 19 | astArrayElementCountsDictionary["50-60"] = 0; 20 | astArrayElementCountsDictionary["60-70"] = 0; 21 | astArrayElementCountsDictionary["70-80"] = 0; 22 | astArrayElementCountsDictionary["80-90"] = 0; 23 | astArrayElementCountsDictionary["90-100"] = 0; 24 | astArrayElementCountsDictionary["UNKNOWN"] = 0; 25 | 26 | // Return all targeted AST objects by Count Ranges and Percent across the entire input AST object. 27 | return RevokeObfuscationHelpers.AstValueGrouper(ast, typeof(ArrayLiteralAst), astArrayElementCountsDictionary, "AstGroupedArrayElementRangeCounts", 28 | targetAst => { return ( ((int)((((ArrayLiteralAst) targetAst).Elements.Count / 10) * 10)).ToString() + "-" + (((int)((((ArrayLiteralAst) targetAst).Elements.Count / 10) * 10) + 10)).ToString() ); } ); 29 | } 30 | } -------------------------------------------------------------------------------- /Checks/AST_Line_By_Line_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Text.RegularExpressions; 7 | 8 | public class LineByLineMetrics 9 | { 10 | public static IDictionary AnalyzeAst(Ast ast) 11 | { 12 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 13 | List stringList = new List(); 14 | 15 | // Set entire script into a variable so we can perform regex (if necessary) to remove code signature block. 16 | string scriptContent = ast.Extent.Text; 17 | 18 | // Only perform regex removal of signature blocks if the signature block header and tail syntax exist. 19 | if( scriptContent.Contains("# SIG # Begin signature block") && scriptContent.Contains("# SIG # End signature block") ) 20 | { 21 | string pattern = "(?s)# SIG # Begin signature block.*?# SIG # End signature block\\s*$"; 22 | string replacement = ""; 23 | Regex regExp = new Regex( pattern ); 24 | scriptContent = regExp.Replace( scriptContent, replacement ); 25 | } 26 | 27 | // Add each line of the input script to stringList. 28 | foreach(String curLine in scriptContent.Split(new String[]{Environment.NewLine},StringSplitOptions.None)) 29 | { 30 | stringList.Add(curLine); 31 | } 32 | 33 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 34 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "LineByLineMetrics"); 35 | } 36 | } -------------------------------------------------------------------------------- /Checks/AST_Cmdlet_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class CmdletMetrics 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 12 | List stringList = new List(); 13 | 14 | foreach(CommandAst targetAst in ast.FindAll( testAst => testAst is CommandAst, true )) 15 | { 16 | // Extract the AST object value. 17 | // If InvocationOperator is "Unknown" then the cmdlet will be the first object in CommandElements (i.e. most likely a cmdlet like Invoke-Expression, Write-Output, etc.). 18 | // Otherwise it will be the name of the invocation operator. 19 | string cmdlet = null; 20 | if(targetAst.InvocationOperator.ToString() == "Unknown") 21 | { 22 | cmdlet = targetAst.CommandElements[0].Extent.Text; 23 | } 24 | else 25 | { 26 | // Convert InvocationOperator name to the operator value (using ? for UNKNOWN). 27 | switch(targetAst.InvocationOperator.ToString()) 28 | { 29 | case "Dot" : cmdlet = "."; break; 30 | case "Ampersand" : cmdlet = "&"; break; 31 | default : cmdlet = "?"; break; 32 | } 33 | } 34 | 35 | stringList.Add(cmdlet); 36 | } 37 | 38 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 39 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstCmdletMetrics"); 40 | } 41 | } -------------------------------------------------------------------------------- /Checks/AST_Unary_Expression_Operators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class GroupedUnaryExpressionOperators 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Initialize Dictionary with common array counts initialized to 0. 12 | Dictionary astGroupedUnaryExpressionOperatorsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 13 | 14 | astGroupedUnaryExpressionOperatorsDictionary["Exclaim"] = 0; 15 | astGroupedUnaryExpressionOperatorsDictionary["Not"] = 0; 16 | astGroupedUnaryExpressionOperatorsDictionary["Minus"] = 0; 17 | astGroupedUnaryExpressionOperatorsDictionary["Plus"] = 0; 18 | astGroupedUnaryExpressionOperatorsDictionary["Bnot"] = 0; 19 | astGroupedUnaryExpressionOperatorsDictionary["PlusPlus"] = 0; 20 | astGroupedUnaryExpressionOperatorsDictionary["MinusMinus"] = 0; 21 | astGroupedUnaryExpressionOperatorsDictionary["PostfixPlusPlus"] = 0; 22 | astGroupedUnaryExpressionOperatorsDictionary["PostfixMinusMinus"] = 0; 23 | astGroupedUnaryExpressionOperatorsDictionary["Join"] = 0; 24 | astGroupedUnaryExpressionOperatorsDictionary["Isplit"] = 0; 25 | astGroupedUnaryExpressionOperatorsDictionary["Csplit"] = 0; 26 | astGroupedUnaryExpressionOperatorsDictionary["UNKNOWN"] = 0; 27 | 28 | // Return all targeted AST objects by Count and Percent across the entire input AST object. 29 | return RevokeObfuscationHelpers.AstValueGrouper(ast, typeof(UnaryExpressionAst), astGroupedUnaryExpressionOperatorsDictionary, "AstGroupedUnaryExpressionOperators", 30 | targetAst => { return ((UnaryExpressionAst) targetAst).TokenKind.ToString(); } ); 31 | } 32 | } -------------------------------------------------------------------------------- /Checks/AST_Comment_Character_Distribution.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Text.RegularExpressions; 7 | 8 | public class CommentMetrics 9 | { 10 | public static IDictionary AnalyzeAst(Ast ast) 11 | { 12 | // Build string list of all AST object values that will be later sent to StringMetricCalculator. 13 | List stringList = new List(); 14 | 15 | // Set entire script into a variable so we can perform regex (if necessary) to remove code signature block. 16 | string scriptContent = ast.Extent.Text; 17 | 18 | // Only perform regex removal of signature blocks if the signature block header and tail syntax exist. 19 | if( scriptContent.Contains("# SIG # Begin signature block") && scriptContent.Contains("# SIG # End signature block") ) 20 | { 21 | string pattern = "(?s)# SIG # Begin signature block.*?# SIG # End signature block\\s*$"; 22 | string replacement = ""; 23 | Regex regExp = new Regex( pattern ); 24 | scriptContent = regExp.Replace( scriptContent, replacement ); 25 | } 26 | 27 | // Create required variables for tokenization. 28 | Token[] tokens = new Token[]{}; 29 | ParseError[] parseErrors = new ParseError[]{}; 30 | 31 | // Tokenize the entire input script. We must tokenize (instead of AST) to retrieve comment tokens. 32 | Parser.ParseInput( scriptContent, out tokens, out parseErrors ); 33 | 34 | // Iterate each token returned from above tokenization. 35 | foreach(Token token in tokens) 36 | { 37 | // If token is a comment then add to stringList. 38 | if( token.Kind.ToString() == "Comment" ) 39 | { 40 | stringList.Add(token.Text); 41 | } 42 | } 43 | 44 | // Return character distribution and additional string metrics across all targeted AST objects across the entire input AST object. 45 | return RevokeObfuscationHelpers.StringMetricCalculator(stringList, "AstCommentMetrics"); 46 | } 47 | } -------------------------------------------------------------------------------- /Revoke-Obfuscation.psd1: -------------------------------------------------------------------------------- 1 | # This file is part of Revoke-Obfuscation. 2 | # 3 | # Copyright 2017 Daniel Bohannon <@danielhbohannon> 4 | # while at Mandiant 5 | # and Lee Holmes <@Lee_Holmes> 6 | # while at Microsoft 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | 20 | 21 | 22 | # 23 | # Module manifest for module 'Revoke-Obfuscation' 24 | # 25 | # Generated by: Daniel Bohannon (@danielhbohannon) 26 | # 27 | # Generated on: 2017-05-23 28 | # 29 | 30 | 31 | 32 | @{ 33 | 34 | # Script module or binary module file associated with this manifest. 35 | RootModule = 'Revoke-Obfuscation.psm1' 36 | 37 | # Version number of this module. 38 | ModuleVersion = '1.1' 39 | 40 | # ID used to uniquely identify this module 41 | GUID = 'a0a9150d-a6a4-ad17-f325-a3a24fed0aa9' 42 | 43 | # Author of this module 44 | Author = 'Daniel Bohannon (@danielhbohannon)' 45 | 46 | # Copyright statement for this module 47 | Copyright = 'Apache License, Version 2.0' 48 | 49 | # Description of the functionality provided by this module 50 | Description = 'PowerShell module file for importing all required modules for the Revoke-Obfuscation framework.' 51 | 52 | # Minimum version of the Windows PowerShell engine required by this module 53 | PowerShellVersion = '3.0' 54 | 55 | # Minimum version of the Windows PowerShell host required by this module 56 | PowerShellHostVersion = '3.0' 57 | 58 | # Script files (.ps1) that are run in the caller's environment prior to importing this module 59 | #ScriptsToProcess = @('Get-ScriptBlock.ps1') 60 | ScriptsToProcess = @() 61 | 62 | # Functions to export from this module 63 | FunctionsToExport = 'Measure-RvoObfuscation','Get-RvoFeatureVector','Get-RvoScriptBlock','Update-RvoWhitelist','Revoke-Obfuscation' 64 | 65 | FormatsToProcess = 'format.ps1xml' 66 | 67 | # HelpInfo URI of this module 68 | # HelpInfoURI = '' 69 | 70 | } -------------------------------------------------------------------------------- /Requirements/CommandLine/ConvertToPowerShellExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | public class ConvertToPowerShellExpression 5 | { 6 | public static void Main(string[] args) 7 | { 8 | string encodedCommandRegex = @"^[\u2013\u2014\u2015/-]e(?![px])[a-zA-Z]*"; 9 | string commandRegex = @"^[\u2013\u2014\u2015/-]c[a-zA-Z]*"; 10 | string parameterWithArgumentRegex = @"^[\u2013\u2014\u2015/-](v|config|c|w|f|mod|o|in|if|ex|ep|e|s)"; 11 | string paramRegex = @"^[\u2013\u2014\u2015/-](v|h|\?|noe|imp|nop|nol|noni|so|s|nam|sshs|i|config|c|w|f|w|iss|mod|o|in|if|ex|ep|e|s|mta)"; 12 | 13 | bool foundCommand = false; 14 | 15 | // Emit the process name 16 | Console.Write(args[0] + " "); 17 | 18 | // Process the arguments 19 | for(int arg = 1; arg < args.Length; arg++) 20 | { 21 | if(Regex.IsMatch(args[arg], encodedCommandRegex, RegexOptions.IgnoreCase)) 22 | { 23 | // Transform EncodedCommand 24 | foundCommand = true; 25 | string encodedCommandText = args[arg + 1]; 26 | 27 | string newCommandText; 28 | try 29 | { 30 | newCommandText= System.Text.Encoding.Unicode.GetString(Convert.FromBase64String(encodedCommandText)); 31 | } 32 | catch (FormatException) 33 | { 34 | newCommandText = "''"; 35 | } 36 | 37 | Console.Write("-DecodedFromBase64Command {" + newCommandText + "}"); 38 | arg ++; 39 | } 40 | else if(Regex.IsMatch(args[arg], commandRegex, RegexOptions.IgnoreCase)) 41 | { 42 | // Emit -Command when named 43 | foundCommand = true; 44 | 45 | string originalCommand = args[arg]; 46 | string command = collectArguments(args, arg); 47 | arg = args.Length; 48 | 49 | Console.Write(originalCommand + " { " + command + " }"); 50 | } 51 | else if(Regex.IsMatch(args[arg], parameterWithArgumentRegex, RegexOptions.IgnoreCase)) 52 | { 53 | Console.Write(args[arg] + " " + args[++arg]); 54 | } 55 | else 56 | { 57 | // Emit the implicit "-Command" 58 | if( 59 | // If we're at the end and haven't found -Command 60 | ((arg == args.Length - 1) && (! foundCommand)) || 61 | 62 | // Our first parameter that does not look like a known parameter 63 | (! Regex.IsMatch(args[arg], paramRegex, RegexOptions.IgnoreCase))) 64 | { 65 | Console.Write("{ " + collectArguments(args, arg - 1) + " }"); 66 | arg = args.Length; 67 | } 68 | else 69 | { 70 | Console.Write(args[arg]); 71 | } 72 | } 73 | 74 | if(arg < args.Length - 1) 75 | { 76 | Console.Write(" "); 77 | } 78 | } 79 | } 80 | 81 | static string collectArguments(string[] args, int currentIndex) 82 | { 83 | string command = ""; 84 | 85 | do 86 | { 87 | currentIndex++; 88 | command += args[currentIndex]; 89 | 90 | if(currentIndex < args.Length - 1) 91 | { 92 | command += " "; 93 | } 94 | 95 | } while(currentIndex < args.Length - 1); 96 | 97 | return command; 98 | } 99 | } -------------------------------------------------------------------------------- /Checks/AST_Binary_Expression_Operators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class GroupedBinaryExpressionOperators 8 | { 9 | public static IDictionary AnalyzeAst(Ast ast) 10 | { 11 | // Initialize Dictionary with common array counts initialized to 0. 12 | Dictionary astGroupedBinaryExpressionOperatorsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 13 | 14 | astGroupedBinaryExpressionOperatorsDictionary["And"] = 0; 15 | astGroupedBinaryExpressionOperatorsDictionary["Or"] = 0; 16 | astGroupedBinaryExpressionOperatorsDictionary["Is"] = 0; 17 | astGroupedBinaryExpressionOperatorsDictionary["IsNot"] = 0; 18 | astGroupedBinaryExpressionOperatorsDictionary["As"] = 0; 19 | astGroupedBinaryExpressionOperatorsDictionary["DotDot"] = 0; 20 | astGroupedBinaryExpressionOperatorsDictionary["Multiply"] = 0; 21 | astGroupedBinaryExpressionOperatorsDictionary["Divide"] = 0; 22 | astGroupedBinaryExpressionOperatorsDictionary["Rem"] = 0; 23 | astGroupedBinaryExpressionOperatorsDictionary["Plus"] = 0; 24 | astGroupedBinaryExpressionOperatorsDictionary["Minus"] = 0; 25 | astGroupedBinaryExpressionOperatorsDictionary["Format"] = 0; 26 | astGroupedBinaryExpressionOperatorsDictionary["Xor"] = 0; 27 | astGroupedBinaryExpressionOperatorsDictionary["Shl"] = 0; 28 | astGroupedBinaryExpressionOperatorsDictionary["Shr"] = 0; 29 | astGroupedBinaryExpressionOperatorsDictionary["Band"] = 0; 30 | astGroupedBinaryExpressionOperatorsDictionary["Bor"] = 0; 31 | astGroupedBinaryExpressionOperatorsDictionary["Bxor"] = 0; 32 | astGroupedBinaryExpressionOperatorsDictionary["Join"] = 0; 33 | astGroupedBinaryExpressionOperatorsDictionary["Ieq"] = 0; 34 | astGroupedBinaryExpressionOperatorsDictionary["Ine"] = 0; 35 | astGroupedBinaryExpressionOperatorsDictionary["Ige"] = 0; 36 | astGroupedBinaryExpressionOperatorsDictionary["Igt"] = 0; 37 | astGroupedBinaryExpressionOperatorsDictionary["Ilt"] = 0; 38 | astGroupedBinaryExpressionOperatorsDictionary["Ile"] = 0; 39 | astGroupedBinaryExpressionOperatorsDictionary["Ilike"] = 0; 40 | astGroupedBinaryExpressionOperatorsDictionary["Inotlike"] = 0; 41 | astGroupedBinaryExpressionOperatorsDictionary["Imatch"] = 0; 42 | astGroupedBinaryExpressionOperatorsDictionary["Inotmatch"] = 0; 43 | astGroupedBinaryExpressionOperatorsDictionary["Ireplace"] = 0; 44 | astGroupedBinaryExpressionOperatorsDictionary["Icontains"] = 0; 45 | astGroupedBinaryExpressionOperatorsDictionary["Inotcontains"] = 0; 46 | astGroupedBinaryExpressionOperatorsDictionary["Iin"] = 0; 47 | astGroupedBinaryExpressionOperatorsDictionary["Inotin"] = 0; 48 | astGroupedBinaryExpressionOperatorsDictionary["Isplit"] = 0; 49 | astGroupedBinaryExpressionOperatorsDictionary["Ceq"] = 0; 50 | astGroupedBinaryExpressionOperatorsDictionary["Cne"] = 0; 51 | astGroupedBinaryExpressionOperatorsDictionary["Cge"] = 0; 52 | astGroupedBinaryExpressionOperatorsDictionary["Cgt"] = 0; 53 | astGroupedBinaryExpressionOperatorsDictionary["Clt"] = 0; 54 | astGroupedBinaryExpressionOperatorsDictionary["Cle"] = 0; 55 | astGroupedBinaryExpressionOperatorsDictionary["Clike"] = 0; 56 | astGroupedBinaryExpressionOperatorsDictionary["Cnotlike"] = 0; 57 | astGroupedBinaryExpressionOperatorsDictionary["Cmatch"] = 0; 58 | astGroupedBinaryExpressionOperatorsDictionary["Cnotmatch"] = 0; 59 | astGroupedBinaryExpressionOperatorsDictionary["Creplace"] = 0; 60 | astGroupedBinaryExpressionOperatorsDictionary["Ccontains"] = 0; 61 | astGroupedBinaryExpressionOperatorsDictionary["Cnotcontains"] = 0; 62 | astGroupedBinaryExpressionOperatorsDictionary["Cin"] = 0; 63 | astGroupedBinaryExpressionOperatorsDictionary["Cnotin"] = 0; 64 | astGroupedBinaryExpressionOperatorsDictionary["Csplit"] = 0; 65 | astGroupedBinaryExpressionOperatorsDictionary["UNKNOWN"] = 0; 66 | 67 | // Return all targeted AST objects by Count and Percent across the entire input AST object. 68 | return RevokeObfuscationHelpers.AstValueGrouper(ast, typeof(BinaryExpressionAst), astGroupedBinaryExpressionOperatorsDictionary, "AstGroupedBinaryExpressionOperators", 69 | targetAst => { return ((BinaryExpressionAst) targetAst).Operator.ToString(); } ); 70 | } 71 | } -------------------------------------------------------------------------------- /DataScience/README.md: -------------------------------------------------------------------------------- 1 | # About Revoke-Obfuscation's Data Science Approach 2 | 3 | When we published our work on detecting obfuscated PowerShell through Revoke-Obfuscation, a big part of the effort was from a data science perspective. While it is relatively easy to find some examples of obfuscated PowerShell, determining whether an approach is likely to be successful in the real world requires much more than a few off-hand examples. 4 | 5 | We primarily broke our investigation into five phases, which we are sharing here in the hope that the work we did can benefit future investigations into obfuscated PowerShell using alternative methods. 6 | 7 | 1) Prepare a PowerShell Corpus 8 | 2) Label items in the corpus as Obfuscated / Not Obfuscated 9 | 3) Identify a feature set for the PowerShell scripts 10 | 4) Run a Logistic Regression against this corpus given the identified features 11 | 5) Export the trained feature weights to incorporate into the Revoke-Obfuscation cmdlet itself 12 | 13 | ## Prepare a PowerShell Corpus 14 | 15 | The PowerShell Corpus we collected represented every publicly-available PowerShell script we could reasonably find and collect. We harvested scripts from the PowerShell Gallery, the earlier Poshcode script repository, Github, Github gists, and Technet script center. It also includes the output from three popular obfuscation frameworks: Invoke-CradleCrafter, Invoke-Obfuscation, and ISESteroids. 16 | 17 | It is important to note that this corpus is not targeted. It does not specifically represent a collection of PowerShell Malware, obfuscated PowerShell, developer's use of PowerShell, administrator's use of PowerShell, or anything else. While it contains examples of all of these, its purpose was to cast as wide a net as possible. 18 | 19 | Uncompressed, this corpus is approximately 4GB 20 | - 408,665 scripts 21 | - 28,748 authors 22 | 23 | You can download this corpus from http://aka.ms/PowerShellCorpus 24 | 25 | ## Label Items 26 | 27 | As our goal for Revoke-Obfuscation was to detect obfuscated PowerShell, we spent a long time manually reviewing and labeling scripts to categorize them into the labels of "Obfuscated" and "Not Obfuscated". This is not the same thing as malicious / not malicious. There are many commands that are malicious but not obfuscated, and many commands that are obfuscated but not malicious (hello Code Golf!). 28 | 29 | Our guiding principle during the labeling phase was this question: "Would we want this item surfaced for human review during an incident response?" 30 | 31 | The PowerShell script we used to label scripts was 'Start-LabelSession.ps1', contained in this repository. With very minor modifications, it can be used to label any data set into a binary classification. 32 | 33 | The labeled data is available as *-labeledData.csv in this repository. The 'Path' column in the CSV represents a relative path into the PowerShell Corpus. The 'Label' column is '1' if the script was obfuscated, '0' if not. 34 | 35 | This labeled data resulted in: 36 | 37 | - 7,000 labeled scripts from human authors (1,600 labeled as obfuscated) 38 | - 4,000 intentionally obfuscated scripts via frameworks (all labeled as obfuscated) 39 | 40 | And as an important reminder, this labeling was for obfuscation and not for malicious intent. Using these labels to detect "malicious PowerShell" would be an unwise approach. 41 | 42 | ## Identify Feature Set 43 | 44 | The features identified and extracted during the modeling phase are included in the 'Checks' subdirectory of the Revoke-Obfuscation repository. 45 | 46 | The 'Get-RvoFeatureVector' cmdlet in Revoke-Obfuscation is written to parse a PowerShell script and emit a feature vector for all of these identified features. 47 | 48 | By running 'Get-RvoFeatureVector' on each script, merging its output with the label for that script, we were able to generate a master CSV of all labeled scripts, their feature vectors, and their label. 49 | 50 | We have not included this resulting CSV in this part of the repository, but you can see how it is generated by reviewing 'Invoke-TrainingProcess.ps1'. Note that 'Invoke-TrainingProcess.ps1' is not intended to be run directly, it is more of a workbook that can be used to copy + paste required steps from. 51 | 52 | ## Run a Logistic Regression 53 | 54 | The Logistic Regression training is implemented in the 'ModelTrainer' directory. Please see the source code in that directory for more information about the implementation. The script 'Invoke-TrainingProcess.ps1' in this repository contains more information about how to invoke it. 55 | 56 | The implementation of this Logistic Regression was heavily based on the MSDN series, "[Gradient Descent Training using C#](https://docs.microsoft.com/en-us/archive/msdn-magazine/2015/march/test-run-gradient-descent-training-using-csharp)" 57 | 58 | ## Export the Trained Feature Weights 59 | 60 | One of the last lines of output from ModelTrainer are the feature weights that best allow the Logistic Regression to fit the labeling of that data. ModelTrainer outputs this as a row of a CSV, which we then manually copied and pasted into the 'Measure-Vector' function in Revoke-Obfuscation.psm1. Measure-RvoObfuscation then uses this these weights against the feature vector to evaluate whether a target script is obfuscated or not. -------------------------------------------------------------------------------- /Checks/AST_Ast_Types.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | public class GroupedAstTypes 8 | { 9 | //public static List> AnalyzeAst(Ast ast) 10 | public static IDictionary AnalyzeAst(Ast ast) 11 | { 12 | // Initialize Dictionary with all known AST object types initialized to 0. 13 | Dictionary astTypeDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); 14 | 15 | astTypeDictionary["Ast"] = 0; 16 | astTypeDictionary["SequencePointAst"] = 0; 17 | astTypeDictionary["ErrorStatementAst"] = 0; 18 | astTypeDictionary["ErrorExpressionAst"] = 0; 19 | astTypeDictionary["ScriptBlockAst"] = 0; 20 | astTypeDictionary["ParamBlockAst"] = 0; 21 | astTypeDictionary["NamedBlockAst"] = 0; 22 | astTypeDictionary["NamedAttributeArgumentAst"] = 0; 23 | astTypeDictionary["AttributeBaseAst"] = 0; 24 | astTypeDictionary["AttributeAst"] = 0; 25 | astTypeDictionary["TypeConstraintAst"] = 0; 26 | astTypeDictionary["ParameterAst"] = 0; 27 | astTypeDictionary["StatementBlockAst"] = 0; 28 | astTypeDictionary["StatementAst"] = 0; 29 | astTypeDictionary["TypeDefinitionAst"] = 0; 30 | astTypeDictionary["UsingStatementAst"] = 0; 31 | astTypeDictionary["MemberAst"] = 0; 32 | astTypeDictionary["PropertyMemberAst"] = 0; 33 | astTypeDictionary["FunctionMemberAst"] = 0; 34 | astTypeDictionary["CompilerGeneratedMemberFunctionAst"] = 0; 35 | astTypeDictionary["FunctionDefinitionAst"] = 0; 36 | astTypeDictionary["IfStatementAst"] = 0; 37 | astTypeDictionary["DataStatementAst"] = 0; 38 | astTypeDictionary["LabeledStatementAst"] = 0; 39 | astTypeDictionary["LoopStatementAst"] = 0; 40 | astTypeDictionary["ForEachStatementAst"] = 0; 41 | astTypeDictionary["ForStatementAst"] = 0; 42 | astTypeDictionary["DoWhileStatementAst"] = 0; 43 | astTypeDictionary["DoUntilStatementAst"] = 0; 44 | astTypeDictionary["WhileStatementAst"] = 0; 45 | astTypeDictionary["SwitchStatementAst"] = 0; 46 | astTypeDictionary["CatchClauseAst"] = 0; 47 | astTypeDictionary["TryStatementAst"] = 0; 48 | astTypeDictionary["TrapStatementAst"] = 0; 49 | astTypeDictionary["BreakStatementAst"] = 0; 50 | astTypeDictionary["ContinueStatementAst"] = 0; 51 | astTypeDictionary["ReturnStatementAst"] = 0; 52 | astTypeDictionary["ExitStatementAst"] = 0; 53 | astTypeDictionary["ThrowStatementAst"] = 0; 54 | astTypeDictionary["PipelineBaseAst"] = 0; 55 | astTypeDictionary["PipelineAst"] = 0; 56 | astTypeDictionary["CommandElementAst"] = 0; 57 | astTypeDictionary["CommandParameterAst"] = 0; 58 | astTypeDictionary["CommandBaseAst"] = 0; 59 | astTypeDictionary["CommandAst"] = 0; 60 | astTypeDictionary["CommandExpressionAst"] = 0; 61 | astTypeDictionary["RedirectionAst"] = 0; 62 | astTypeDictionary["MergingRedirectionAst"] = 0; 63 | astTypeDictionary["FileRedirectionAst"] = 0; 64 | astTypeDictionary["AssignmentStatementAst"] = 0; 65 | astTypeDictionary["ConfigurationDefinitionAst"] = 0; 66 | astTypeDictionary["DynamicKeywordStatementAst"] = 0; 67 | astTypeDictionary["ExpressionAst"] = 0; 68 | astTypeDictionary["BinaryExpressionAst"] = 0; 69 | astTypeDictionary["UnaryExpressionAst"] = 0; 70 | astTypeDictionary["BlockStatementAst"] = 0; 71 | astTypeDictionary["AttributedExpressionAst"] = 0; 72 | astTypeDictionary["ConvertExpressionAst"] = 0; 73 | astTypeDictionary["MemberExpressionAst"] = 0; 74 | astTypeDictionary["InvokeMemberExpressionAst"] = 0; 75 | astTypeDictionary["BaseCtorInvokeMemberExpressionAst"] = 0; 76 | astTypeDictionary["TypeExpressionAst"] = 0; 77 | astTypeDictionary["VariableExpressionAst"] = 0; 78 | astTypeDictionary["ConstantExpressionAst"] = 0; 79 | astTypeDictionary["StringConstantExpressionAst"] = 0; 80 | astTypeDictionary["ExpandableStringExpressionAst"] = 0; 81 | astTypeDictionary["ScriptBlockExpressionAst"] = 0; 82 | astTypeDictionary["ArrayLiteralAst"] = 0; 83 | astTypeDictionary["HashtableAst"] = 0; 84 | astTypeDictionary["ArrayExpressionAst"] = 0; 85 | astTypeDictionary["ParenExpressionAst"] = 0; 86 | astTypeDictionary["SubExpressionAst"] = 0; 87 | astTypeDictionary["UsingExpressionAst"] = 0; 88 | astTypeDictionary["IndexExpressionAst"] = 0; 89 | astTypeDictionary["AssignmentTarget"] = 0; 90 | astTypeDictionary["UNKNOWN"] = 0; 91 | 92 | // Return all targeted AST objects by Count and Percent across the entire input AST object. 93 | return RevokeObfuscationHelpers.AstValueGrouper(ast, typeof(Ast), astTypeDictionary, "AstGroupedAstTypes", 94 | targetAst => { return ((Ast) targetAst).GetType().FullName.Replace("System.Management.Automation.Language.","").Replace("System.",""); } ); 95 | } 96 | } -------------------------------------------------------------------------------- /DataScience/Invoke-TrainingProcess.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Accuracy / False Positive rate on training data = 0.9854 / 0.0035 3 | Accuracy / False Positive rate on test data = 0.9115 / 0.0549 4 | #> 5 | 6 | ## Process all the files in a directory, opening them up in notepad. When notepad closes: 7 | ## Push ENTER if it is clean 8 | ## Type any letter and then ENTER if it is obfuscated 9 | ## They will then be moved to "Obfuscated" or "Clean" subdirectories. 10 | if(-not (Test-Path Obfuscated)) { mkdir Obfuscated } 11 | if(-not (Test-Path Clean)) { mkdir Clean } 12 | dir -af | % { notepad $_.Fullname | Out-Null; $d = Read-Host; if($d) { $_ | Move-Item -Destination Obfuscated } else { $_ | Move-Item -Destination Clean } } 13 | 14 | 15 | ## Process known obfuscated 16 | dir .\InvokeCradleCrafter\ -af -rec | % { [PSCustomObject] @{ Path = (Resolve-Path -Relative $_.FullName).Substring(2); Label = "1" } } | Export-Csv InvokeCradleCrafter-obfuscation-labeledData.csv -NoTypeInformation 17 | 18 | ## Helpful intern version: 19 | ## Have Revoke-Obfuscation emit results for all items in the Clean or Obfuscated subdirectories. Requires an update to 20 | ## Revoke-Obfuscation ("-PassThru") to emit the objects directly rather than have them write to a CSV. 21 | ## Dump into one massive "AnalyzedCorpus.csv" 22 | ## 30 minutes for 11264 scripts 23 | $labeledData = @{} 24 | $bn = "c:\users\lee\corpus" 25 | dir PowerShellCorpus\*.csv | % { Import-Csv $_.FullName | % { $labeledData[ (Join-Path $bn $_.path) ] = $_.Label } } 26 | $analyzedCorpus = $labeledData.Keys | % { 27 | $scriptResult = ([PSCustomObject] (Get-RvoFeatureVector -Path $_)) | ConvertTo-CSV -NoTypeInformation | Select -Last 1 28 | $scriptResult += "," + $labeledData[$_] 29 | $scriptResult 30 | } 31 | $analyzedCorpus | Set-Content AnalyzedCorpus.csv 32 | 33 | ## Run the model traininer on that CSV. It will output a weight vector as one of its lines. 34 | ModelTrainer\ModelTrainer.exe C:\Users\lee\OneDrive\Documents\Revoke-Obfuscation\AnalyzedCorpus.csv 0.01 | Tee-Object -Variable Output 35 | $weights = ($output[-12] -split ' ' | ? { $_ } ) -join ', ' 36 | 37 | 38 | ## "In the wild"" version: 39 | $labeledData = @{} 40 | $bn = "c:\users\lee\corpus" 41 | $inTheWild = "PowerShellCorpus\InvokeCradleCrafter-obfuscation-labeledData.csv", "PowerShellCorpus\InvokeObfuscation-obfuscation-labeledData.csv", "PowerShellCorpus\IseSteroids-obfuscation-labeledData.csv", "PowerShellCorpus\UnderhandedPowerShell-obfuscation-labeledData.csv" 42 | dir PowerShellCorpus\*.csv | % { Import-Csv $_.FullName | % { $labeledData[ (Join-Path $bn $_.path) ] = 0 } } 43 | dir $inTheWild | % { Import-Csv $_.FullName | % { $labeledData[ (Join-Path $bn $_.path) ] = $_.Label } } 44 | 45 | $analyzedCorpus = $labeledData.Keys | % { 46 | $scriptResult = ([PSCustomObject] (Get-RvoFeatureVector -Path $_)) | ConvertTo-CSV -NoTypeInformation | Select -Last 1 47 | $scriptResult += "," + $labeledData[$_] 48 | $scriptResult 49 | } 50 | $analyzedCorpus | Set-Content AnalyzedCorpusInTheWild.csv 51 | 52 | ## Run the model traininer on that CSV. It will output a weight vector as one of its lines. 53 | ModelTrainer\ModelTrainer.exe C:\Users\lee\OneDrive\Documents\Revoke-Obfuscation\AnalyzedCorpusInTheWild.csv | Tee-Object -Variable Output 54 | $weights = ($output[-12] -split ' ' | ? { $_ } ) -join ', ' 55 | 56 | 57 | 58 | 59 | ## Seeing Measure-CharacterFrequency 60 | <# 61 | 62 | Accuracy: 0.707203179334327 63 | Precision: 0.898895027624309 64 | Recall: 0.370530630835801 65 | F1Score: 0.524754071923883 66 | 67 | TruePositiveRate: 0.161649279682067 68 | FalsePositiveRate: 0.0181818181818182 69 | TrueNegativeRate: 0.54555389965226 70 | FalseNegativeRate: 0.274615002483855 71 | 72 | #> 73 | $labeledData = @{} 74 | dir corpus\*.csv | % { $bn = $_.Directory; Import-Csv $_.FullName | % { if(($_.Path -notmatch '^(Invoke|Underhanded|IseSteroids)') -and ($_.Label -eq '1')) { Write-Host "Skipping $_" } else { $_ } } | % { $labeledData[ (Join-Path $bn $_.path) ] = $_.Label } } 75 | $globalFrequency = Measure-CharacterFrequency -LiteralPath ($labeledData.GetEnumerator() | ? Value -eq "0" | % Name) 76 | 77 | $truePositives = 0 78 | $falsePositives = 0 79 | $trueNegatives = 0 80 | $falseNegatives = 0 81 | $labeledData.GetEnumerator() | % { 82 | $scriptFrequency = Measure-CharacterFrequency.ps1 -LiteralPath $_.Name 83 | $sim = Measure-VectorSimilarity $globalFrequency $scriptFrequency -KeyProperty Name -ValueProperty Percent 84 | 85 | ## Evaluated as obfuscated 86 | if($sim -lt 0.8) 87 | { 88 | ## Is actually obfuscated 89 | if($_.Value -eq "1") 90 | { 91 | $truePositives++ 92 | } 93 | else 94 | { 95 | $falsePositives++ 96 | } 97 | } 98 | else 99 | { 100 | ## Evaluated as clean 101 | 102 | ## Is actually obfuscated 103 | if($_.Value -eq "1") 104 | { 105 | $falseNegatives++ 106 | } 107 | else 108 | { 109 | $trueNegatives++ 110 | } 111 | } 112 | } 113 | 114 | $totalCount = $truePositives + $falsePositives + $falseNegatives + $trueNegatives 115 | $accuracy = ($truePositives * 1.0 + $trueNegatives) / $totalCount 116 | $precision = ($truePositives * 1.0) / ($truePositives + $falsePositives) 117 | $recall = ($truePositives * 1.0) / ($truePositives + $falseNegatives) 118 | $f1Score = (2.0 * $precision * $recall) / ($precision + $recall) 119 | 120 | "Accuracy: " + $accuracy 121 | "Precision: " + $precision 122 | "Recall: " + $recall 123 | "F1Score: " + $f1Score 124 | 125 | "TruePositiveRate: " + ($truePositives / $totalCount) 126 | "FalsePositiveRate: " + ($falsePositives / $totalCount) 127 | "TrueNegativeRate: " + ($trueNegatives / $totalCount) 128 | "FalseNegativeRate: " + ($falseNegatives / $totalCount) 129 | 130 | 131 | 132 | ## Command lines 133 | $all = $(ipcsv .\Lee_Clean.csv; ipcsv .\Lee_Obf.csv) 134 | $analyzedCommandLines = $all | % { 135 | $scriptResult = ([PSCustomObject] (Get-RvoFeatureVector -ScriptExpression $_.ArgsCleaned)) | ConvertTo-CSV -NoTypeInformation | Select -Last 1 136 | if($_.DBO_Obfuscated -match 'clean') 137 | { 138 | $scriptResult += ",0" 139 | } 140 | else 141 | { 142 | $scriptResult += ",1" 143 | } 144 | 145 | $scriptResult 146 | } 147 | $analyzedCommandLines | Set-Content AnalyzedCommandLines.csv 148 | 149 | $output = C:\users\lee\OneDrive\Documents\Revoke-Obfuscation\ModelTrainer\ModelTrainer.exe RandomAnalyzedCommandLines.csv 0.3 100 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Revoke-Obfuscation v1.0 2 | =============== 3 | 4 | ![Revoke-Obfuscation Demo](https://github.com/danielbohannon/danielbohannon.github.io/blob/master/Revoke-Obfuscation%20Demo.gif) 5 | 6 | 7 | ![Revoke-Obfuscation Screenshot](https://github.com/danielbohannon/danielbohannon.github.io/blob/master/Revoke-Obfuscation%20Screenshot.png) 8 | 9 | Introduction 10 | ------------ 11 | Revoke-Obfuscation is a PowerShell v3.0+ compatible PowerShell obfuscation detection framework. 12 | 13 | Authors 14 | ------- 15 | Daniel Bohannon ([@danielhbohannon](https://twitter.com/danielhbohannon)) 16 | Lee Holmes ([@Lee_Homes](https://twitter.com/Lee_Holmes)) 17 | 18 | Research 19 | -------- 20 | Blog Post: [https://www.fireeye.com/blog/threat-research/2017/07/revoke-obfuscation-powershell.html](https://www.fireeye.com/blog/threat-research/2017/07/revoke-obfuscation-powershell.html) 21 | 22 | White Paper: [https://www.fireeye.com/content/dam/fireeye-www/blog/pdfs/revoke-obfuscation-report.pdf](https://www.fireeye.com/content/dam/fireeye-www/blog/pdfs/revoke-obfuscation-report.pdf) 23 | 24 | Background 25 | ---------- 26 | In the Fall of 2016 and Spring of 2017, Daniel Bohannon ([@danielhbohannon](https://twitter.com/danielhbohannon)) released [Invoke-Obfuscation](https://github.com/danielbohannon/Invoke-Obfuscation) and [Invoke-CradleCrafter](https://github.com/danielbohannon/Invoke-CradleCrafter), two open-source PowerShell obfuscation frameworks. The goal of this research and these frameworks was to highlight the limitations of a purely signature-based approach to detecting attackers' usage of PowerShell. The core message to defenders has been to focus on detecting **Indicators of Obfuscation** in addition to known suspicious syntax. 27 | 28 | However, the extreme levels of randomization in Invoke-Obfuscation and Invoke-CradleCrafter paired with the token-layer obfuscation options that are not deobfuscated in PowerShell's script block logging have led defenders to look for a new, scalable means of generically detecting both known and unknown obfuscation techniques. 29 | 30 | A few weeks after the release of Invoke-Obfuscation, Lee Holmes ([@Lee_Homes](https://twitter.com/Lee_Holmes)) authored a blog posted entitled [More Detecting Obfuscated PowerShell](http://www.leeholmes.com/blog/2016/10/22/more-detecting-obfuscated-powershell) in which he highlighted statistical analysis techniques to detect anomalous features found in heavily obfuscated scripts, specifically those produced by Invoke-Obfuscation. 31 | 32 | Since this exchange, Daniel and Lee became good friends and shared many common interests/obsessions -- namely, a love of fine coffee and the pursuit of creating new ways to thoroughly detect obfuscated PowerShell. 33 | 34 | The amount of time both Blue Teamers spent pouring over research and POC code would equate to several thousand cups of Chemex-brewed coffee assuming the proper 4-minute target brew time (assuming at least one other coffee enthusiast picked up on this "pour over" pun). 35 | 36 | Revoke-Obfuscation is the final hand-crafted product of these efforts. 37 | 38 | Purpose 39 | ------- 40 | Revoke-Obfuscation is an open-source PowerShell v3.0+ framework for detecting obfuscated PowerShell commands and scripts at scale. It relies on PowerShell's AST (Abstract Syntax Tree) to rapidly extract thousands of features from any input PowerShell script and compare this feature vector against one of several pre-defined weighted feature vectors computed through an automated learning process conducted against a corpus of 408K+ PowerShell scripts. This full corpus can be downloaded from ([https://aka.ms/PowerShellCorpus](https://aka.ms/PowerShellCorpus)). You can find the details behind the data science aspects of this work in the 'DataScience' subdirectory of the repository. 41 | 42 | Since Revoke-Obfuscation relies on feature extraction and comparison instead of pure IOCs or RegEx matching, it is more robust in its ability to identify unknown obfuscation techniques even when attackers attempt to subdue their obfuscation by padding it with unobfuscated script contents to overthrow basic checks like character frequency analysis. 43 | 44 | Revoke-Obfuscation can easily measure most input PowerShell scripts within 100-300 milliseconds. This level of performance allows an organization to measure the obfuscation of (at worst) 12K+ PowerShell scripts per hour without the need to index verbose PowerShell script block logs in a SIEM. 45 | 46 | Lastly, Revoke-Obfuscation supports easy whitelisting functionality along with the ability to ingest PowerShell Operational event log records and reassemble script blocks that are recorded across numerous script block EID 4104 records. It can easily become a one-stop shop for ingesting an environment's PowerShell Operational event logs, reassembling and unique'ing all scripts within those logs, and then identifying obfuscated PowerShell scripts that deserve manual inspection. 47 | 48 | Installation 49 | ------------ 50 | The source code for Revoke-Obfuscation is hosted at Github, and you may download, fork and review it from this repository (https://github.com/danielbohannon/Revoke-Obfuscation). Please report issues or feature requests through Github's bug tracker associated with this project. 51 | 52 | To install (from Github): 53 | 54 | Import-Module .\Revoke-Obfuscation.psd1 55 | 56 | The source code can also be installed directly from the PowerShell Gallery via the following commands: 57 | 58 | To install (from PowerShell Gallery): 59 | 60 | Install-Module Revoke-Obfuscation 61 | Import-Module Revoke-Obfuscation 62 | 63 | Usage 64 | ----- 65 | `Revoke-Obfuscation` will provide a detailed tutorial as well as a few other fun surprises. But if you are not into the lulz then you can simply run `Get-Help Measure-RvoObfuscation` to see usage syntax or just continue reading. 66 | 67 | There are two primary functions used in this framework: 68 | * **Get-RvoScriptBlock** -- reassembles scripts from EID 4104 script block logs 69 | * **Measure-RvoObfuscation** -- measures input script(s) and returns obfuscation score 70 | 71 | If you need to reassemble and extract script block logs from PowerShell Operational logs then `Get-RvoScriptBlock` is your function of choice. It automatically returns only unique script blocks and excludes certain default script block values deemed not malicious. This can be overridden with the -Deep switch. 72 | 73 | * `Get-RvoScriptBlock -Path 'C:\Windows\System32\Winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx' -Verbose` 74 | * `Get-ChildItem .\Demo\demo.evtx | Get-RvoScriptBlock -Verbose` 75 | * `Get-WinEvent -LogName Microsoft-Windows-PowerShell/Operational | Get-RvoScriptBlock -Verbose` 76 | 77 | `Get-RvoScriptBlock` also supports MIR/HX audit results as well as PowerShell 78 | Operational logs retrieved via Matt Graeber's (@mattifestation) CimSweep project 79 | (https://github.com/PowerShellMafia/CimSweep). For CimSweep there is a minor 80 | registry tweak required to trick WMI into querying a non-classic event log. 81 | Details can be found in the NOTES section of `Get-RvoScriptBlock`. 82 | 83 | * `Get-ChildItem C:\MirOrHxAuditFiles\*_w32eventlogs.xml | Get-RvoScriptBlock -Verbose` 84 | * `Get-CSEventLogEntry -LogName Microsoft-Windows-PowerShell/Operational | Get-RvoScriptBlock` 85 | 86 | A full example against test data recorded in demo.evtx can be found below: 87 | 88 | $obfResults = Get-WinEvent -Path .\Demo\demo.evtx | Get-RvoScriptBlock | Measure-RvoObfuscation -OutputToDisk -Verbose 89 | 90 | A full example against local and remotely hosted test scripts can be found below: 91 | 92 | * `Measure-RvoObfuscation -Url 'http://bit.ly/DBOdemo1' -Verbose -OutputToDisk` 93 | * `Get-Content .\Demo\DBOdemo*.ps1 | Measure-RvoObfuscation -Verbose -OutputToDisk` 94 | * `Get-ChildItem .\Demo\DBOdemo*.ps1 | Measure-RvoObfuscation -Verbose -OutputToDisk` 95 | 96 | The `-OutputToDisk` switch will automatically output all obfuscated scripts to .\Results\Obfuscated\. Regardless, all results will be returned as PSCustomObjects containing the script content along with metadata like an obfuscation score, measurement time, whitelisting result, all extracted script features, etc. 97 | 98 | Three whitelisting options exist in two locations in Revoke-Obfuscation: 99 | 1. On Disk (automatically applied if present): 100 | 1. **.\Whitelist\Scripts_To_Whitelist\\** -- All scripts placed in this directory will be hashed 101 | and any identical scripts will be whitelisted. This whitelisting method is preferred above the next two options. 102 | 1. **.\Whitelist\Strings_To_Whitelist.txt** -- A script containing ANY of the strings in this file 103 | will be whitelisted. *Syntax: Rule_Name,string_to_whitelist* 104 | 1. **.\Whitelist\Regex_To_Whitelist.txt** -- A script containing ANY of the regular expressions in 105 | this file will be whitelisted. *Syntax: Rule_Name,regex_to_whitelist* 106 | 1. Arguments for Measure-RvoObfuscation (applied in addition to above whitelisting options): 107 | 1. **-WhitelistFile** -- `-WhitelistFile .\files\*.ps1,.\more_files\*.ps1,.\one_more_file.ps1` 108 | 1. **-WhitelistContent** -- `-WhitelistContent 'string 1 to whitelist','string 2 to whitelist'` 109 | 1. **-WhitelistRegex** -- `-WhitelistRegex 'regex 1 to whitelist','regex 2 to whitelist'` 110 | 111 | If interested in creating your own set of training data and generating a weighted vector for the Measure-Vector 112 | function, then ModelTrainer.cs/ModelTrainer.exe can be executed against a labeled data set. The following command will extract feature vectors from all input scripts and aggregate them into a single CSV used in this training phase: 113 | 114 | Get-ChildItem .\*.ps1 | ForEach-Object { [PSCustomObject](Get-RvoFeatureVector -Path $_.FullName) | Export-Csv .\all_features.csv -Append } 115 | 116 | Lastly, if looking for a platform for creating indicators (IOCs) that harness the power of PowerShell's AST (Abstract Syntax Tree) -- which we would highly recommend for identifying malicious PowerShell activity that is NOT obfuscated -- then [PS Script Analyzer](https://github.com/PowerShell/PSScriptAnalyzer) is an excellent framework designed to handle such tasks. 117 | 118 | License 119 | ------- 120 | Revoke-Obfuscation is released under the Apache 2.0 license. 121 | 122 | Release Notes 123 | ------------- 124 | v1.0 - 2017-07-27 Black Hat USA & 2017-07-30 DEF CON: PUBLIC Release of Revoke-Obfuscation. 125 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 Daniel Bohannon & Lee Holmes 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /DataScience/InvokeCradleCrafter-obfuscation-labeledData.csv: -------------------------------------------------------------------------------- 1 | "Path","Label" 2 | "InvokeCradleCrafter\invoke-cradlecrafter_random_1.ps1","1" 3 | "InvokeCradleCrafter\invoke-cradlecrafter_random_10.ps1","1" 4 | "InvokeCradleCrafter\invoke-cradlecrafter_random_100.ps1","1" 5 | "InvokeCradleCrafter\invoke-cradlecrafter_random_101.ps1","1" 6 | "InvokeCradleCrafter\invoke-cradlecrafter_random_102.ps1","1" 7 | "InvokeCradleCrafter\invoke-cradlecrafter_random_103.ps1","1" 8 | "InvokeCradleCrafter\invoke-cradlecrafter_random_104.ps1","1" 9 | "InvokeCradleCrafter\invoke-cradlecrafter_random_105.ps1","1" 10 | "InvokeCradleCrafter\invoke-cradlecrafter_random_106.ps1","1" 11 | "InvokeCradleCrafter\invoke-cradlecrafter_random_107.ps1","1" 12 | "InvokeCradleCrafter\invoke-cradlecrafter_random_108.ps1","1" 13 | "InvokeCradleCrafter\invoke-cradlecrafter_random_109.ps1","1" 14 | "InvokeCradleCrafter\invoke-cradlecrafter_random_11.ps1","1" 15 | "InvokeCradleCrafter\invoke-cradlecrafter_random_110.ps1","1" 16 | "InvokeCradleCrafter\invoke-cradlecrafter_random_111.ps1","1" 17 | "InvokeCradleCrafter\invoke-cradlecrafter_random_112.ps1","1" 18 | "InvokeCradleCrafter\invoke-cradlecrafter_random_113.ps1","1" 19 | "InvokeCradleCrafter\invoke-cradlecrafter_random_114.ps1","1" 20 | "InvokeCradleCrafter\invoke-cradlecrafter_random_115.ps1","1" 21 | "InvokeCradleCrafter\invoke-cradlecrafter_random_116.ps1","1" 22 | "InvokeCradleCrafter\invoke-cradlecrafter_random_117.ps1","1" 23 | "InvokeCradleCrafter\invoke-cradlecrafter_random_118.ps1","1" 24 | "InvokeCradleCrafter\invoke-cradlecrafter_random_119.ps1","1" 25 | "InvokeCradleCrafter\invoke-cradlecrafter_random_12.ps1","1" 26 | "InvokeCradleCrafter\invoke-cradlecrafter_random_120.ps1","1" 27 | "InvokeCradleCrafter\invoke-cradlecrafter_random_121.ps1","1" 28 | "InvokeCradleCrafter\invoke-cradlecrafter_random_122.ps1","1" 29 | "InvokeCradleCrafter\invoke-cradlecrafter_random_123.ps1","1" 30 | "InvokeCradleCrafter\invoke-cradlecrafter_random_124.ps1","1" 31 | "InvokeCradleCrafter\invoke-cradlecrafter_random_125.ps1","1" 32 | "InvokeCradleCrafter\invoke-cradlecrafter_random_126.ps1","1" 33 | "InvokeCradleCrafter\invoke-cradlecrafter_random_127.ps1","1" 34 | "InvokeCradleCrafter\invoke-cradlecrafter_random_128.ps1","1" 35 | "InvokeCradleCrafter\invoke-cradlecrafter_random_129.ps1","1" 36 | "InvokeCradleCrafter\invoke-cradlecrafter_random_13.ps1","1" 37 | "InvokeCradleCrafter\invoke-cradlecrafter_random_130.ps1","1" 38 | "InvokeCradleCrafter\invoke-cradlecrafter_random_131.ps1","1" 39 | "InvokeCradleCrafter\invoke-cradlecrafter_random_132.ps1","1" 40 | "InvokeCradleCrafter\invoke-cradlecrafter_random_133.ps1","1" 41 | "InvokeCradleCrafter\invoke-cradlecrafter_random_134.ps1","1" 42 | "InvokeCradleCrafter\invoke-cradlecrafter_random_135.ps1","1" 43 | "InvokeCradleCrafter\invoke-cradlecrafter_random_136.ps1","1" 44 | "InvokeCradleCrafter\invoke-cradlecrafter_random_137.ps1","1" 45 | "InvokeCradleCrafter\invoke-cradlecrafter_random_138.ps1","1" 46 | "InvokeCradleCrafter\invoke-cradlecrafter_random_139.ps1","1" 47 | "InvokeCradleCrafter\invoke-cradlecrafter_random_14.ps1","1" 48 | "InvokeCradleCrafter\invoke-cradlecrafter_random_140.ps1","1" 49 | "InvokeCradleCrafter\invoke-cradlecrafter_random_141.ps1","1" 50 | "InvokeCradleCrafter\invoke-cradlecrafter_random_142.ps1","1" 51 | "InvokeCradleCrafter\invoke-cradlecrafter_random_143.ps1","1" 52 | "InvokeCradleCrafter\invoke-cradlecrafter_random_144.ps1","1" 53 | "InvokeCradleCrafter\invoke-cradlecrafter_random_145.ps1","1" 54 | "InvokeCradleCrafter\invoke-cradlecrafter_random_146.ps1","1" 55 | "InvokeCradleCrafter\invoke-cradlecrafter_random_147.ps1","1" 56 | "InvokeCradleCrafter\invoke-cradlecrafter_random_148.ps1","1" 57 | "InvokeCradleCrafter\invoke-cradlecrafter_random_149.ps1","1" 58 | "InvokeCradleCrafter\invoke-cradlecrafter_random_15.ps1","1" 59 | "InvokeCradleCrafter\invoke-cradlecrafter_random_150.ps1","1" 60 | "InvokeCradleCrafter\invoke-cradlecrafter_random_151.ps1","1" 61 | "InvokeCradleCrafter\invoke-cradlecrafter_random_152.ps1","1" 62 | "InvokeCradleCrafter\invoke-cradlecrafter_random_153.ps1","1" 63 | "InvokeCradleCrafter\invoke-cradlecrafter_random_154.ps1","1" 64 | "InvokeCradleCrafter\invoke-cradlecrafter_random_155.ps1","1" 65 | "InvokeCradleCrafter\invoke-cradlecrafter_random_156.ps1","1" 66 | "InvokeCradleCrafter\invoke-cradlecrafter_random_157.ps1","1" 67 | "InvokeCradleCrafter\invoke-cradlecrafter_random_158.ps1","1" 68 | "InvokeCradleCrafter\invoke-cradlecrafter_random_159.ps1","1" 69 | "InvokeCradleCrafter\invoke-cradlecrafter_random_16.ps1","1" 70 | "InvokeCradleCrafter\invoke-cradlecrafter_random_160.ps1","1" 71 | "InvokeCradleCrafter\invoke-cradlecrafter_random_161.ps1","1" 72 | "InvokeCradleCrafter\invoke-cradlecrafter_random_162.ps1","1" 73 | "InvokeCradleCrafter\invoke-cradlecrafter_random_163.ps1","1" 74 | "InvokeCradleCrafter\invoke-cradlecrafter_random_164.ps1","1" 75 | "InvokeCradleCrafter\invoke-cradlecrafter_random_165.ps1","1" 76 | "InvokeCradleCrafter\invoke-cradlecrafter_random_166.ps1","1" 77 | "InvokeCradleCrafter\invoke-cradlecrafter_random_167.ps1","1" 78 | "InvokeCradleCrafter\invoke-cradlecrafter_random_168.ps1","1" 79 | "InvokeCradleCrafter\invoke-cradlecrafter_random_169.ps1","1" 80 | "InvokeCradleCrafter\invoke-cradlecrafter_random_17.ps1","1" 81 | "InvokeCradleCrafter\invoke-cradlecrafter_random_170.ps1","1" 82 | "InvokeCradleCrafter\invoke-cradlecrafter_random_171.ps1","1" 83 | "InvokeCradleCrafter\invoke-cradlecrafter_random_172.ps1","1" 84 | "InvokeCradleCrafter\invoke-cradlecrafter_random_173.ps1","1" 85 | "InvokeCradleCrafter\invoke-cradlecrafter_random_174.ps1","1" 86 | "InvokeCradleCrafter\invoke-cradlecrafter_random_175.ps1","1" 87 | "InvokeCradleCrafter\invoke-cradlecrafter_random_176.ps1","1" 88 | "InvokeCradleCrafter\invoke-cradlecrafter_random_177.ps1","1" 89 | "InvokeCradleCrafter\invoke-cradlecrafter_random_178.ps1","1" 90 | "InvokeCradleCrafter\invoke-cradlecrafter_random_179.ps1","1" 91 | "InvokeCradleCrafter\invoke-cradlecrafter_random_18.ps1","1" 92 | "InvokeCradleCrafter\invoke-cradlecrafter_random_180.ps1","1" 93 | "InvokeCradleCrafter\invoke-cradlecrafter_random_181.ps1","1" 94 | "InvokeCradleCrafter\invoke-cradlecrafter_random_182.ps1","1" 95 | "InvokeCradleCrafter\invoke-cradlecrafter_random_183.ps1","1" 96 | "InvokeCradleCrafter\invoke-cradlecrafter_random_184.ps1","1" 97 | "InvokeCradleCrafter\invoke-cradlecrafter_random_185.ps1","1" 98 | "InvokeCradleCrafter\invoke-cradlecrafter_random_186.ps1","1" 99 | "InvokeCradleCrafter\invoke-cradlecrafter_random_187.ps1","1" 100 | "InvokeCradleCrafter\invoke-cradlecrafter_random_188.ps1","1" 101 | "InvokeCradleCrafter\invoke-cradlecrafter_random_189.ps1","1" 102 | "InvokeCradleCrafter\invoke-cradlecrafter_random_19.ps1","1" 103 | "InvokeCradleCrafter\invoke-cradlecrafter_random_190.ps1","1" 104 | "InvokeCradleCrafter\invoke-cradlecrafter_random_191.ps1","1" 105 | "InvokeCradleCrafter\invoke-cradlecrafter_random_192.ps1","1" 106 | "InvokeCradleCrafter\invoke-cradlecrafter_random_193.ps1","1" 107 | "InvokeCradleCrafter\invoke-cradlecrafter_random_194.ps1","1" 108 | "InvokeCradleCrafter\invoke-cradlecrafter_random_195.ps1","1" 109 | "InvokeCradleCrafter\invoke-cradlecrafter_random_196.ps1","1" 110 | "InvokeCradleCrafter\invoke-cradlecrafter_random_197.ps1","1" 111 | "InvokeCradleCrafter\invoke-cradlecrafter_random_198.ps1","1" 112 | "InvokeCradleCrafter\invoke-cradlecrafter_random_199.ps1","1" 113 | "InvokeCradleCrafter\invoke-cradlecrafter_random_2.ps1","1" 114 | "InvokeCradleCrafter\invoke-cradlecrafter_random_20.ps1","1" 115 | "InvokeCradleCrafter\invoke-cradlecrafter_random_200.ps1","1" 116 | "InvokeCradleCrafter\invoke-cradlecrafter_random_21.ps1","1" 117 | "InvokeCradleCrafter\invoke-cradlecrafter_random_22.ps1","1" 118 | "InvokeCradleCrafter\invoke-cradlecrafter_random_23.ps1","1" 119 | "InvokeCradleCrafter\invoke-cradlecrafter_random_24.ps1","1" 120 | "InvokeCradleCrafter\invoke-cradlecrafter_random_25.ps1","1" 121 | "InvokeCradleCrafter\invoke-cradlecrafter_random_26.ps1","1" 122 | "InvokeCradleCrafter\invoke-cradlecrafter_random_27.ps1","1" 123 | "InvokeCradleCrafter\invoke-cradlecrafter_random_28.ps1","1" 124 | "InvokeCradleCrafter\invoke-cradlecrafter_random_29.ps1","1" 125 | "InvokeCradleCrafter\invoke-cradlecrafter_random_3.ps1","1" 126 | "InvokeCradleCrafter\invoke-cradlecrafter_random_30.ps1","1" 127 | "InvokeCradleCrafter\invoke-cradlecrafter_random_31.ps1","1" 128 | "InvokeCradleCrafter\invoke-cradlecrafter_random_32.ps1","1" 129 | "InvokeCradleCrafter\invoke-cradlecrafter_random_33.ps1","1" 130 | "InvokeCradleCrafter\invoke-cradlecrafter_random_34.ps1","1" 131 | "InvokeCradleCrafter\invoke-cradlecrafter_random_35.ps1","1" 132 | "InvokeCradleCrafter\invoke-cradlecrafter_random_36.ps1","1" 133 | "InvokeCradleCrafter\invoke-cradlecrafter_random_37.ps1","1" 134 | "InvokeCradleCrafter\invoke-cradlecrafter_random_38.ps1","1" 135 | "InvokeCradleCrafter\invoke-cradlecrafter_random_39.ps1","1" 136 | "InvokeCradleCrafter\invoke-cradlecrafter_random_4.ps1","1" 137 | "InvokeCradleCrafter\invoke-cradlecrafter_random_40.ps1","1" 138 | "InvokeCradleCrafter\invoke-cradlecrafter_random_41.ps1","1" 139 | "InvokeCradleCrafter\invoke-cradlecrafter_random_42.ps1","1" 140 | "InvokeCradleCrafter\invoke-cradlecrafter_random_43.ps1","1" 141 | "InvokeCradleCrafter\invoke-cradlecrafter_random_44.ps1","1" 142 | "InvokeCradleCrafter\invoke-cradlecrafter_random_45.ps1","1" 143 | "InvokeCradleCrafter\invoke-cradlecrafter_random_46.ps1","1" 144 | "InvokeCradleCrafter\invoke-cradlecrafter_random_47.ps1","1" 145 | "InvokeCradleCrafter\invoke-cradlecrafter_random_48.ps1","1" 146 | "InvokeCradleCrafter\invoke-cradlecrafter_random_49.ps1","1" 147 | "InvokeCradleCrafter\invoke-cradlecrafter_random_5.ps1","1" 148 | "InvokeCradleCrafter\invoke-cradlecrafter_random_50.ps1","1" 149 | "InvokeCradleCrafter\invoke-cradlecrafter_random_51.ps1","1" 150 | "InvokeCradleCrafter\invoke-cradlecrafter_random_52.ps1","1" 151 | "InvokeCradleCrafter\invoke-cradlecrafter_random_53.ps1","1" 152 | "InvokeCradleCrafter\invoke-cradlecrafter_random_54.ps1","1" 153 | "InvokeCradleCrafter\invoke-cradlecrafter_random_55.ps1","1" 154 | "InvokeCradleCrafter\invoke-cradlecrafter_random_56.ps1","1" 155 | "InvokeCradleCrafter\invoke-cradlecrafter_random_57.ps1","1" 156 | "InvokeCradleCrafter\invoke-cradlecrafter_random_58.ps1","1" 157 | "InvokeCradleCrafter\invoke-cradlecrafter_random_59.ps1","1" 158 | "InvokeCradleCrafter\invoke-cradlecrafter_random_6.ps1","1" 159 | "InvokeCradleCrafter\invoke-cradlecrafter_random_60.ps1","1" 160 | "InvokeCradleCrafter\invoke-cradlecrafter_random_61.ps1","1" 161 | "InvokeCradleCrafter\invoke-cradlecrafter_random_62.ps1","1" 162 | "InvokeCradleCrafter\invoke-cradlecrafter_random_63.ps1","1" 163 | "InvokeCradleCrafter\invoke-cradlecrafter_random_64.ps1","1" 164 | "InvokeCradleCrafter\invoke-cradlecrafter_random_65.ps1","1" 165 | "InvokeCradleCrafter\invoke-cradlecrafter_random_66.ps1","1" 166 | "InvokeCradleCrafter\invoke-cradlecrafter_random_67.ps1","1" 167 | "InvokeCradleCrafter\invoke-cradlecrafter_random_68.ps1","1" 168 | "InvokeCradleCrafter\invoke-cradlecrafter_random_69.ps1","1" 169 | "InvokeCradleCrafter\invoke-cradlecrafter_random_7.ps1","1" 170 | "InvokeCradleCrafter\invoke-cradlecrafter_random_70.ps1","1" 171 | "InvokeCradleCrafter\invoke-cradlecrafter_random_71.ps1","1" 172 | "InvokeCradleCrafter\invoke-cradlecrafter_random_72.ps1","1" 173 | "InvokeCradleCrafter\invoke-cradlecrafter_random_73.ps1","1" 174 | "InvokeCradleCrafter\invoke-cradlecrafter_random_74.ps1","1" 175 | "InvokeCradleCrafter\invoke-cradlecrafter_random_75.ps1","1" 176 | "InvokeCradleCrafter\invoke-cradlecrafter_random_76.ps1","1" 177 | "InvokeCradleCrafter\invoke-cradlecrafter_random_77.ps1","1" 178 | "InvokeCradleCrafter\invoke-cradlecrafter_random_78.ps1","1" 179 | "InvokeCradleCrafter\invoke-cradlecrafter_random_79.ps1","1" 180 | "InvokeCradleCrafter\invoke-cradlecrafter_random_8.ps1","1" 181 | "InvokeCradleCrafter\invoke-cradlecrafter_random_80.ps1","1" 182 | "InvokeCradleCrafter\invoke-cradlecrafter_random_81.ps1","1" 183 | "InvokeCradleCrafter\invoke-cradlecrafter_random_82.ps1","1" 184 | "InvokeCradleCrafter\invoke-cradlecrafter_random_83.ps1","1" 185 | "InvokeCradleCrafter\invoke-cradlecrafter_random_84.ps1","1" 186 | "InvokeCradleCrafter\invoke-cradlecrafter_random_85.ps1","1" 187 | "InvokeCradleCrafter\invoke-cradlecrafter_random_86.ps1","1" 188 | "InvokeCradleCrafter\invoke-cradlecrafter_random_87.ps1","1" 189 | "InvokeCradleCrafter\invoke-cradlecrafter_random_88.ps1","1" 190 | "InvokeCradleCrafter\invoke-cradlecrafter_random_89.ps1","1" 191 | "InvokeCradleCrafter\invoke-cradlecrafter_random_9.ps1","1" 192 | "InvokeCradleCrafter\invoke-cradlecrafter_random_90.ps1","1" 193 | "InvokeCradleCrafter\invoke-cradlecrafter_random_91.ps1","1" 194 | "InvokeCradleCrafter\invoke-cradlecrafter_random_92.ps1","1" 195 | "InvokeCradleCrafter\invoke-cradlecrafter_random_93.ps1","1" 196 | "InvokeCradleCrafter\invoke-cradlecrafter_random_94.ps1","1" 197 | "InvokeCradleCrafter\invoke-cradlecrafter_random_95.ps1","1" 198 | "InvokeCradleCrafter\invoke-cradlecrafter_random_96.ps1","1" 199 | "InvokeCradleCrafter\invoke-cradlecrafter_random_97.ps1","1" 200 | "InvokeCradleCrafter\invoke-cradlecrafter_random_98.ps1","1" 201 | "InvokeCradleCrafter\invoke-cradlecrafter_random_99.ps1","1" 202 | -------------------------------------------------------------------------------- /DataScience/ModelTrainer/ModelTrainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace LogisticGradient 5 | { 6 | class ModelTrainer 7 | { 8 | static void Main (string[] args) 9 | { 10 | Console.WriteLine ("\nBegin Logistic Regression (binary) Classification"); 11 | 12 | double[][] allData = ReadData (args[0]); 13 | 14 | Console.WriteLine ("Creating train (50%) and test (50%) matrices"); 15 | double[][] trainData; 16 | double[][] testData; 17 | MakeTrainTest (allData, 0, out trainData, out testData); 18 | Console.WriteLine ("Done"); 19 | 20 | Console.WriteLine ("Creating LR binary classifier"); 21 | int numFeatures = allData[0].Length - 1; 22 | LogisticClassifier lc = new LogisticClassifier (numFeatures); 23 | 24 | double alpha = 0.03; 25 | if(args.Length > 1) 26 | { 27 | alpha = Double.Parse(args[1]); 28 | } 29 | Console.WriteLine ("Setting learning rate = " + alpha.ToString ("F2")); 30 | 31 | int maxEpochs = 5000; 32 | if(args.Length > 2) 33 | { 34 | maxEpochs = Int32.Parse(args[2]); 35 | } 36 | Console.WriteLine ("Setting maxEpochs = " + maxEpochs); 37 | 38 | Console.WriteLine ("\nStarting training using (stochastic) gradient descent"); 39 | double[] weights = lc.Train (trainData, maxEpochs, alpha); 40 | Console.WriteLine ("Training complete"); 41 | 42 | Console.WriteLine ("\nBest weights found:"); 43 | ShowVector (weights, 4, true); 44 | 45 | TrainResult trainAcc = lc.Accuracy (trainData, weights); 46 | Console.WriteLine ("Accuracy / False Positive rate on training data = " + 47 | trainAcc.Accuracy.ToString ("F4") + " / " + trainAcc.FalsePositiveRate.ToString("F4")); 48 | 49 | TrainResult testAcc = lc.Accuracy (testData, weights); 50 | Console.WriteLine ("Test data:"); 51 | 52 | Console.WriteLine("Accuracy: " + testAcc.Accuracy.ToString("F4")); 53 | Console.WriteLine("Precision: " + testAcc.Precision.ToString("F4")); 54 | Console.WriteLine("Recall: " + testAcc.Recall.ToString("F4")); 55 | Console.WriteLine("F1Score: " + testAcc.F1Score.ToString("F4")); 56 | 57 | Console.WriteLine("TruePositiveRate: " + testAcc.TruePositiveRate.ToString("F4")); 58 | Console.WriteLine("FalsePositiveRate: " + testAcc.FalsePositiveRate.ToString("F4")); 59 | Console.WriteLine("TrueNegativeRate: " + testAcc.TrueNegativeRate.ToString("F4")); 60 | Console.WriteLine("FalseNegativeRate: " + testAcc.FalseNegativeRate.ToString("F4")); 61 | } 62 | 63 | static double[][] ReadData (string path) 64 | { 65 | string[] fileContent = File.ReadAllLines (path); 66 | double[][] result = new double[fileContent.Length][]; 67 | 68 | double[] values = null; 69 | int dataWidth = 0; 70 | 71 | for (int dataRow = 0; dataRow < fileContent.Length; dataRow++) 72 | { 73 | string[] dataElements = fileContent[dataRow].Split (','); 74 | 75 | if(dataRow == 0) 76 | { 77 | values = new double[dataElements.Length]; 78 | dataWidth = dataElements.Length; 79 | } 80 | else 81 | { 82 | values = new double[result[0].Length]; 83 | } 84 | 85 | if(dataElements.Length == dataWidth) 86 | { 87 | for (int dataElement = 0; dataElement < dataElements.Length; dataElement++) 88 | { 89 | string elementValue = dataElements[dataElement].Replace ("\"", ""); 90 | 91 | Double elementDoubleValue; 92 | if (!Double.TryParse (elementValue, out elementDoubleValue)) 93 | { 94 | Console.WriteLine("Could not convert " + elementValue + " on row " + dataRow + " to a Double"); 95 | elementDoubleValue = 0; 96 | } 97 | 98 | values[dataElement] = elementDoubleValue; 99 | } 100 | } 101 | 102 | result[dataRow] = values; 103 | } 104 | 105 | return result; 106 | } 107 | 108 | static void MakeTrainTest (double[][] allData, int seed, out double[][] trainData, out double[][] testData) 109 | { 110 | Random rnd = new Random (seed); 111 | int totRows = allData.Length; 112 | 113 | // Train on 50% 114 | int numTrainRows = (int) (totRows * 0.8); 115 | int numTestRows = totRows - numTrainRows; 116 | trainData = new double[numTrainRows][]; 117 | testData = new double[numTestRows][]; 118 | 119 | for (int i = 0; i < numTrainRows; ++i) 120 | trainData[i] = allData[i]; 121 | 122 | for (int i = 0; i < numTestRows; ++i) 123 | testData[i] = allData[i + numTrainRows]; 124 | } 125 | 126 | static void ShowVector (double[] vector, int decimals, bool newLine) 127 | { 128 | for (int i = 0; i < vector.Length; ++i) 129 | Console.Write (vector[i].ToString ("F" + decimals) + " "); 130 | Console.WriteLine (""); 131 | if (newLine == true) 132 | Console.WriteLine (""); 133 | } 134 | } 135 | 136 | public class LogisticClassifier 137 | { 138 | private int numFeatures; 139 | 140 | // First index is the bias / constant weight 141 | private double[] weights; 142 | private Random rnd; 143 | 144 | public LogisticClassifier (int numFeatures) 145 | { 146 | this.numFeatures = numFeatures; 147 | this.rnd = new Random (0); 148 | this.weights = new double[numFeatures + 1]; 149 | } 150 | 151 | public double[] Train (double[][] trainData, int maxEpochs, double alpha) 152 | { 153 | // alpha is the learning rate 154 | int epoch = 0; 155 | 156 | // The order in which we will process training data, constantly shuffled 157 | int[] sequence = new int[trainData.Length]; 158 | for (int i = 0; i < sequence.Length; ++i) 159 | sequence[i] = i; 160 | 161 | while (epoch < maxEpochs) 162 | { 163 | ++epoch; 164 | 165 | if (epoch % 500 == 0 && epoch != maxEpochs) 166 | { 167 | double mse = Error(trainData, weights).ErrorRate; 168 | Console.Write ("epoch = " + epoch); 169 | Console.WriteLine (" error = " + mse.ToString ("F4")); 170 | } 171 | 172 | // Randomize every epoch 173 | Shuffle (sequence); 174 | 175 | // stochastic/online/incremental approach 176 | for (int ti = 0; ti < trainData.Length; ++ti) 177 | { 178 | int i = sequence[ti]; 179 | double computed = ComputeOutput (trainData[i], weights); 180 | int targetIndex = trainData[i].Length - 1; 181 | double target = trainData[i][targetIndex]; 182 | 183 | weights[0] += alpha * (target - computed) * 1; 184 | 185 | for (int j = 1; j < weights.Length; ++j) 186 | { 187 | weights[j] += alpha * (target - computed) * trainData[i][j - 1]; 188 | } 189 | } 190 | 191 | } 192 | 193 | return this.weights; 194 | } 195 | 196 | private void Shuffle (int[] sequence) 197 | { 198 | for (int i = 0; i < sequence.Length; ++i) 199 | { 200 | int r = rnd.Next (i, sequence.Length); 201 | int tmp = sequence[r]; 202 | sequence[r] = sequence[i]; 203 | sequence[i] = tmp; 204 | } 205 | } 206 | 207 | private ErrorResult Error (double[][] trainData, double[] weights) 208 | { 209 | ErrorResult output = new ErrorResult(); 210 | 211 | // mean squared error using supplied weights 212 | // y-value / label (0/1) is last column 213 | int yIndex = trainData[0].Length - 1; 214 | double sumSquaredError = 0.0; 215 | double falsePositiveCount = 0.0; 216 | 217 | for (int i = 0; i < trainData.Length; ++i) 218 | { 219 | double computed = ComputeOutput (trainData[i], weights); 220 | double desired = trainData[i][yIndex]; 221 | sumSquaredError += (computed - desired) * (computed - desired); 222 | 223 | if((computed > 0.5) && (desired < 0.5)) 224 | { 225 | falsePositiveCount++; 226 | } 227 | } 228 | 229 | output.ErrorRate = sumSquaredError / trainData.Length; 230 | output.FalsePositiveRate = falsePositiveCount / trainData.Length; 231 | return output; 232 | } 233 | 234 | private double ComputeOutput (double[] dataItem, double[] weights) 235 | { 236 | double z = 0.0; 237 | 238 | // first element is the bias constant 239 | z += weights[0]; 240 | 241 | // Go to lenghth - 1 because the final element is the label 242 | for (int i = 0; i < weights.Length - 1; ++i) 243 | z += (weights[i + 1] * dataItem[i]); 244 | 245 | return 1.0 / (1.0 + Math.Exp (-z)); 246 | } 247 | 248 | private int ComputeDependent (double[] dataItem, double[] weights) 249 | { 250 | double y = ComputeOutput (dataItem, weights); 251 | 252 | if (y <= 0.5) 253 | return 0; 254 | else 255 | return 1; 256 | } 257 | 258 | public TrainResult Accuracy (double[][] dataSet, double[] weights) 259 | { 260 | TrainResult output = new TrainResult(); 261 | 262 | int truePositives = 0; 263 | int trueNegatives = 0; 264 | int falsePositives = 0; 265 | int falseNegatives = 0; 266 | 267 | int yIndex = dataSet[0].Length - 1; 268 | for (int i = 0; i < dataSet.Length; ++i) 269 | { 270 | if(dataSet[i].Length != weights.Length) 271 | { 272 | continue; 273 | } 274 | 275 | int computed = ComputeDependent (dataSet[i], weights); 276 | int target = (int) dataSet[i][yIndex]; 277 | 278 | if(target == 0) 279 | { 280 | if(computed < 0.5) 281 | { 282 | trueNegatives++; 283 | } 284 | else 285 | { 286 | falsePositives++; 287 | } 288 | } 289 | else 290 | { 291 | if(computed < 0.5) 292 | { 293 | falseNegatives++; 294 | } 295 | else 296 | { 297 | truePositives++; 298 | } 299 | } 300 | } 301 | 302 | output.Accuracy = (truePositives * 1.0 + trueNegatives) / (truePositives + trueNegatives + falsePositives + falseNegatives); 303 | output.Precision = (truePositives * 1.0) / (truePositives + falsePositives); 304 | output.Recall = (truePositives * 1.0) / (truePositives + falseNegatives); 305 | output.F1Score = (2.0 * output.Precision * output.Recall) / (output.Precision + output.Recall); 306 | 307 | output.TruePositiveRate = (truePositives * 1.0) / (truePositives + trueNegatives + falsePositives + falseNegatives); 308 | output.FalsePositiveRate = (falsePositives * 1.0) / (truePositives + trueNegatives + falsePositives + falseNegatives); 309 | output.TrueNegativeRate = (trueNegatives * 1.0) / (truePositives + trueNegatives + falsePositives + falseNegatives); 310 | output.FalseNegativeRate = (falseNegatives * 1.0) / (truePositives + trueNegatives + falsePositives + falseNegatives); 311 | 312 | return output; 313 | } 314 | } 315 | 316 | internal class ErrorResult 317 | { 318 | public double ErrorRate { get; set; } 319 | public double FalsePositiveRate { get; set; } 320 | } 321 | 322 | public class TrainResult 323 | { 324 | public double Accuracy { get; set; } 325 | public double Precision { get; set; } 326 | public double Recall { get; set; } 327 | public double F1Score { get; set; } 328 | 329 | public double TruePositiveRate { get; set; } 330 | public double FalsePositiveRate { get; set; } 331 | public double TrueNegativeRate { get; set; } 332 | public double FalseNegativeRate { get; set; } 333 | } 334 | } -------------------------------------------------------------------------------- /DataScience/UnderhandedPowerShell-obfuscation-labeledData.csv: -------------------------------------------------------------------------------- 1 | "Path","Label" 2 | "UnderhandedPowerShell\UnderhandedPowerShell_000.ps1","1" 3 | "UnderhandedPowerShell\UnderhandedPowerShell_001.ps1","1" 4 | "UnderhandedPowerShell\UnderhandedPowerShell_002.ps1","1" 5 | "UnderhandedPowerShell\UnderhandedPowerShell_003.ps1","1" 6 | "UnderhandedPowerShell\UnderhandedPowerShell_004.ps1","1" 7 | "UnderhandedPowerShell\UnderhandedPowerShell_005.ps1","1" 8 | "UnderhandedPowerShell\UnderhandedPowerShell_006.ps1","1" 9 | "UnderhandedPowerShell\UnderhandedPowerShell_007.ps1","1" 10 | "UnderhandedPowerShell\UnderhandedPowerShell_008.ps1","1" 11 | "UnderhandedPowerShell\UnderhandedPowerShell_009.ps1","1" 12 | "UnderhandedPowerShell\UnderhandedPowerShell_010.ps1","1" 13 | "UnderhandedPowerShell\UnderhandedPowerShell_011.ps1","1" 14 | "UnderhandedPowerShell\UnderhandedPowerShell_012.ps1","1" 15 | "UnderhandedPowerShell\UnderhandedPowerShell_013.ps1","1" 16 | "UnderhandedPowerShell\UnderhandedPowerShell_014.ps1","1" 17 | "UnderhandedPowerShell\UnderhandedPowerShell_015.ps1","1" 18 | "UnderhandedPowerShell\UnderhandedPowerShell_016.ps1","1" 19 | "UnderhandedPowerShell\UnderhandedPowerShell_017.ps1","1" 20 | "UnderhandedPowerShell\UnderhandedPowerShell_018.ps1","1" 21 | "UnderhandedPowerShell\UnderhandedPowerShell_019.ps1","1" 22 | "UnderhandedPowerShell\UnderhandedPowerShell_020.ps1","1" 23 | "UnderhandedPowerShell\UnderhandedPowerShell_021.ps1","1" 24 | "UnderhandedPowerShell\UnderhandedPowerShell_022.ps1","1" 25 | "UnderhandedPowerShell\UnderhandedPowerShell_023.ps1","1" 26 | "UnderhandedPowerShell\UnderhandedPowerShell_024.ps1","1" 27 | "UnderhandedPowerShell\UnderhandedPowerShell_025.ps1","1" 28 | "UnderhandedPowerShell\UnderhandedPowerShell_026.ps1","1" 29 | "UnderhandedPowerShell\UnderhandedPowerShell_027.ps1","1" 30 | "UnderhandedPowerShell\UnderhandedPowerShell_028.ps1","1" 31 | "UnderhandedPowerShell\UnderhandedPowerShell_029.ps1","1" 32 | "UnderhandedPowerShell\UnderhandedPowerShell_030.ps1","1" 33 | "UnderhandedPowerShell\UnderhandedPowerShell_031.ps1","1" 34 | "UnderhandedPowerShell\UnderhandedPowerShell_032.ps1","1" 35 | "UnderhandedPowerShell\UnderhandedPowerShell_033.ps1","1" 36 | "UnderhandedPowerShell\UnderhandedPowerShell_034.ps1","1" 37 | "UnderhandedPowerShell\UnderhandedPowerShell_035.ps1","1" 38 | "UnderhandedPowerShell\UnderhandedPowerShell_036.ps1","1" 39 | "UnderhandedPowerShell\UnderhandedPowerShell_037.ps1","1" 40 | "UnderhandedPowerShell\UnderhandedPowerShell_038.ps1","1" 41 | "UnderhandedPowerShell\UnderhandedPowerShell_039.ps1","1" 42 | "UnderhandedPowerShell\UnderhandedPowerShell_040.ps1","1" 43 | "UnderhandedPowerShell\UnderhandedPowerShell_041.ps1","1" 44 | "UnderhandedPowerShell\UnderhandedPowerShell_042.ps1","1" 45 | "UnderhandedPowerShell\UnderhandedPowerShell_043.ps1","1" 46 | "UnderhandedPowerShell\UnderhandedPowerShell_044.ps1","1" 47 | "UnderhandedPowerShell\UnderhandedPowerShell_045.ps1","1" 48 | "UnderhandedPowerShell\UnderhandedPowerShell_046.ps1","1" 49 | "UnderhandedPowerShell\UnderhandedPowerShell_047.ps1","1" 50 | "UnderhandedPowerShell\UnderhandedPowerShell_048.ps1","1" 51 | "UnderhandedPowerShell\UnderhandedPowerShell_049.ps1","1" 52 | "UnderhandedPowerShell\UnderhandedPowerShell_050.ps1","1" 53 | "UnderhandedPowerShell\UnderhandedPowerShell_051.ps1","1" 54 | "UnderhandedPowerShell\UnderhandedPowerShell_052.ps1","1" 55 | "UnderhandedPowerShell\UnderhandedPowerShell_053.ps1","1" 56 | "UnderhandedPowerShell\UnderhandedPowerShell_054.ps1","1" 57 | "UnderhandedPowerShell\UnderhandedPowerShell_055.ps1","1" 58 | "UnderhandedPowerShell\UnderhandedPowerShell_056.ps1","1" 59 | "UnderhandedPowerShell\UnderhandedPowerShell_057.ps1","1" 60 | "UnderhandedPowerShell\UnderhandedPowerShell_058.ps1","1" 61 | "UnderhandedPowerShell\UnderhandedPowerShell_059.ps1","1" 62 | "UnderhandedPowerShell\UnderhandedPowerShell_060.ps1","1" 63 | "UnderhandedPowerShell\UnderhandedPowerShell_061.ps1","1" 64 | "UnderhandedPowerShell\UnderhandedPowerShell_062.ps1","1" 65 | "UnderhandedPowerShell\UnderhandedPowerShell_063.ps1","1" 66 | "UnderhandedPowerShell\UnderhandedPowerShell_064.ps1","1" 67 | "UnderhandedPowerShell\UnderhandedPowerShell_065.ps1","1" 68 | "UnderhandedPowerShell\UnderhandedPowerShell_066.ps1","1" 69 | "UnderhandedPowerShell\UnderhandedPowerShell_067.ps1","1" 70 | "UnderhandedPowerShell\UnderhandedPowerShell_068.ps1","1" 71 | "UnderhandedPowerShell\UnderhandedPowerShell_069.ps1","1" 72 | "UnderhandedPowerShell\UnderhandedPowerShell_070.ps1","1" 73 | "UnderhandedPowerShell\UnderhandedPowerShell_071.ps1","1" 74 | "UnderhandedPowerShell\UnderhandedPowerShell_072.ps1","1" 75 | "UnderhandedPowerShell\UnderhandedPowerShell_073.ps1","1" 76 | "UnderhandedPowerShell\UnderhandedPowerShell_074.ps1","1" 77 | "UnderhandedPowerShell\UnderhandedPowerShell_075.ps1","1" 78 | "UnderhandedPowerShell\UnderhandedPowerShell_076.ps1","1" 79 | "UnderhandedPowerShell\UnderhandedPowerShell_077.ps1","1" 80 | "UnderhandedPowerShell\UnderhandedPowerShell_078.ps1","1" 81 | "UnderhandedPowerShell\UnderhandedPowerShell_079.ps1","1" 82 | "UnderhandedPowerShell\UnderhandedPowerShell_080.ps1","1" 83 | "UnderhandedPowerShell\UnderhandedPowerShell_081.ps1","1" 84 | "UnderhandedPowerShell\UnderhandedPowerShell_082.ps1","1" 85 | "UnderhandedPowerShell\UnderhandedPowerShell_083.ps1","1" 86 | "UnderhandedPowerShell\UnderhandedPowerShell_084.ps1","1" 87 | "UnderhandedPowerShell\UnderhandedPowerShell_085.ps1","1" 88 | "UnderhandedPowerShell\UnderhandedPowerShell_086.ps1","1" 89 | "UnderhandedPowerShell\UnderhandedPowerShell_087.ps1","1" 90 | "UnderhandedPowerShell\UnderhandedPowerShell_088.ps1","1" 91 | "UnderhandedPowerShell\UnderhandedPowerShell_089.ps1","1" 92 | "UnderhandedPowerShell\UnderhandedPowerShell_090.ps1","1" 93 | "UnderhandedPowerShell\UnderhandedPowerShell_091.ps1","1" 94 | "UnderhandedPowerShell\UnderhandedPowerShell_092.ps1","1" 95 | "UnderhandedPowerShell\UnderhandedPowerShell_093.ps1","1" 96 | "UnderhandedPowerShell\UnderhandedPowerShell_094.ps1","1" 97 | "UnderhandedPowerShell\UnderhandedPowerShell_095.ps1","1" 98 | "UnderhandedPowerShell\UnderhandedPowerShell_096.ps1","1" 99 | "UnderhandedPowerShell\UnderhandedPowerShell_097.ps1","1" 100 | "UnderhandedPowerShell\UnderhandedPowerShell_098.ps1","1" 101 | "UnderhandedPowerShell\UnderhandedPowerShell_099.ps1","1" 102 | "UnderhandedPowerShell\UnderhandedPowerShell_100.ps1","1" 103 | "UnderhandedPowerShell\UnderhandedPowerShell_101.ps1","1" 104 | "UnderhandedPowerShell\UnderhandedPowerShell_102.ps1","1" 105 | "UnderhandedPowerShell\UnderhandedPowerShell_103.ps1","1" 106 | "UnderhandedPowerShell\UnderhandedPowerShell_104.ps1","1" 107 | "UnderhandedPowerShell\UnderhandedPowerShell_105.ps1","1" 108 | "UnderhandedPowerShell\UnderhandedPowerShell_106.ps1","1" 109 | "UnderhandedPowerShell\UnderhandedPowerShell_107.ps1","1" 110 | "UnderhandedPowerShell\UnderhandedPowerShell_108.ps1","1" 111 | "UnderhandedPowerShell\UnderhandedPowerShell_109.ps1","1" 112 | "UnderhandedPowerShell\UnderhandedPowerShell_110.ps1","1" 113 | "UnderhandedPowerShell\UnderhandedPowerShell_111.ps1","1" 114 | "UnderhandedPowerShell\UnderhandedPowerShell_112.ps1","1" 115 | "UnderhandedPowerShell\UnderhandedPowerShell_113.ps1","1" 116 | "UnderhandedPowerShell\UnderhandedPowerShell_114.ps1","1" 117 | "UnderhandedPowerShell\UnderhandedPowerShell_115.ps1","1" 118 | "UnderhandedPowerShell\UnderhandedPowerShell_116.ps1","1" 119 | "UnderhandedPowerShell\UnderhandedPowerShell_117.ps1","1" 120 | "UnderhandedPowerShell\UnderhandedPowerShell_118.ps1","1" 121 | "UnderhandedPowerShell\UnderhandedPowerShell_119.ps1","1" 122 | "UnderhandedPowerShell\UnderhandedPowerShell_120.ps1","1" 123 | "UnderhandedPowerShell\UnderhandedPowerShell_121.ps1","1" 124 | "UnderhandedPowerShell\UnderhandedPowerShell_122.ps1","1" 125 | "UnderhandedPowerShell\UnderhandedPowerShell_123.ps1","1" 126 | "UnderhandedPowerShell\UnderhandedPowerShell_124.ps1","1" 127 | "UnderhandedPowerShell\UnderhandedPowerShell_125.ps1","1" 128 | "UnderhandedPowerShell\UnderhandedPowerShell_126.ps1","1" 129 | "UnderhandedPowerShell\UnderhandedPowerShell_127.ps1","1" 130 | "UnderhandedPowerShell\UnderhandedPowerShell_128.ps1","1" 131 | "UnderhandedPowerShell\UnderhandedPowerShell_129.ps1","1" 132 | "UnderhandedPowerShell\UnderhandedPowerShell_130.ps1","1" 133 | "UnderhandedPowerShell\UnderhandedPowerShell_131.ps1","1" 134 | "UnderhandedPowerShell\UnderhandedPowerShell_132.ps1","1" 135 | "UnderhandedPowerShell\UnderhandedPowerShell_133.ps1","1" 136 | "UnderhandedPowerShell\UnderhandedPowerShell_134.ps1","1" 137 | "UnderhandedPowerShell\UnderhandedPowerShell_135.ps1","1" 138 | "UnderhandedPowerShell\UnderhandedPowerShell_136.ps1","1" 139 | "UnderhandedPowerShell\UnderhandedPowerShell_137.ps1","1" 140 | "UnderhandedPowerShell\UnderhandedPowerShell_138.ps1","1" 141 | "UnderhandedPowerShell\UnderhandedPowerShell_139.ps1","1" 142 | "UnderhandedPowerShell\UnderhandedPowerShell_140.ps1","1" 143 | "UnderhandedPowerShell\UnderhandedPowerShell_141.ps1","1" 144 | "UnderhandedPowerShell\UnderhandedPowerShell_142.ps1","1" 145 | "UnderhandedPowerShell\UnderhandedPowerShell_143.ps1","1" 146 | "UnderhandedPowerShell\UnderhandedPowerShell_144.ps1","1" 147 | "UnderhandedPowerShell\UnderhandedPowerShell_145.ps1","1" 148 | "UnderhandedPowerShell\UnderhandedPowerShell_146.ps1","1" 149 | "UnderhandedPowerShell\UnderhandedPowerShell_147.ps1","1" 150 | "UnderhandedPowerShell\UnderhandedPowerShell_148.ps1","1" 151 | "UnderhandedPowerShell\UnderhandedPowerShell_149.ps1","1" 152 | "UnderhandedPowerShell\UnderhandedPowerShell_150.ps1","1" 153 | "UnderhandedPowerShell\UnderhandedPowerShell_151.ps1","1" 154 | "UnderhandedPowerShell\UnderhandedPowerShell_152.ps1","1" 155 | "UnderhandedPowerShell\UnderhandedPowerShell_153.ps1","1" 156 | "UnderhandedPowerShell\UnderhandedPowerShell_154.ps1","1" 157 | "UnderhandedPowerShell\UnderhandedPowerShell_155.ps1","1" 158 | "UnderhandedPowerShell\UnderhandedPowerShell_156.ps1","1" 159 | "UnderhandedPowerShell\UnderhandedPowerShell_157.ps1","1" 160 | "UnderhandedPowerShell\UnderhandedPowerShell_158.ps1","1" 161 | "UnderhandedPowerShell\UnderhandedPowerShell_159.ps1","1" 162 | "UnderhandedPowerShell\UnderhandedPowerShell_160.ps1","1" 163 | "UnderhandedPowerShell\UnderhandedPowerShell_161.ps1","1" 164 | "UnderhandedPowerShell\UnderhandedPowerShell_162.ps1","1" 165 | "UnderhandedPowerShell\UnderhandedPowerShell_163.ps1","1" 166 | "UnderhandedPowerShell\UnderhandedPowerShell_164.ps1","1" 167 | "UnderhandedPowerShell\UnderhandedPowerShell_165.ps1","1" 168 | "UnderhandedPowerShell\UnderhandedPowerShell_166.ps1","1" 169 | "UnderhandedPowerShell\UnderhandedPowerShell_167.ps1","1" 170 | "UnderhandedPowerShell\UnderhandedPowerShell_168.ps1","1" 171 | "UnderhandedPowerShell\UnderhandedPowerShell_169.ps1","1" 172 | "UnderhandedPowerShell\UnderhandedPowerShell_170.ps1","1" 173 | "UnderhandedPowerShell\UnderhandedPowerShell_171.ps1","1" 174 | "UnderhandedPowerShell\UnderhandedPowerShell_172.ps1","1" 175 | "UnderhandedPowerShell\UnderhandedPowerShell_173.ps1","1" 176 | "UnderhandedPowerShell\UnderhandedPowerShell_174.ps1","1" 177 | "UnderhandedPowerShell\UnderhandedPowerShell_175.ps1","1" 178 | "UnderhandedPowerShell\UnderhandedPowerShell_176.ps1","1" 179 | "UnderhandedPowerShell\UnderhandedPowerShell_177.ps1","1" 180 | "UnderhandedPowerShell\UnderhandedPowerShell_178.ps1","1" 181 | "UnderhandedPowerShell\UnderhandedPowerShell_179.ps1","1" 182 | "UnderhandedPowerShell\UnderhandedPowerShell_180.ps1","1" 183 | "UnderhandedPowerShell\UnderhandedPowerShell_181.ps1","1" 184 | "UnderhandedPowerShell\UnderhandedPowerShell_182.ps1","1" 185 | "UnderhandedPowerShell\UnderhandedPowerShell_183.ps1","1" 186 | "UnderhandedPowerShell\UnderhandedPowerShell_184.ps1","1" 187 | "UnderhandedPowerShell\UnderhandedPowerShell_185.ps1","1" 188 | "UnderhandedPowerShell\UnderhandedPowerShell_186.ps1","1" 189 | "UnderhandedPowerShell\UnderhandedPowerShell_187.ps1","1" 190 | "UnderhandedPowerShell\UnderhandedPowerShell_188.ps1","1" 191 | "UnderhandedPowerShell\UnderhandedPowerShell_189.ps1","1" 192 | "UnderhandedPowerShell\UnderhandedPowerShell_190.ps1","1" 193 | "UnderhandedPowerShell\UnderhandedPowerShell_191.ps1","1" 194 | "UnderhandedPowerShell\UnderhandedPowerShell_192.ps1","1" 195 | "UnderhandedPowerShell\UnderhandedPowerShell_193.ps1","1" 196 | "UnderhandedPowerShell\UnderhandedPowerShell_194.ps1","1" 197 | "UnderhandedPowerShell\UnderhandedPowerShell_195.ps1","1" 198 | "UnderhandedPowerShell\UnderhandedPowerShell_196.ps1","1" 199 | "UnderhandedPowerShell\UnderhandedPowerShell_197.ps1","1" 200 | "UnderhandedPowerShell\UnderhandedPowerShell_198.ps1","1" 201 | "UnderhandedPowerShell\UnderhandedPowerShell_199.ps1","1" 202 | "UnderhandedPowerShell\UnderhandedPowerShell_200.ps1","1" 203 | "UnderhandedPowerShell\UnderhandedPowerShell_201.ps1","1" 204 | "UnderhandedPowerShell\UnderhandedPowerShell_202.ps1","1" 205 | "UnderhandedPowerShell\UnderhandedPowerShell_203.ps1","1" 206 | "UnderhandedPowerShell\UnderhandedPowerShell_204.ps1","1" 207 | "UnderhandedPowerShell\UnderhandedPowerShell_205.ps1","1" 208 | "UnderhandedPowerShell\UnderhandedPowerShell_206.ps1","1" 209 | "UnderhandedPowerShell\UnderhandedPowerShell_207.ps1","1" 210 | "UnderhandedPowerShell\UnderhandedPowerShell_208.ps1","1" 211 | "UnderhandedPowerShell\UnderhandedPowerShell_209.ps1","1" 212 | "UnderhandedPowerShell\UnderhandedPowerShell_210.ps1","1" 213 | "UnderhandedPowerShell\UnderhandedPowerShell_211.ps1","1" 214 | "UnderhandedPowerShell\UnderhandedPowerShell_212.ps1","1" 215 | "UnderhandedPowerShell\UnderhandedPowerShell_213.ps1","1" 216 | "UnderhandedPowerShell\UnderhandedPowerShell_214.ps1","1" 217 | "UnderhandedPowerShell\UnderhandedPowerShell_215.ps1","1" 218 | "UnderhandedPowerShell\UnderhandedPowerShell_216.ps1","1" 219 | "UnderhandedPowerShell\UnderhandedPowerShell_217.ps1","1" 220 | "UnderhandedPowerShell\UnderhandedPowerShell_218.ps1","1" 221 | "UnderhandedPowerShell\UnderhandedPowerShell_219.ps1","1" 222 | "UnderhandedPowerShell\UnderhandedPowerShell_220.ps1","1" 223 | "UnderhandedPowerShell\UnderhandedPowerShell_221.ps1","1" 224 | "UnderhandedPowerShell\UnderhandedPowerShell_222.ps1","1" 225 | "UnderhandedPowerShell\UnderhandedPowerShell_223.ps1","1" 226 | "UnderhandedPowerShell\UnderhandedPowerShell_224.ps1","1" 227 | "UnderhandedPowerShell\UnderhandedPowerShell_225.ps1","1" 228 | "UnderhandedPowerShell\UnderhandedPowerShell_226.ps1","1" 229 | "UnderhandedPowerShell\UnderhandedPowerShell_227.ps1","1" 230 | "UnderhandedPowerShell\UnderhandedPowerShell_228.ps1","1" 231 | "UnderhandedPowerShell\UnderhandedPowerShell_229.ps1","1" 232 | "UnderhandedPowerShell\UnderhandedPowerShell_230.ps1","1" 233 | "UnderhandedPowerShell\UnderhandedPowerShell_231.ps1","1" 234 | "UnderhandedPowerShell\UnderhandedPowerShell_232.ps1","1" 235 | "UnderhandedPowerShell\UnderhandedPowerShell_233.ps1","1" 236 | "UnderhandedPowerShell\UnderhandedPowerShell_234.ps1","1" 237 | "UnderhandedPowerShell\UnderhandedPowerShell_235.ps1","1" 238 | "UnderhandedPowerShell\UnderhandedPowerShell_236.ps1","1" 239 | "UnderhandedPowerShell\UnderhandedPowerShell_237.ps1","1" 240 | "UnderhandedPowerShell\UnderhandedPowerShell_238.ps1","1" 241 | "UnderhandedPowerShell\UnderhandedPowerShell_239.ps1","1" 242 | "UnderhandedPowerShell\UnderhandedPowerShell_240.ps1","1" 243 | "UnderhandedPowerShell\UnderhandedPowerShell_241.ps1","1" 244 | "UnderhandedPowerShell\UnderhandedPowerShell_242.ps1","1" 245 | "UnderhandedPowerShell\UnderhandedPowerShell_243.ps1","1" 246 | "UnderhandedPowerShell\UnderhandedPowerShell_244.ps1","1" 247 | "UnderhandedPowerShell\UnderhandedPowerShell_245.ps1","1" 248 | "UnderhandedPowerShell\UnderhandedPowerShell_246.ps1","1" 249 | "UnderhandedPowerShell\UnderhandedPowerShell_247.ps1","1" 250 | "UnderhandedPowerShell\UnderhandedPowerShell_248.ps1","1" 251 | "UnderhandedPowerShell\UnderhandedPowerShell_249.ps1","1" 252 | "UnderhandedPowerShell\UnderhandedPowerShell_250.ps1","1" 253 | "UnderhandedPowerShell\UnderhandedPowerShell_251.ps1","1" 254 | "UnderhandedPowerShell\UnderhandedPowerShell_252.ps1","1" 255 | "UnderhandedPowerShell\UnderhandedPowerShell_253.ps1","1" 256 | "UnderhandedPowerShell\UnderhandedPowerShell_254.ps1","1" 257 | "UnderhandedPowerShell\UnderhandedPowerShell_255.ps1","1" 258 | "UnderhandedPowerShell\UnderhandedPowerShell_256.ps1","1" 259 | "UnderhandedPowerShell\UnderhandedPowerShell_257.ps1","1" 260 | "UnderhandedPowerShell\UnderhandedPowerShell_258.ps1","1" 261 | "UnderhandedPowerShell\UnderhandedPowerShell_259.ps1","1" 262 | "UnderhandedPowerShell\UnderhandedPowerShell_260.ps1","1" 263 | "UnderhandedPowerShell\UnderhandedPowerShell_261.ps1","1" 264 | "UnderhandedPowerShell\UnderhandedPowerShell_262.ps1","1" 265 | "UnderhandedPowerShell\UnderhandedPowerShell_263.ps1","1" 266 | "UnderhandedPowerShell\UnderhandedPowerShell_264.ps1","1" 267 | "UnderhandedPowerShell\UnderhandedPowerShell_265.ps1","1" 268 | "UnderhandedPowerShell\UnderhandedPowerShell_266.ps1","1" 269 | "UnderhandedPowerShell\UnderhandedPowerShell_267.ps1","1" 270 | "UnderhandedPowerShell\UnderhandedPowerShell_268.ps1","1" 271 | "UnderhandedPowerShell\UnderhandedPowerShell_269.ps1","1" 272 | "UnderhandedPowerShell\UnderhandedPowerShell_270.ps1","1" 273 | "UnderhandedPowerShell\UnderhandedPowerShell_271.ps1","1" 274 | "UnderhandedPowerShell\UnderhandedPowerShell_272.ps1","1" 275 | "UnderhandedPowerShell\UnderhandedPowerShell_273.ps1","1" 276 | "UnderhandedPowerShell\UnderhandedPowerShell_274.ps1","1" 277 | "UnderhandedPowerShell\UnderhandedPowerShell_275.ps1","1" 278 | "UnderhandedPowerShell\UnderhandedPowerShell_276.ps1","1" 279 | "UnderhandedPowerShell\UnderhandedPowerShell_277.ps1","1" 280 | "UnderhandedPowerShell\UnderhandedPowerShell_278.ps1","1" 281 | "UnderhandedPowerShell\UnderhandedPowerShell_279.ps1","1" 282 | "UnderhandedPowerShell\UnderhandedPowerShell_280.ps1","1" 283 | "UnderhandedPowerShell\UnderhandedPowerShell_281.ps1","1" 284 | "UnderhandedPowerShell\UnderhandedPowerShell_282.ps1","1" 285 | "UnderhandedPowerShell\UnderhandedPowerShell_283.ps1","1" 286 | "UnderhandedPowerShell\UnderhandedPowerShell_284.ps1","1" 287 | "UnderhandedPowerShell\UnderhandedPowerShell_285.ps1","1" 288 | "UnderhandedPowerShell\UnderhandedPowerShell_286.ps1","1" 289 | "UnderhandedPowerShell\UnderhandedPowerShell_287.ps1","1" 290 | "UnderhandedPowerShell\UnderhandedPowerShell_288.ps1","1" 291 | "UnderhandedPowerShell\UnderhandedPowerShell_289.ps1","1" 292 | "UnderhandedPowerShell\UnderhandedPowerShell_290.ps1","1" 293 | "UnderhandedPowerShell\UnderhandedPowerShell_291.ps1","1" 294 | "UnderhandedPowerShell\UnderhandedPowerShell_292.ps1","1" 295 | "UnderhandedPowerShell\UnderhandedPowerShell_293.ps1","1" 296 | "UnderhandedPowerShell\UnderhandedPowerShell_294.ps1","1" 297 | "UnderhandedPowerShell\UnderhandedPowerShell_295.ps1","1" 298 | "UnderhandedPowerShell\UnderhandedPowerShell_296.ps1","1" 299 | "UnderhandedPowerShell\UnderhandedPowerShell_297.ps1","1" 300 | "UnderhandedPowerShell\UnderhandedPowerShell_298.ps1","1" 301 | "UnderhandedPowerShell\UnderhandedPowerShell_299.ps1","1" 302 | "UnderhandedPowerShell\UnderhandedPowerShell_300.ps1","1" 303 | "UnderhandedPowerShell\UnderhandedPowerShell_301.ps1","1" 304 | "UnderhandedPowerShell\UnderhandedPowerShell_302.ps1","1" 305 | "UnderhandedPowerShell\UnderhandedPowerShell_303.ps1","1" 306 | "UnderhandedPowerShell\UnderhandedPowerShell_304.ps1","1" 307 | "UnderhandedPowerShell\UnderhandedPowerShell_305.ps1","1" 308 | "UnderhandedPowerShell\UnderhandedPowerShell_306.ps1","1" 309 | "UnderhandedPowerShell\UnderhandedPowerShell_307.ps1","1" 310 | "UnderhandedPowerShell\UnderhandedPowerShell_308.ps1","1" 311 | "UnderhandedPowerShell\UnderhandedPowerShell_309.ps1","1" 312 | "UnderhandedPowerShell\UnderhandedPowerShell_310.ps1","1" 313 | "UnderhandedPowerShell\UnderhandedPowerShell_311.ps1","1" 314 | "UnderhandedPowerShell\UnderhandedPowerShell_312.ps1","1" 315 | "UnderhandedPowerShell\UnderhandedPowerShell_313.ps1","1" 316 | "UnderhandedPowerShell\UnderhandedPowerShell_314.ps1","1" 317 | "UnderhandedPowerShell\UnderhandedPowerShell_315.ps1","1" 318 | "UnderhandedPowerShell\UnderhandedPowerShell_316.ps1","1" 319 | "UnderhandedPowerShell\UnderhandedPowerShell_317.ps1","1" 320 | "UnderhandedPowerShell\UnderhandedPowerShell_318.ps1","1" 321 | "UnderhandedPowerShell\UnderhandedPowerShell_319.ps1","1" 322 | "UnderhandedPowerShell\UnderhandedPowerShell_320.ps1","1" 323 | "UnderhandedPowerShell\UnderhandedPowerShell_321.ps1","1" 324 | "UnderhandedPowerShell\UnderhandedPowerShell_322.ps1","1" 325 | "UnderhandedPowerShell\UnderhandedPowerShell_323.ps1","1" 326 | "UnderhandedPowerShell\UnderhandedPowerShell_324.ps1","1" 327 | "UnderhandedPowerShell\UnderhandedPowerShell_325.ps1","1" 328 | "UnderhandedPowerShell\UnderhandedPowerShell_326.ps1","1" 329 | "UnderhandedPowerShell\UnderhandedPowerShell_327.ps1","1" 330 | "UnderhandedPowerShell\UnderhandedPowerShell_328.ps1","1" 331 | "UnderhandedPowerShell\UnderhandedPowerShell_329.ps1","1" 332 | "UnderhandedPowerShell\UnderhandedPowerShell_330.ps1","1" 333 | "UnderhandedPowerShell\UnderhandedPowerShell_331.ps1","1" 334 | "UnderhandedPowerShell\UnderhandedPowerShell_332.ps1","1" 335 | "UnderhandedPowerShell\UnderhandedPowerShell_333.ps1","1" 336 | "UnderhandedPowerShell\UnderhandedPowerShell_334.ps1","1" 337 | "UnderhandedPowerShell\UnderhandedPowerShell_335.ps1","1" 338 | "UnderhandedPowerShell\UnderhandedPowerShell_336.ps1","1" 339 | "UnderhandedPowerShell\UnderhandedPowerShell_337.ps1","1" 340 | "UnderhandedPowerShell\UnderhandedPowerShell_338.ps1","1" 341 | "UnderhandedPowerShell\UnderhandedPowerShell_339.ps1","1" 342 | "UnderhandedPowerShell\UnderhandedPowerShell_340.ps1","1" 343 | "UnderhandedPowerShell\UnderhandedPowerShell_341.ps1","1" 344 | "UnderhandedPowerShell\UnderhandedPowerShell_342.ps1","1" 345 | "UnderhandedPowerShell\UnderhandedPowerShell_343.ps1","1" 346 | "UnderhandedPowerShell\UnderhandedPowerShell_344.ps1","1" 347 | "UnderhandedPowerShell\UnderhandedPowerShell_345.ps1","1" 348 | "UnderhandedPowerShell\UnderhandedPowerShell_346.ps1","1" 349 | "UnderhandedPowerShell\UnderhandedPowerShell_347.ps1","1" 350 | "UnderhandedPowerShell\UnderhandedPowerShell_348.ps1","1" 351 | "UnderhandedPowerShell\UnderhandedPowerShell_349.ps1","1" 352 | "UnderhandedPowerShell\UnderhandedPowerShell_350.ps1","1" 353 | "UnderhandedPowerShell\UnderhandedPowerShell_351.ps1","1" 354 | "UnderhandedPowerShell\UnderhandedPowerShell_352.ps1","1" 355 | "UnderhandedPowerShell\UnderhandedPowerShell_353.ps1","1" 356 | "UnderhandedPowerShell\UnderhandedPowerShell_354.ps1","1" 357 | "UnderhandedPowerShell\UnderhandedPowerShell_355.ps1","1" 358 | "UnderhandedPowerShell\UnderhandedPowerShell_356.ps1","1" 359 | "UnderhandedPowerShell\UnderhandedPowerShell_357.ps1","1" 360 | "UnderhandedPowerShell\UnderhandedPowerShell_358.ps1","1" 361 | "UnderhandedPowerShell\UnderhandedPowerShell_359.ps1","1" 362 | "UnderhandedPowerShell\UnderhandedPowerShell_360.ps1","1" 363 | "UnderhandedPowerShell\UnderhandedPowerShell_361.ps1","1" 364 | "UnderhandedPowerShell\UnderhandedPowerShell_362.ps1","1" 365 | "UnderhandedPowerShell\UnderhandedPowerShell_363.ps1","1" 366 | "UnderhandedPowerShell\UnderhandedPowerShell_364.ps1","1" 367 | "UnderhandedPowerShell\UnderhandedPowerShell_365.ps1","1" 368 | "UnderhandedPowerShell\UnderhandedPowerShell_366.ps1","1" 369 | "UnderhandedPowerShell\UnderhandedPowerShell_367.ps1","1" 370 | "UnderhandedPowerShell\UnderhandedPowerShell_368.ps1","1" 371 | "UnderhandedPowerShell\UnderhandedPowerShell_369.ps1","1" 372 | "UnderhandedPowerShell\UnderhandedPowerShell_370.ps1","1" 373 | "UnderhandedPowerShell\UnderhandedPowerShell_371.ps1","1" 374 | "UnderhandedPowerShell\UnderhandedPowerShell_372.ps1","1" 375 | "UnderhandedPowerShell\UnderhandedPowerShell_373.ps1","1" 376 | "UnderhandedPowerShell\UnderhandedPowerShell_374.ps1","1" 377 | "UnderhandedPowerShell\UnderhandedPowerShell_375.ps1","1" 378 | "UnderhandedPowerShell\UnderhandedPowerShell_376.ps1","1" 379 | "UnderhandedPowerShell\UnderhandedPowerShell_377.ps1","1" 380 | "UnderhandedPowerShell\UnderhandedPowerShell_378.ps1","1" 381 | "UnderhandedPowerShell\UnderhandedPowerShell_379.ps1","1" 382 | "UnderhandedPowerShell\UnderhandedPowerShell_380.ps1","1" 383 | "UnderhandedPowerShell\UnderhandedPowerShell_381.ps1","1" 384 | "UnderhandedPowerShell\UnderhandedPowerShell_382.ps1","1" 385 | -------------------------------------------------------------------------------- /Requirements/RevokeObfuscationHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Language; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | public delegate string AstExtractValue(Ast ast); 9 | public static class RevokeObfuscationHelpers 10 | { 11 | public static IDictionary AstValueGrouper(Ast ast, Type targetType, Dictionary workingResult, string checkName, AstExtractValue extractValue) 12 | { 13 | // Initialize Dictionary to store final results for this check since we cannot modify a Dictionary over which we are iterating. 14 | Dictionary finalResult = new Dictionary(StringComparer.OrdinalIgnoreCase); 15 | 16 | // Initialize counter to store Count results for percentage calculations. 17 | double totalItems = 0; 18 | 19 | // Compute the counts for each specified item. 20 | foreach(Ast targetAst in ast.FindAll( testAst => targetType.IsAssignableFrom(testAst.GetType()), true )) 21 | { 22 | // Increment count for totalItems (to be used in next foreach loop for calculating percentages). 23 | totalItems++; 24 | 25 | // Value extraction filter defined as a delegate so it is specified in the calling function. 26 | String resultKey = extractValue(targetAst); 27 | 28 | // Add entry to UNKNOWN if new item and not in predefined indexes. 29 | if(resultKey != null) 30 | { 31 | if(! workingResult.ContainsKey(resultKey)) 32 | { 33 | resultKey = "UNKNOWN"; 34 | } 35 | 36 | // Increment count for current item. 37 | workingResult[resultKey]++; 38 | } 39 | } 40 | 41 | foreach(String workingResultValue in workingResult.Keys) 42 | { 43 | // Add Count and Percent to final Dictionary. 44 | if(totalItems == 0) 45 | { 46 | finalResult[checkName + "_" + workingResultValue + "_Count"] = 0; 47 | finalResult[checkName + "_" + workingResultValue + "_Percent"] = 0; 48 | } 49 | else 50 | { 51 | finalResult[checkName + "_" + workingResultValue + "_Count"] = workingResult[workingResultValue]; 52 | finalResult[checkName + "_" + workingResultValue + "_Percent"] = ((double) workingResult[workingResultValue]) * 100 / totalItems; 53 | } 54 | } 55 | 56 | // Return final result after sorting as a SortedDictionary. 57 | return new SortedDictionary(finalResult); 58 | } 59 | 60 | 61 | public static IDictionary StringMetricCalculator(List stringList, string checkName) 62 | { 63 | // Initialize Dictionary to store final results for this check since we cannot modify a Dictionary over which we are iterating. 64 | Dictionary finalResult = new Dictionary(StringComparer.OrdinalIgnoreCase); 65 | 66 | // Initialize Dictionary and counter to store Count results. 67 | // Dictionary is a clone of initializedCharDict so all possible characters are already initialized to zero so attribute columns will be consistent for any evaluated script or command. 68 | Dictionary workingResult = new Dictionary(initializedCharDict); 69 | double totalCharacters = 0; 70 | double totalSpecialCharacters = 0; 71 | 72 | // Initialize Dictionary and List of integers to store the length of each string in stringList for later calculations (average/maximum/minimum/median/mode/range). 73 | List stringLengthList = new List(); 74 | Dictionary stringLengthDict = new Dictionary(); 75 | 76 | // Initialize Dictionary and List of doubles and counter to store the density of each string in stringList for later calculations (average/maximum/minimum/median/mode/range). 77 | List stringDensityList = new List(); 78 | Dictionary stringDensityDict = new Dictionary(); 79 | double curStringWhitespaceTabCount = 0; 80 | double totalDensityValues = 0; 81 | 82 | // Initialize Dictionary and List of doubles and counter to store the entropy of each string in stringList for later calculations (average/maximum/minimum/median/mode/range). 83 | List stringEntropyList = new List(); 84 | Dictionary stringEntropyDict = new Dictionary(); 85 | Dictionary curStringCharDict = new Dictionary(); 86 | double totalEntropyValues = 0; 87 | 88 | // Initialize Dictionary and List of doubles and counter to store the percentage of upper-case alpha characters among all alpha characters in each string in stringList for later calculations (average/maximum/minimum/median/mode/range). 89 | List stringUpperAlphaPercentList = new List(); 90 | Dictionary stringUpperAlphaPercentDict = new Dictionary(); 91 | double curStringAlphaCharCount = 0; 92 | double curStringUpperAlphaCharCount = 0; 93 | double totalUpperAlphaPercentValues = 0; 94 | 95 | // Compute the counts for each specified item. 96 | foreach(String curString in stringList) 97 | { 98 | // Add curString lengths for later calculations (average/maximum/minimum/median/mode/range). 99 | stringLengthList.Add(curString.Length); 100 | 101 | // Add to dictionary for Mode calculation. 102 | if(stringLengthDict.ContainsKey(curString.Length)) 103 | { 104 | stringLengthDict[curString.Length] = stringLengthDict[curString.Length] + 1; 105 | } 106 | else 107 | { 108 | stringLengthDict[curString.Length] = 1; 109 | } 110 | 111 | // Reset curString whitespace and tab counts for later density calculations. 112 | curStringWhitespaceTabCount = 0; 113 | 114 | // Reset Dictionary for curString character distribution for later entropy calculations. 115 | curStringCharDict.Clear(); 116 | 117 | // Reset curString alpha and upper-alpha character counts for later upper-alpha character percentage calculations. 118 | curStringAlphaCharCount = 0; 119 | curStringUpperAlphaCharCount = 0; 120 | 121 | foreach(Char curChar in curString.ToCharArray()) 122 | { 123 | // Convert current character to a string that is the UTF8 representation of the character. 124 | // This is to handle non-printable characters, Unicode characters, and to properly both upper- and lower-case versions of the same characters. 125 | String resultKey = ConvertToEncodedChar(curChar); 126 | 127 | // Increment count for totalCharacters for later percentage calculations. 128 | totalCharacters++; 129 | 130 | // Increment counter for tab or whitespace for later density calculations. 131 | switch(curChar) 132 | { 133 | case ' ' : curStringWhitespaceTabCount++; break; 134 | case '\t' : curStringWhitespaceTabCount++; break; 135 | } 136 | 137 | // Increment character frequency for later entropy calculations. 138 | if(curStringCharDict.ContainsKey(curChar)) 139 | { 140 | curStringCharDict[curChar]++; 141 | } 142 | else 143 | { 144 | curStringCharDict[curChar] = 1; 145 | } 146 | 147 | // Increment counter if curChar is a special character. 148 | if(IsSpecialChar(curChar)) 149 | { 150 | totalSpecialCharacters++; 151 | } 152 | 153 | // Increment counter if curChar is an alpha character. 154 | if(IsAlphaChar(curChar)) 155 | { 156 | curStringAlphaCharCount++; 157 | 158 | // Increment counter if curChar is an upper-case alpha character. 159 | if(IsUpperAlphaChar(curChar)) 160 | { 161 | curStringUpperAlphaCharCount++; 162 | } 163 | } 164 | 165 | // Increment count for current item. 166 | workingResult[resultKey]++; 167 | } 168 | 169 | // Add curString density to list for later calculations (average/maximum/minimum/median/mode/range). 170 | double curStringDensity = 0; 171 | if(curString.Length > 0) 172 | { 173 | curStringDensity = (curString.Length - curStringWhitespaceTabCount) / curString.Length; 174 | } 175 | 176 | stringDensityList.Add(curStringDensity); 177 | totalDensityValues += curStringDensity; 178 | 179 | // Add to dictionary for Mode calculation. 180 | if(stringDensityDict.ContainsKey(curStringDensity)) 181 | { 182 | stringDensityDict[curStringDensity]++; 183 | } 184 | else 185 | { 186 | stringDensityDict[curStringDensity] = 1; 187 | } 188 | 189 | // Calculate entropy for curString. 190 | double curStringEntropy = GetEntropy(curStringCharDict, curString.Length); 191 | 192 | // Add curString entropy to list for later calculations (average/maximum/minimum/median/mode/range). 193 | stringEntropyList.Add(curStringEntropy); 194 | totalEntropyValues += curStringEntropy; 195 | 196 | // Add to dictionary for Mode calculation. 197 | if(stringEntropyDict.ContainsKey(curStringEntropy)) 198 | { 199 | stringEntropyDict[curStringEntropy]++; 200 | } 201 | else 202 | { 203 | stringEntropyDict[curStringEntropy] = 1; 204 | } 205 | 206 | // Add curString upper-alpha character percentage (only if there are alpha characters in curString) to list for later calculations (average/maximum/minimum/median/mode/range). 207 | if(curStringAlphaCharCount > 0) 208 | { 209 | double curStringUpperAlphaCharPercent = curStringUpperAlphaCharCount / curStringAlphaCharCount; 210 | 211 | stringUpperAlphaPercentList.Add(curStringUpperAlphaCharPercent); 212 | totalUpperAlphaPercentValues += curStringUpperAlphaCharPercent; 213 | 214 | // Add to dictionary for Mode calculation. 215 | if(stringUpperAlphaPercentDict.ContainsKey(curStringUpperAlphaCharPercent)) 216 | { 217 | stringUpperAlphaPercentDict[curStringUpperAlphaCharPercent]++; 218 | } 219 | else 220 | { 221 | stringUpperAlphaPercentDict[curStringUpperAlphaCharPercent] = 1; 222 | } 223 | } 224 | } 225 | 226 | foreach(String workingResultValue in workingResult.Keys) 227 | { 228 | // Add Count and Percent to final Dictionary. 229 | if(totalCharacters == 0) 230 | { 231 | finalResult[checkName + "_CharacterDistribution_" + workingResultValue + "_Count"] = 0; 232 | finalResult[checkName + "_CharacterDistribution_" + workingResultValue + "_Percent"] = 0; 233 | } 234 | else 235 | { 236 | finalResult[checkName + "_CharacterDistribution_" + workingResultValue + "_Count"] = workingResult[workingResultValue]; 237 | finalResult[checkName + "_CharacterDistribution_" + workingResultValue + "_Percent"] = ((double) workingResult[workingResultValue]) * 100 / totalCharacters; 238 | } 239 | } 240 | 241 | // Add Count and Percent for special characters to final Dictionary. 242 | if(totalCharacters == 0) 243 | { 244 | finalResult[checkName + "_CharacterDistribution_SpecialCharacterOnly_Count"] = 0; 245 | finalResult[checkName + "_CharacterDistribution_SpecialCharacterOnly_Percent"] = 0; 246 | } 247 | else 248 | { 249 | finalResult[checkName + "_CharacterDistribution_SpecialCharacterOnly_Count"] = totalSpecialCharacters; 250 | finalResult[checkName + "_CharacterDistribution_SpecialCharacterOnly_Percent"] = ((double) totalSpecialCharacters) * 100 / totalCharacters; 251 | } 252 | 253 | // Add total count of all input strings to final Dictionary. 254 | finalResult[checkName + "_Count"] = stringLengthList.Count; 255 | 256 | // Add cumulative length of all input strings to final Dictionary. 257 | finalResult[checkName + "_Length_Total"] = totalCharacters; 258 | 259 | // Calculate length, density and entropy values and add to finalResult. 260 | finalResult = GetAvgMaxMinMedModRan(finalResult, stringLengthList, totalCharacters, stringLengthDict, checkName + "_Length"); 261 | finalResult = GetAvgMaxMinMedModRan(finalResult, stringDensityList, totalDensityValues, stringDensityDict, checkName + "_Density"); 262 | finalResult = GetAvgMaxMinMedModRan(finalResult, stringEntropyList, totalEntropyValues, stringEntropyDict, checkName + "_Entropy"); 263 | finalResult = GetAvgMaxMinMedModRan(finalResult, stringUpperAlphaPercentList, totalUpperAlphaPercentValues, stringUpperAlphaPercentDict, checkName + "_UpperAlphaPercent"); 264 | 265 | // Return final result after sorting as a SortedDictionary. 266 | return new SortedDictionary(finalResult); 267 | } 268 | 269 | 270 | public static Dictionary GetAvgMaxMinMedModRan(Dictionary finalResult, List inputList, double totalInputs, Dictionary inputDictForMode, string checkName) 271 | { 272 | // Calculate the average/maximum/minimum/median/mode/range of input values. 273 | 274 | // Set default values if inputList is empty. 275 | if(inputList.Count == 0) 276 | { 277 | // Add average/maximum/minimum/median/mode/range to final Dictionary. 278 | finalResult[checkName + "_Average"] = 0; 279 | finalResult[checkName + "_Maximum"] = 0; 280 | finalResult[checkName + "_Minimum"] = 0; 281 | finalResult[checkName + "_Median"] = 0; 282 | finalResult[checkName + "_Mode"] = 0; 283 | finalResult[checkName + "_Range"] = 0; 284 | } 285 | else 286 | { 287 | // Sort all inputList values and calculate average/maximum/minimum/median/mode/range. 288 | inputList.Sort(); 289 | 290 | // Add average/maximum/minimum/median/mode/range to final Dictionary. 291 | finalResult[checkName + "_Average"] = totalInputs / inputList.Count; 292 | finalResult[checkName + "_Maximum"] = inputList[inputList.Count-1]; 293 | finalResult[checkName + "_Minimum"] = inputList[0]; 294 | finalResult[checkName + "_Median"] = inputList[inputList.Count / 2]; 295 | finalResult[checkName + "_Mode"] = GetMode(inputDictForMode); 296 | finalResult[checkName + "_Range"] = inputList[inputList.Count-1] - inputList[0]; 297 | } 298 | 299 | // Return results. 300 | return finalResult; 301 | } 302 | 303 | 304 | public static double GetEntropy(Dictionary charDict, int totalChars) 305 | { 306 | // Calculate the entropy of input values. 307 | 308 | double entropy = 0; 309 | if(totalChars > 0) 310 | { 311 | foreach(char charCountKey in charDict.Keys) 312 | { 313 | double curCharPercent = (double)charDict[charCountKey] / totalChars; 314 | 315 | entropy += -curCharPercent * Math.Log(curCharPercent, 2); 316 | } 317 | } 318 | 319 | // Return entropy value. 320 | return entropy; 321 | } 322 | 323 | 324 | public static double GetMode(Dictionary inputDictionary) 325 | { 326 | // Calculate the mode of input values. 327 | 328 | double modeValue = double.MinValue; 329 | double maxInputCount = double.MinValue; 330 | foreach(double inputDictionaryValue in inputDictionary.Keys) 331 | { 332 | if(inputDictionary[inputDictionaryValue] > maxInputCount) 333 | { 334 | maxInputCount = inputDictionary[inputDictionaryValue]; 335 | modeValue = inputDictionaryValue; 336 | } 337 | } 338 | 339 | // Return mode value. 340 | return modeValue; 341 | } 342 | 343 | 344 | public static Dictionary initializedCharDict = new Dictionary(StringComparer.OrdinalIgnoreCase) 345 | { 346 | // Initialize Dictionary with all possible character key values included in the character frequency analysis in StringMetricCalculator function. 347 | // This Dictionary will be cloned for each instance of the StringMetricCalculator function for performance purposes. 348 | 349 | // Initialize printable characters. 350 | {"SPACE_20", 0}, 351 | {"!_21", 0}, 352 | {"\"_22", 0}, 353 | {"#_23", 0}, 354 | {"$_24", 0}, 355 | {"%_25", 0}, 356 | {"&_26", 0}, 357 | {"'_27", 0}, 358 | {"(_28", 0}, 359 | {")_29", 0}, 360 | {"*_2a", 0}, 361 | {"+_2b", 0}, 362 | {",_2c", 0}, 363 | {"-_2d", 0}, 364 | {"._2e", 0}, 365 | {"/_2f", 0}, 366 | {"0_30", 0}, 367 | {"1_31", 0}, 368 | {"2_32", 0}, 369 | {"3_33", 0}, 370 | {"4_34", 0}, 371 | {"5_35", 0}, 372 | {"6_36", 0}, 373 | {"7_37", 0}, 374 | {"8_38", 0}, 375 | {"9_39", 0}, 376 | {":_3a", 0}, 377 | {";_3b", 0}, 378 | {"<_3c", 0}, 379 | {"=_3d", 0}, 380 | {">_3e", 0}, 381 | {"?_3f", 0}, 382 | {"@_40", 0}, 383 | {"A_41", 0}, 384 | {"B_42", 0}, 385 | {"C_43", 0}, 386 | {"D_44", 0}, 387 | {"E_45", 0}, 388 | {"F_46", 0}, 389 | {"G_47", 0}, 390 | {"H_48", 0}, 391 | {"I_49", 0}, 392 | {"J_4a", 0}, 393 | {"K_4b", 0}, 394 | {"L_4c", 0}, 395 | {"M_4d", 0}, 396 | {"N_4e", 0}, 397 | {"O_4f", 0}, 398 | {"P_50", 0}, 399 | {"Q_51", 0}, 400 | {"R_52", 0}, 401 | {"S_53", 0}, 402 | {"T_54", 0}, 403 | {"U_55", 0}, 404 | {"V_56", 0}, 405 | {"W_57", 0}, 406 | {"X_58", 0}, 407 | {"Y_59", 0}, 408 | {"Z_5a", 0}, 409 | {"[_5b", 0}, 410 | {"\\_5c", 0}, 411 | {"]_5d", 0}, 412 | {"^_5e", 0}, 413 | {"__5f", 0}, 414 | {"`_60", 0}, 415 | {"a_61", 0}, 416 | {"b_62", 0}, 417 | {"c_63", 0}, 418 | {"d_64", 0}, 419 | {"e_65", 0}, 420 | {"f_66", 0}, 421 | {"g_67", 0}, 422 | {"h_68", 0}, 423 | {"i_69", 0}, 424 | {"j_6a", 0}, 425 | {"k_6b", 0}, 426 | {"l_6c", 0}, 427 | {"m_6d", 0}, 428 | {"n_6e", 0}, 429 | {"o_6f", 0}, 430 | {"p_70", 0}, 431 | {"q_71", 0}, 432 | {"r_72", 0}, 433 | {"s_73", 0}, 434 | {"t_74", 0}, 435 | {"u_75", 0}, 436 | {"v_76", 0}, 437 | {"w_77", 0}, 438 | {"x_78", 0}, 439 | {"y_79", 0}, 440 | {"z_7a", 0}, 441 | {"{_7b", 0}, 442 | {"|_7c", 0}, 443 | {"}_7d", 0}, 444 | {"~_7e", 0}, 445 | 446 | // Initialize non-printable (but common) UTF keys. 447 | {"NUL_00", 0}, 448 | {"SOH_01", 0}, 449 | {"STX_02", 0}, 450 | {"ETX_03", 0}, 451 | {"EOT_04", 0}, 452 | {"ENQ_05", 0}, 453 | {"ACK_06", 0}, 454 | {"BEL_07", 0}, 455 | {"BS_08" , 0}, 456 | {"TAB_09", 0}, 457 | {"LF_0A" , 0}, 458 | {"VT_0B" , 0}, 459 | {"FF_0C" , 0}, 460 | {"CR_0D" , 0}, 461 | {"SO_0E" , 0}, 462 | {"SI_0F" , 0}, 463 | {"DLE_10", 0}, 464 | {"DC1_11", 0}, 465 | {"DC2_12", 0}, 466 | {"DC3_13", 0}, 467 | {"DC4_14", 0}, 468 | {"NAK_15", 0}, 469 | {"SYN_16", 0}, 470 | {"ETB_17", 0}, 471 | {"CAN_18", 0}, 472 | {"EM_19" , 0}, 473 | {"SUB_1A", 0}, 474 | {"ESC_1B", 0}, 475 | {"FS_1C" , 0}, 476 | {"GS_1D" , 0}, 477 | {"RS_1E" , 0}, 478 | {"US_1F" , 0}, 479 | {"DEL_7F", 0}, 480 | 481 | // Initialize specific special whitespaces, dashes and quotes that PowerShell explicitly handles (https://github.com/PowerShell/PowerShell/blob/02b5f357a20e6dee9f8e60e3adb9025be3c94490/src/System.Management.Automation/engine/parser/CharTraits.cs#L7-L26). 482 | 483 | // Uncommon whitespace. 484 | {"SpecialChar_NoBreakSpace_194_160", 0}, 485 | {"SpecialChar_NextLine_194_133", 0}, 486 | 487 | // Special dashes. 488 | {"SpecialChar_EnDash_226_128_147", 0}, 489 | {"SpecialChar_EmDash_226_128_148", 0}, 490 | {"SpecialChar_HorizontalBar_226_128_149", 0}, 491 | 492 | // Special single quotes. 493 | {"SpecialChar_QuoteSingleLeft_226_128_152", 0}, 494 | {"SpecialChar_QuoteSingleRight_226_128_153", 0}, 495 | {"SpecialChar_QuoteSingleBase_226_128_154", 0}, 496 | {"SpecialChar_QuoteReversed_226_128_155", 0}, 497 | 498 | // Special double quotes. 499 | {"SpecialChar_QuoteDoubleLeft_226_128_156", 0}, 500 | {"SpecialChar_QuoteDoubleRight_226_128_157", 0}, 501 | {"SpecialChar_QuoteLowDoubleLeft_226_128_158", 0}, 502 | 503 | // Initialize UNKNOWN_UNICODE and UNKNOWN_UTF keys. 504 | {"UNKNOWN_UNICODE", 0}, 505 | {"UNKNOWN_UTF", 0}, 506 | }; 507 | 508 | 509 | public static bool IsSpecialChar(char charToCheck) 510 | { 511 | // Return true if input charToCheck is a special character. Otherwise return false. 512 | 513 | switch(charToCheck) 514 | { 515 | case '\t' : return false; 516 | case '\n' : return false; 517 | case ' ' : return false; 518 | case '0' : return false; 519 | case '1' : return false; 520 | case '2' : return false; 521 | case '3' : return false; 522 | case '4' : return false; 523 | case '5' : return false; 524 | case '6' : return false; 525 | case '7' : return false; 526 | case '8' : return false; 527 | case '9' : return false; 528 | case 'A' : return false; 529 | case 'B' : return false; 530 | case 'C' : return false; 531 | case 'D' : return false; 532 | case 'E' : return false; 533 | case 'F' : return false; 534 | case 'G' : return false; 535 | case 'H' : return false; 536 | case 'I' : return false; 537 | case 'J' : return false; 538 | case 'K' : return false; 539 | case 'L' : return false; 540 | case 'M' : return false; 541 | case 'N' : return false; 542 | case 'O' : return false; 543 | case 'P' : return false; 544 | case 'Q' : return false; 545 | case 'R' : return false; 546 | case 'S' : return false; 547 | case 'T' : return false; 548 | case 'U' : return false; 549 | case 'V' : return false; 550 | case 'W' : return false; 551 | case 'X' : return false; 552 | case 'Y' : return false; 553 | case 'Z' : return false; 554 | case 'a' : return false; 555 | case 'b' : return false; 556 | case 'c' : return false; 557 | case 'd' : return false; 558 | case 'e' : return false; 559 | case 'f' : return false; 560 | case 'g' : return false; 561 | case 'h' : return false; 562 | case 'i' : return false; 563 | case 'j' : return false; 564 | case 'k' : return false; 565 | case 'l' : return false; 566 | case 'm' : return false; 567 | case 'n' : return false; 568 | case 'o' : return false; 569 | case 'p' : return false; 570 | case 'q' : return false; 571 | case 'r' : return false; 572 | case 's' : return false; 573 | case 't' : return false; 574 | case 'u' : return false; 575 | case 'v' : return false; 576 | case 'w' : return false; 577 | case 'x' : return false; 578 | case 'y' : return false; 579 | case 'z' : return false; 580 | default : return true; 581 | } 582 | } 583 | 584 | 585 | public static bool IsAlphaChar(char charToCheck) 586 | { 587 | // Return true if input charToCheck is an alpha character [a-zA-Z]. Otherwise return false. 588 | 589 | switch(charToCheck) 590 | { 591 | case 'A' : return true; 592 | case 'B' : return true; 593 | case 'C' : return true; 594 | case 'D' : return true; 595 | case 'E' : return true; 596 | case 'F' : return true; 597 | case 'G' : return true; 598 | case 'H' : return true; 599 | case 'I' : return true; 600 | case 'J' : return true; 601 | case 'K' : return true; 602 | case 'L' : return true; 603 | case 'M' : return true; 604 | case 'N' : return true; 605 | case 'O' : return true; 606 | case 'P' : return true; 607 | case 'Q' : return true; 608 | case 'R' : return true; 609 | case 'S' : return true; 610 | case 'T' : return true; 611 | case 'U' : return true; 612 | case 'V' : return true; 613 | case 'W' : return true; 614 | case 'X' : return true; 615 | case 'Y' : return true; 616 | case 'Z' : return true; 617 | case 'a' : return true; 618 | case 'b' : return true; 619 | case 'c' : return true; 620 | case 'd' : return true; 621 | case 'e' : return true; 622 | case 'f' : return true; 623 | case 'g' : return true; 624 | case 'h' : return true; 625 | case 'i' : return true; 626 | case 'j' : return true; 627 | case 'k' : return true; 628 | case 'l' : return true; 629 | case 'm' : return true; 630 | case 'n' : return true; 631 | case 'o' : return true; 632 | case 'p' : return true; 633 | case 'q' : return true; 634 | case 'r' : return true; 635 | case 's' : return true; 636 | case 't' : return true; 637 | case 'u' : return true; 638 | case 'v' : return true; 639 | case 'w' : return true; 640 | case 'x' : return true; 641 | case 'y' : return true; 642 | case 'z' : return true; 643 | default : return false; 644 | } 645 | } 646 | 647 | 648 | public static bool IsUpperAlphaChar(char charToCheck) 649 | { 650 | // Return true if input charToCheck is an upper-case alpha character [A-Z]. Otherwise return false. 651 | 652 | switch(charToCheck) 653 | { 654 | case 'A' : return true; 655 | case 'B' : return true; 656 | case 'C' : return true; 657 | case 'D' : return true; 658 | case 'E' : return true; 659 | case 'F' : return true; 660 | case 'G' : return true; 661 | case 'H' : return true; 662 | case 'I' : return true; 663 | case 'J' : return true; 664 | case 'K' : return true; 665 | case 'L' : return true; 666 | case 'M' : return true; 667 | case 'N' : return true; 668 | case 'O' : return true; 669 | case 'P' : return true; 670 | case 'Q' : return true; 671 | case 'R' : return true; 672 | case 'S' : return true; 673 | case 'T' : return true; 674 | case 'U' : return true; 675 | case 'V' : return true; 676 | case 'W' : return true; 677 | case 'X' : return true; 678 | case 'Y' : return true; 679 | case 'Z' : return true; 680 | default : return false; 681 | } 682 | } 683 | 684 | 685 | public static String ConvertToEncodedChar(Char curChar) 686 | { 687 | // For efficiency we will avoid computing UTF8 encoding values for the most commmon printable characters. 688 | // Any characters not found in the below switch statement will have their UTF8 encoded values computed in the remainder of this function. 689 | 690 | switch(curChar) 691 | { 692 | case ' ': return "SPACE_20"; 693 | case '!': return "!_21"; 694 | case '"': return "\"_22"; 695 | case '#': return "#_23"; 696 | case '$': return "$_24"; 697 | case '%': return "%_25"; 698 | case '&': return "&_26"; 699 | case '\'': return "'_27"; 700 | case '(': return "(_28"; 701 | case ')': return ")_29"; 702 | case '*': return "*_2a"; 703 | case '+': return "+_2b"; 704 | case ',': return ",_2c"; 705 | case '-': return "-_2d"; 706 | case '.': return "._2e"; 707 | case '/': return "/_2f"; 708 | case '0': return "0_30"; 709 | case '1': return "1_31"; 710 | case '2': return "2_32"; 711 | case '3': return "3_33"; 712 | case '4': return "4_34"; 713 | case '5': return "5_35"; 714 | case '6': return "6_36"; 715 | case '7': return "7_37"; 716 | case '8': return "8_38"; 717 | case '9': return "9_39"; 718 | case ':': return ":_3a"; 719 | case ';': return ";_3b"; 720 | case '<': return "<_3c"; 721 | case '=': return "=_3d"; 722 | case '>': return ">_3e"; 723 | case '?': return "?_3f"; 724 | case '@': return "@_40"; 725 | case 'A': return "A_41"; 726 | case 'B': return "B_42"; 727 | case 'C': return "C_43"; 728 | case 'D': return "D_44"; 729 | case 'E': return "E_45"; 730 | case 'F': return "F_46"; 731 | case 'G': return "G_47"; 732 | case 'H': return "H_48"; 733 | case 'I': return "I_49"; 734 | case 'J': return "J_4a"; 735 | case 'K': return "K_4b"; 736 | case 'L': return "L_4c"; 737 | case 'M': return "M_4d"; 738 | case 'N': return "N_4e"; 739 | case 'O': return "O_4f"; 740 | case 'P': return "P_50"; 741 | case 'Q': return "Q_51"; 742 | case 'R': return "R_52"; 743 | case 'S': return "S_53"; 744 | case 'T': return "T_54"; 745 | case 'U': return "U_55"; 746 | case 'V': return "V_56"; 747 | case 'W': return "W_57"; 748 | case 'X': return "X_58"; 749 | case 'Y': return "Y_59"; 750 | case 'Z': return "Z_5a"; 751 | case '[': return "[_5b"; 752 | case '\\': return "\\_5c"; 753 | case ']': return "]_5d"; 754 | case '^': return "^_5e"; 755 | case '_': return "__5f"; 756 | case '`': return "`_60"; 757 | case 'a': return "a_61"; 758 | case 'b': return "b_62"; 759 | case 'c': return "c_63"; 760 | case 'd': return "d_64"; 761 | case 'e': return "e_65"; 762 | case 'f': return "f_66"; 763 | case 'g': return "g_67"; 764 | case 'h': return "h_68"; 765 | case 'i': return "i_69"; 766 | case 'j': return "j_6a"; 767 | case 'k': return "k_6b"; 768 | case 'l': return "l_6c"; 769 | case 'm': return "m_6d"; 770 | case 'n': return "n_6e"; 771 | case 'o': return "o_6f"; 772 | case 'p': return "p_70"; 773 | case 'q': return "q_71"; 774 | case 'r': return "r_72"; 775 | case 's': return "s_73"; 776 | case 't': return "t_74"; 777 | case 'u': return "u_75"; 778 | case 'v': return "v_76"; 779 | case 'w': return "w_77"; 780 | case 'x': return "x_78"; 781 | case 'y': return "y_79"; 782 | case 'z': return "z_7a"; 783 | case '{': return "{_7b"; 784 | case '|': return "|_7c"; 785 | case '}': return "}_7d"; 786 | case '~': return "~_7e"; 787 | } 788 | 789 | // Convert curChar to string and then get UTF8 encoding as a string. 790 | Encoding utfEnc = new UTF8Encoding(true, true); 791 | Byte[] charBytes = utfEnc.GetBytes(curChar.ToString()); 792 | 793 | // Return if curChar is a Unicode character. 794 | if(charBytes.Length > 1) 795 | { 796 | // Handle specific special whitespaces, dashes and quotes that PowerShell explicitly handles (https://github.com/PowerShell/PowerShell/blob/02b5f357a20e6dee9f8e60e3adb9025be3c94490/src/System.Management.Automation/engine/parser/CharTraits.cs#L7-L26). 797 | switch(String.Join("_", charBytes)) 798 | { 799 | // Uncommon whitespace. 800 | case "194_160": return "SpecialChar_NoBreakSpace_194_160"; 801 | case "194_133": return "SpecialChar_NextLine_194_133"; 802 | 803 | // Special dashes. 804 | case "226_128_147": return "SpecialChar_EnDash_226_128_147"; 805 | case "226_128_148": return "SpecialChar_EmDash_226_128_148"; 806 | case "226_128_149": return "SpecialChar_HorizontalBar_226_128_149"; 807 | 808 | // Special single quotes. 809 | case "226_128_152": return "SpecialChar_QuoteSingleLeft_226_128_152"; 810 | case "226_128_153": return "SpecialChar_QuoteSingleRight_226_128_153"; 811 | case "226_128_154": return "SpecialChar_QuoteSingleBase_226_128_154"; 812 | case "226_128_155": return "SpecialChar_QuoteReversed_226_128_155"; 813 | 814 | // Special double quotes. 815 | case "226_128_156": return "SpecialChar_QuoteDoubleLeft_226_128_156"; 816 | case "226_128_157": return "SpecialChar_QuoteDoubleRight_226_128_157"; 817 | case "226_128_158": return "SpecialChar_QuoteLowDoubleLeft_226_128_158"; 818 | 819 | default: return "UNKNOWN_UNICODE"; 820 | } 821 | } 822 | 823 | // Convert char byte to a string. 824 | String charBytesUtfAsString = String.Format("{0:X2}", charBytes[0]); 825 | 826 | // Convert input char to string to be included in the key in result. 827 | String charName = curChar.ToString(); 828 | 829 | // If char is not included in "printable character" regex then we do not want to use it as a key as it would pollute our resultant data for analysis. 830 | // For non-printable characters we will replace common characters with TEXT representation of the character's meaning (or UTF8 as the default) in below switch statement. 831 | switch(charBytesUtfAsString) 832 | { 833 | case "00": return "NUL_00"; 834 | case "01": return "SOH_01"; 835 | case "02": return "STX_02"; 836 | case "03": return "ETX_03"; 837 | case "04": return "EOT_04"; 838 | case "05": return "ENQ_05"; 839 | case "06": return "ACK_06"; 840 | case "07": return "BEL_07"; 841 | case "08": return "BS_08"; 842 | case "09": return "TAB_09"; 843 | case "0A": return "LF_0A"; 844 | case "0B": return "VT_0B"; 845 | case "0C": return "FF_0C"; 846 | case "0D": return "CR_0D"; 847 | case "0E": return "SO_0E"; 848 | case "0F": return "SI_OF"; 849 | case "10": return "DLE_10"; 850 | case "11": return "DC1_11"; 851 | case "12": return "DC2_12"; 852 | case "13": return "DC3_13"; 853 | case "14": return "DC4_14"; 854 | case "15": return "NAK_15"; 855 | case "16": return "SYN_16"; 856 | case "17": return "ETB_17"; 857 | case "18": return "CAN_18"; 858 | case "19": return "EM_19"; 859 | case "1A": return "SUB_1A"; 860 | case "1B": return "ESC_1B"; 861 | case "1C": return "FS_1C"; 862 | case "1D": return "GS_1D"; 863 | case "1E": return "RS_1E"; 864 | case "1F": return "US_1F"; 865 | case "7F": return "DEL_7F"; 866 | default: return "UNKNOWN_UTF"; 867 | } 868 | } 869 | } --------------------------------------------------------------------------------