From 922d302a8f1b268b062b1486d7eba2134e044230 Mon Sep 17 00:00:00 2001 From: sbruyere Date: Wed, 11 Jan 2023 11:43:15 +0100 Subject: [PATCH] Provide new CLI, and fix github release workflow Provide syntax coloring Try improve workflow Fix naming in publish workflow Provide real CLI with syntax Coloring Fix readme with information about CLI options Improve way of processing CLI options & standard input --- .github/workflows/main.yml | 4 +- README.md | 23 +- Resources/samples/sample_10.txt | 10 +- Sources/vbSparkle.Console/Options.cs | 38 ++- Sources/vbSparkle.Console/Program.cs | 256 ++++++++++++++++-- .../Properties/launchSettings.json | 14 +- .../vbSparkle.Console/vbSparkle.CLI.csproj | 20 +- Sources/vbSparkle/vbSparkle.csproj | 5 + 8 files changed, 320 insertions(+), 50 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5afaf76..f8aff21 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,7 +42,7 @@ jobs: shell: bash run: | tag=$(git describe --tags --abbrev=0) - release_name="App-$tag-${{ matrix.target }}" + release_name="vbSparkle-$tag-${{ matrix.target }}" # Build everything dotnet publish Sources/vbSparkle.Console/ --framework netcoreapp3.1 --runtime "${{ matrix.target }}" -c Release -o "$release_name" @@ -61,6 +61,6 @@ jobs: - name: Publish uses: softprops/action-gh-release@v1 with: - files: "App*" + files: "vbSparkle-*" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index fa8e58d..1c798aa 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,30 @@ string deobfuscated = VbPartialEvaluator.PrettifyEncoded(obfuscatedVB); ## As a CLI The attached project `vbSparkle.CLI` is an exemple of use of vbSparkle as a CLI. The current exemple take either a path as an argument or a full binary in `StdIn`, and return the deobfuscated result. -![Railroad Diagram](/Resources/cli-exemple.JPG) +![cli-exemple](/Resources/cli-exemple.JPG) + + +``` + -p, --path (Group: input) Path of directory or script file(s) + to be deobfuscated. + + --stdin (Group: input) (Default: false) Read from stdin + + -o, --output File offset. + + --sym-rename-mode (Default: None) Define how symbols can be renamed. + Valid values: None, Variables, Constants, All + + --junk-code-processing (Default: Comment) Define how junk code should be + processed. Valid values: Nothing, Remove, Comment + + -i, --indent-spacing (Default: 4) Defines the number of spaces taken into + account for the indentation of the code. +``` ## Web UI The attached project `vbSparkle.Web` is an exemple of use of vbSparkle within a Web UI. -![Railroad Diagram](/Resources/webUI.PNG) +![web-ui](/Resources/webUI.PNG) # Why to write a VBScript de-obfuscator based on partial-evaluation ? VBScript and VBA code obfuscation are popular among attackers and allow to evade detection measures, antivirus, firewalls, EDRs, and allows to make malware analysis more difficult. diff --git a/Resources/samples/sample_10.txt b/Resources/samples/sample_10.txt index 5112fa2..a6f54a7 100644 --- a/Resources/samples/sample_10.txt +++ b/Resources/samples/sample_10.txt @@ -1,14 +1,6 @@ wscript.sleep(10000) dim KWteaHafFeaq,JHgfeomgLpfMj:ZZFJFG58GJ55H85U5:dim kiolmp:kiolmp = chr(101):dhgprdt():i = 10 + 120 - 130:SDRertserfty = chr(10+10+10+9) -Function chr(MYURTHFYTR6YFH6RYHF) -Dim Z7UR7UFHEFRURGHRYHGYR -dim SDRRFGTYHGFGFGf -Z7UR7UFHEFRURGHRYHGYR = chr(MYURTHFYTR6YFH6RYHF) -for i = 1000 - 999 to len(Z7UR7UFHEFRURGHRYHGYR) -SDRRFGTYHGFGFGf = chr(asc(Z7UR7UFHEFRURGHRYHGYR)) -next -W = SDRRFGTYHGFGFGf -end Function + function OPLMITJGUCN57 (OLGTUR783J4H6UR,NHGUIRTNVUTI65,KIOYKGJUTH6785HT) OPLMITJGUCN57 = Replace(OLGTUR783J4H6UR,NHGUIRTNVUTI65,KIOYKGJUTH6785HT) end function diff --git a/Sources/vbSparkle.Console/Options.cs b/Sources/vbSparkle.Console/Options.cs index 723b690..6704b02 100644 --- a/Sources/vbSparkle.Console/Options.cs +++ b/Sources/vbSparkle.Console/Options.cs @@ -1,25 +1,35 @@ using CommandLine; using System.Collections.Generic; +using vbSparkle.Options; namespace vbSparkle.CLI { - class Options + class BaseOptions { - [Option('p', "path", Group = "inputGroup", HelpText = "Path of directory or script file(s) to be deobfuscated.")] - public IEnumerable InputFiles { get; set; } - - [Option("stdin", - Default = false, - Group = "inputGroup", - HelpText = "Read from stdin")] - public bool stdin { get; set; } - [Option('o', "output", Required = false, Default = null, HelpText = "File offset.")] public string Output { get; set; } - [Option( - Default = false, - HelpText = "Prints all messages to standard output.")] - public bool Verbose { get; set; } + [Option("sym-rename-mode", + Default = SymbolRenamingMode.None, + HelpText = "Define how symbols can be renamed.")] + public SymbolRenamingMode SymbolRenamingMode { get; set; } + + [Option("junk-code-processing", + Default = JunkCodeProcessingMode.Comment, + HelpText = "Define how junk code should be processed.")] + public JunkCodeProcessingMode JunkCodeProcessingMode { get; set; } + + [Option('i', "indent-spacing", + Default = 4, + HelpText = "Defines the number of spaces taken into account for the indentation of the code.")] + public int IndentSpacing { get; set; } + + } + + class Options: BaseOptions + { + [Option('p', "path", Required = true, HelpText = "Path of directory or script file(s) to be deobfuscated.")] + public IEnumerable InputFiles { get; set; } + } } diff --git a/Sources/vbSparkle.Console/Program.cs b/Sources/vbSparkle.Console/Program.cs index 65b048f..6cdb2c0 100644 --- a/Sources/vbSparkle.Console/Program.cs +++ b/Sources/vbSparkle.Console/Program.cs @@ -1,10 +1,16 @@ using CommandLine; -using System; +using CommandLine.Text; + using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection; using vbSparkle.Options; +using Colorful; +using System.Drawing; +using System.Threading.Tasks; +using System.Threading; namespace vbSparkle.CLI { @@ -12,14 +18,92 @@ class Program { static void Main(string[] args) { - Parser.Default.ParseArguments(args) - .WithParsed(opts => RunOptionsAndReturnExitCode(opts)) - .WithNotParsed(errs => HandleParseError(errs)); + InitializeConsoleHeader(); + //1- disable auto generated help + var parser = new Parser(with => with.HelpWriter = null); + + if (Console.IsInputRedirected) + { + //2- run parser and get result + var parserResult = parser.ParseArguments(args); + + parserResult.WithNotParsed(errs => DisplayHelp(parserResult, errs)); + parserResult.WithParsed(opts => ProcessStdIn(opts)); + } + else + { + //2- run parser and get result + var parserResult = parser.ParseArguments(args); + + parserResult.WithNotParsed(errs => DisplayHelp(parserResult, errs)); + parserResult.WithParsed(opts => RunOptionsAndReturnExitCode(opts)); + } } - private static void HandleParseError(IEnumerable errs) + private static void InitializeConsoleHeader() { + Console.ForegroundColor = Color.WhiteSmoke; + string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + Console.ResetColor(); + Console.ReplaceAllColorsWithDefaults(); + Console.Title = "vbSparkle " + version; + + Console.WriteLine( + @" _ __ _ _ " + "\r\n" + + @"__ _| |__ / _\_ __ __ _ _ __| | _| | ___ " + "\r\n" + + @"\ \ / / '_ \\ \| '_ \ / _` | '__| |/ / |/ _ \" + "\r\n" + + @" \ V /| |_) |\ \ |_) | (_| | | | <| | __/" + "\r\n" + + @" \_/ |_.__/\__/ .__/ \__,_|_| |_|\_\_|\___|" + "\r\n" + + @" |_| v" + version + , Color.AliceBlue); + + Console.WriteLine(); + Console.WriteLine(); + + Console.WriteLine("Author(s): Sylvain Bruyere, Airbus CERT"); + Console.WriteLine("Copyright © Airbus CERT"); + Console.WriteLine("https://github.com/airbus-cert/vbSparkle"); + Console.WriteLine(); + Console.WriteLine(); + } + + private static void DisplayHelp(ParserResult result, IEnumerable errors) + { + var helpText = HelpText.AutoBuild(result, h => + { + h.AdditionalNewLineAfterOption = true; + h.Heading = string.Empty; + h.Copyright = string.Empty; + h.AddEnumValuesToHelpText = true; + + h.AddPreOptionsLine("Sample usage:"); + h.AddPreOptionsLine("> vbSparkle.CLI -p sample.vbs"); + + return HelpText.DefaultParsingErrorsHandler(result, h); + }, e => e); + + Console.WriteLine(helpText); + + Console.ReadLine(); + } + + private static void ProcessStdIn(BaseOptions opts) + { + using (var reader = new StreamReader(Console.OpenStandardInput(), Console.InputEncoding)) + { + string fileContent = reader.ReadToEnd(); + + string result = DeobfuscateContent(fileContent, opts); + + if (!string.IsNullOrWhiteSpace(opts.Output)) + File.AppendAllText(opts.Output, result); + else + { + WriteSyntaxColoringConsoleCode(result); + // Console.Out.Write(result); + } + } } private static void RunOptionsAndReturnExitCode(Options opts) @@ -27,32 +111,162 @@ private static void RunOptionsAndReturnExitCode(Options opts) if (opts.InputFiles.Count() > 0) foreach (var filename in opts.InputFiles) { - + Console.WriteLine($"# Processing {filename} ..."); string fileContent = File.ReadAllText(filename); - string result = DeobfuscateContent(fileContent); + string result = DeobfuscateContent(fileContent, opts); if (!string.IsNullOrWhiteSpace(opts.Output)) File.AppendAllText(opts.Output, result); else - Console.Out.Write(result); + WriteSyntaxColoringConsoleCode(result); } - if (opts.stdin) + + if (Console.IsInputRedirected) { - string fileContent = Console.In.ReadToEnd(); + ProcessStdIn(opts); + return; + } + } - string result = DeobfuscateContent(fileContent); + private static void WriteSyntaxColoringConsoleCode(string result) + { + Stopwatch perfWatch = new Stopwatch(); + + perfWatch.Start(); + + string[] vbKeywords = new string[] + { + "As", + "Binary", + "ByRef", + "ByVal", + "Date", + "Else", + "Empty", + "Error", + "False", + "For", + "Friend", + "Get", + "Input", + "Is", + "Len", + "Let", + "Lock", + "Me", + "Mid", + "New", + "Next", + "Nothing", + "Null", + "On", + "Option", + "Optional", + "ParamArray", + "Print", + "Private", + "Property", + "PtrSafe", + "Public", + "Resume", + "Seek", + "Set", + "Static", + "Step", + "String", + "Then", + "Time", + "To", + "True", + "WithEvents", + "Dim", + "ReDim", + "Preserve", + "If", + "Then", + "Function", + "Sub", + "GoSub", + "GoTo", + "On", + "Error", + "Do", + "Until", + "End", + "Exit", + "While", + "Loop", + "And", + "Or", + "\\+", + "\\&", + "\\=", + "\\-", + "\\*" + }; + + + string[] funcKeywords = new string[] + { + "Mid", + "Mid$", + "Asc", + "Asc$", + "Chr", + "Chr$", + "UBound", + "LBound", + "Len", + "UCase", + "LCase" + + }; + + StyleSheet styleSheet = new StyleSheet(Color.White); + + string[] dangerKeywords = new string[] + { + "CreateObject", + "WScript.Shell", + "WScript.GetObject", + + }; + + foreach (var v in dangerKeywords.Distinct().ToArray()) + styleSheet.AddStyle(v, Color.Red); + + foreach (var v in vbKeywords.Distinct().ToArray()) + styleSheet.AddStyle(v + "\\s+", Color.CornflowerBlue); + + foreach (var v in funcKeywords.Distinct().ToArray()) + styleSheet.AddStyle(v + "\\(", Color.LightBlue); + + + + //foreach (var v in funcKeywords.Distinct().ToArray()) + styleSheet.AddStyle("[a-zA-Z][\\w]*\\(", Color.SkyBlue); + + styleSheet.AddStyle("\\)", Color.LightBlue); + + styleSheet.AddStyle("\\\"(.*?)\\\"", Color.Orange); + styleSheet.AddStyle("\\\'(.*?).*", Color.Green); + + Console.WriteLine(new string('═', 80)); + + Console.WriteLineStyled(result + "\r\n", styleSheet); + + perfWatch.Stop(); + Console.ForegroundColor = Color.WhiteSmoke; + + Console.WriteLine(new string('═', 80)); + Console.WriteLine($"# Printed in {perfWatch.ElapsedMilliseconds} ms."); - if (!string.IsNullOrWhiteSpace(opts.Output)) - File.AppendAllText(opts.Output, result); - else - Console.Out.Write(result); - } } - private static string DeobfuscateContent(string content) + private static string DeobfuscateContent(string content, BaseOptions opts) { Stopwatch perfWatch = new Stopwatch(); @@ -60,13 +274,13 @@ private static string DeobfuscateContent(string content) var result = VbPartialEvaluator.PrettifyEncoded(content, new EvaluatorOptions() { - SymbolRenamingMode = SymbolRenamingMode.None, - JunkCodeProcessingMode = JunkCodeProcessingMode.Remove, - IndentSpacing = 4, + SymbolRenamingMode = opts.SymbolRenamingMode, + JunkCodeProcessingMode = opts.JunkCodeProcessingMode, + IndentSpacing = opts.IndentSpacing }); perfWatch.Stop(); - Console.WriteLine($"Computed in {perfWatch.ElapsedMilliseconds} ms."); + Console.WriteLine($"# Computed in {perfWatch.ElapsedMilliseconds} ms."); return result; } diff --git a/Sources/vbSparkle.Console/Properties/launchSettings.json b/Sources/vbSparkle.Console/Properties/launchSettings.json index 0e434ba..0d6ff9f 100644 --- a/Sources/vbSparkle.Console/Properties/launchSettings.json +++ b/Sources/vbSparkle.Console/Properties/launchSettings.json @@ -1,8 +1,20 @@ { "profiles": { - "vbSparkle.Console": { + "Help": { + "commandName": "Project", + "commandLineArgs": "" + }, + "Sample_7": { "commandName": "Project", "commandLineArgs": "-p $(SolutionDir)\\Resources\\samples\\sample_7.txt" + }, + "Sample_10": { + "commandName": "Project", + "commandLineArgs": "-p $(SolutionDir)\\Resources\\samples\\sample_10.txt --sym-rename-mode All" + }, + "vbSparkle.Console": { + "commandName": "Project", + "commandLineArgs": "" } } } \ No newline at end of file diff --git a/Sources/vbSparkle.Console/vbSparkle.CLI.csproj b/Sources/vbSparkle.Console/vbSparkle.CLI.csproj index b2b5565..92c3bb2 100644 --- a/Sources/vbSparkle.Console/vbSparkle.CLI.csproj +++ b/Sources/vbSparkle.Console/vbSparkle.CLI.csproj @@ -4,10 +4,28 @@ Exe netcoreapp3.1 vbSparkle.CLI.Program + False + Sylvain Bruyere, Airbus CERT + Airbus CERT + vbSparkle CLI + Airbus CERT + https://github.com/airbus-cert/vbSparkle + README.md + https://github.com/airbus-cert/vbSparkle + + 1.0.2 - + + True + \ + + + + + + diff --git a/Sources/vbSparkle/vbSparkle.csproj b/Sources/vbSparkle/vbSparkle.csproj index 8448d50..caa0de5 100644 --- a/Sources/vbSparkle/vbSparkle.csproj +++ b/Sources/vbSparkle/vbSparkle.csproj @@ -16,6 +16,7 @@ The parsing of Visual Basic Script and VBA is processed through the use of ANTLR Airbus CERT, Sylvain Bruyere Airbus CERT, Sylvain Bruyere Airbus + 1.0.2 @@ -49,4 +50,8 @@ The parsing of Visual Basic Script and VBA is processed through the use of ANTLR + + + +