diff --git a/Examples.md b/Examples.md index a368ac5..a52917e 100644 --- a/Examples.md +++ b/Examples.md @@ -92,3 +92,9 @@ Spartacus.exe --mode sign --action generate --pfx "C:\Output\certificate.pfx" -- ``` Spartacus.exe --mode sign --action sign --pfx "C:\Output\certificate.pfx" --password "Welcome1" --path "C:\Input\MyFakeVersion.dll" --algorithm SHA256 --verbose ``` + +### I want to clone a specific DLL and generate a solution for it + +``` +Spartacus.exe --mode local --existing --dllpath C:\Windows\System32\amsi.dll --solution "C:\Output\AmsiSolution" --pml "C:\Output\ProcMonOutput.pml" --csv "C:\Output\VulnerableDLLs.csv" --verbose +``` \ No newline at end of file diff --git a/README.md b/README.md index 9ed4737..1a69332 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,12 @@ List DLL's exports and check if each function has a pre-generated prototype. --mode proxy --action exports --dll C:\Windows\System32\version.dll --dll C:\Windows\System32\amsi.dll --prototypes ./Assets/prototypes.csv ``` +Generate solution from a specific DLL found locally on the machine. + +``` +--mode local --existing --dllpath C:\Windows\System32\amsi.dll --solution "C:\Output\AmsiSolution" --pml "C:\Output\ProcMonOutput.pml" --csv "C:\Output\VulnerableDLLs.csv" --verbose +``` + ## Signing DLL Files Spartacus now supports generating self-signed certificates (while copying attributes from existing files), and signing DLL files. diff --git a/Spartacus/CommandLine/CommandLineParser.cs b/Spartacus/CommandLine/CommandLineParser.cs index a7c5c2a..f008669 100644 --- a/Spartacus/CommandLine/CommandLineParser.cs +++ b/Spartacus/CommandLine/CommandLineParser.cs @@ -1,313 +1,316 @@ -using Spartacus.Modes.COM; -using Spartacus.Modes.DETECT; -using Spartacus.Modes.DLL; -using Spartacus.Modes.PROXY; -using Spartacus.Modes.SIGN; -using Spartacus.Properties; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Spartacus.Spartacus.CommandLine -{ - class CommandLineParser - { - private readonly string[] RawArguments; - - private Dictionary GlobalArguments = new() - { - { "mode", "" }, - { "verbose", "switch" }, - { "debug", "switch" }, - { "existing", "switch" }, - { "all", "switch" }, - { "overwrite", "switch" }, - { "external-resources", "switch" }, - { "acl", "switch" }, - { "help", "switch" }, - { "pml", "" }, - { "pmc", "" }, - { "procmon", "" }, - { "csv", "" }, - { "dll", "" }, - { "solution", "" }, - { "ghidra", "" }, - { "only", "" }, - { "action", "" }, - { "path", "" }, - { "prototypes", "" }, - { "pfx", "" }, - { "password", "" }, - { "copy-from", "" }, - { "not-before", "" }, - { "not-after", "" }, - { "subject", "" }, - { "issuer", "" }, - { "algorithm", "" }, - { "timestamp", "" } - }; - - private Dictionary> Arguments = new(); - - public CommandLineParser(string[] args) - { - RawArguments = args; - - Load(); - } - - private void Load() - { - Arguments = LoadCommandLine(GlobalArguments); - Parse(Arguments); - } - - private Dictionary> LoadCommandLine(Dictionary arguments) - { - Dictionary> data = new(); - - foreach (string parameter in arguments.Keys.ToList()) - { - data[parameter] = GetArgument($"--{parameter}", arguments[parameter] == "switch"); - } - - return data; - } - - private List GetArgument(string name, bool isSwitch = false) - { - List data = new(); - string value = null; - - for (int i = 0; i < RawArguments.Length; i++) - { - if (RawArguments[i].ToLower() == name.ToLower()) - { - if (isSwitch) - { - // This is a boolean switch, like --verbose, so we just return a non empty value. - value = "true"; - data.Add(value); - } - else - { - if (i + 1 <= RawArguments.Length) - { - value = RawArguments[i + 1]; - data.Add(value); - } - } - // We now support multiple params with the same name, so no needed. - // break; - } - } - - // Remove null values and return. - return data.Where(d => d != null).ToList(); - } - - private void Parse(Dictionary> arguments) - { - string value = ""; - foreach (KeyValuePair> argument in arguments) - { - if (argument.Value.Count == 0) - { - continue; - } - - switch (argument.Key.ToLower()) - { - case "mode": - RuntimeData.Mode = ParseSpartacusMode(argument.Value.First()); - break; - case "debug": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.Debug = (argument.Value.First().Length > 0); - Logger.IsDebug = RuntimeData.Debug; - } - break; - case "verbose": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.Verbose = (argument.Value.First().Length > 0); - Logger.IsVerbose = RuntimeData.Verbose; - } - break; - case "pmc": - RuntimeData.PMCFile = argument.Value.First().Trim(); - break; - case "pml": - RuntimeData.PMLFile = argument.Value.First().Trim(); - break; - case "csv": - RuntimeData.CSVFile = argument.Value.First().Trim(); - break; - case "procmon": - RuntimeData.ProcMonExecutable = argument.Value.First(); - break; - case "existing": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.IsExistingLog = (argument.Value.First().Length > 0); - } - break; - case "all": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.All = (argument.Value.First().Length > 0); - } - break; - case "overwrite": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.Overwrite = (argument.Value.First().Length > 0); - } - break; - case "dll": - // Here load all --dll properties, currently used only with the PROXY mode. - RuntimeData.BatchDLLFiles = argument.Value.Select(v => v.ToLower().Trim()).Distinct().ToList(); - - // If there's only 1 --dll, add it to the original value. - if (RuntimeData.BatchDLLFiles.Count == 1) - { - RuntimeData.DLLFile = RuntimeData.BatchDLLFiles.First(); - } - break; - case "solution": - RuntimeData.Solution = argument.Value.First().Trim(); - break; - case "ghidra": - RuntimeData.GhidraHeadlessPath = argument.Value.First().Trim(); - break; - case "only": - RuntimeData.FunctionsToProxy = argument.Value.First().Trim().Split(',').ToList(); - break; - case "external-resources": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.UseExternalResources = (argument.Value.First().Length > 0); - } - break; - case "acl": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.isACL = (argument.Value.First().Length > 0); - } - break; - case "help": - if (argument.Value.First().ToLower() != "false") - { - RuntimeData.isHelp = (argument.Value.First().Length > 0); - } - break; - case "action": - RuntimeData.Action = argument.Value.First().Trim(); - break; - case "path": - RuntimeData.Path = argument.Value.First().Trim(); - break; - case "prototypes": - RuntimeData.PrototypesFile = argument.Value.First().Trim(); - break; - case "pfx": - RuntimeData.Certificate.PFXFile = argument.Value.First().Trim(); - break; - case "password": - RuntimeData.Certificate.Password = argument.Value.First().Trim(); - break; - case "copy-from": - RuntimeData.Certificate.CopyFrom = argument.Value.First().Trim(); - break; - case "not-before": - try - { - RuntimeData.Certificate.NotBefore = DateTime.ParseExact(argument.Value.First().Trim(), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); - } - catch (Exception e) - { - // Nothing. - } - break; - case "not-after": - try - { - RuntimeData.Certificate.NotAfter = DateTime.ParseExact(argument.Value.First().Trim(), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); - } - catch (Exception e) - { - // Nothing. - } - break; - case "subject": - RuntimeData.Certificate.Subject = argument.Value.First().Trim(); - break; - case "issuer": - RuntimeData.Certificate.Issuer = argument.Value.First().Trim(); - break; - case "algorithm": - RuntimeData.Certificate.Algorithm = argument.Value.First().Trim(); - break; - case "timestamp": - RuntimeData.Certificate.Timestamp = argument.Value.First().Trim(); - break; - default: - throw new Exception("Unknown argument: " + argument.Key); - } - } - - // For debug. - foreach (KeyValuePair> argument in arguments) - { - foreach (string v in argument.Value) - { - Logger.Debug(String.Format("Command Line (raw): {0} = {1}", argument.Key, v)); - } - - } - - // If --help has been passed, there's no reason to validate arguments. - if (!RuntimeData.isHelp) - { - SanitiseAndValidateRuntimeData(); - } - } - - private RuntimeData.SpartacusMode ParseSpartacusMode(string mode) - { - return mode.ToLower() switch - { - "dll" => RuntimeData.SpartacusMode.DLL, - "detect" => RuntimeData.SpartacusMode.DETECT, - "proxy" => RuntimeData.SpartacusMode.PROXY, - "com" => RuntimeData.SpartacusMode.COM, - "sign" => RuntimeData.SpartacusMode.SIGN, - _ => RuntimeData.SpartacusMode.NONE, - }; - } - - private void SanitiseAndValidateRuntimeData() - { - // If Debug is enabled, force-enable Verbose. - if (RuntimeData.Debug) - { - RuntimeData.Verbose = Logger.IsVerbose = Logger.IsDebug = true; - } - - RuntimeData.ModeObject = RuntimeData.Mode switch - { - RuntimeData.SpartacusMode.DLL => new ModeDLL(), - RuntimeData.SpartacusMode.DETECT => new ModeDetect(), - RuntimeData.SpartacusMode.PROXY => new ModeProxy(), - RuntimeData.SpartacusMode.COM => new ModeCOM(), - RuntimeData.SpartacusMode.SIGN => new ModeSign(), - _ => throw new Exception("--mode is not valid"), - }; - RuntimeData.ModeObject.SanitiseAndValidateRuntimeData(); - } - } -} +using Spartacus.Modes.COM; +using Spartacus.Modes.DETECT; +using Spartacus.Modes.DLL; +using Spartacus.Modes.PROXY; +using Spartacus.Modes.SIGN; +using Spartacus.Modes.LOCAL; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Spartacus.Spartacus.CommandLine +{ + class CommandLineParser + { + private readonly string[] RawArguments; + + private Dictionary GlobalArguments = new() + { + { "mode", "" }, + { "verbose", "switch" }, + { "debug", "switch" }, + { "existing", "switch" }, + { "all", "switch" }, + { "overwrite", "switch" }, + { "external-resources", "switch" }, + { "acl", "switch" }, + { "help", "switch" }, + { "pml", "" }, + { "pmc", "" }, + { "procmon", "" }, + { "csv", "" }, + { "dll", "" }, + { "solution", "" }, + { "ghidra", "" }, + { "only", "" }, + { "action", "" }, + { "path", "" }, + { "dllpath", "" }, + { "prototypes", "" }, + { "pfx", "" }, + { "password", "" }, + { "copy-from", "" }, + { "not-before", "" }, + { "not-after", "" }, + { "subject", "" }, + { "issuer", "" }, + { "algorithm", "" }, + { "timestamp", "" } + }; + + private Dictionary> Arguments = new(); + + public CommandLineParser(string[] args) + { + RawArguments = args; + + Load(); + } + + private void Load() + { + Arguments = LoadCommandLine(GlobalArguments); + Parse(Arguments); + } + + private Dictionary> LoadCommandLine(Dictionary arguments) + { + Dictionary> data = new(); + + foreach (string parameter in arguments.Keys.ToList()) + { + data[parameter] = GetArgument($"--{parameter}", arguments[parameter] == "switch"); + } + + return data; + } + + private List GetArgument(string name, bool isSwitch = false) + { + List data = new(); + string value = null; + + for (int i = 0; i < RawArguments.Length; i++) + { + if (RawArguments[i].ToLower() == name.ToLower()) + { + if (isSwitch) + { + // This is a boolean switch, like --verbose, so we just return a non empty value. + value = "true"; + data.Add(value); + } + else + { + if (i + 1 <= RawArguments.Length) + { + value = RawArguments[i + 1]; + data.Add(value); + } + } + // We now support multiple params with the same name, so no needed. + // break; + } + } + + // Remove null values and return. + return data.Where(d => d != null).ToList(); + } + + private void Parse(Dictionary> arguments) + { + string value = ""; + foreach (KeyValuePair> argument in arguments) + { + if (argument.Value.Count == 0) + { + continue; + } + + switch (argument.Key.ToLower()) + { + case "mode": + RuntimeData.Mode = ParseSpartacusMode(argument.Value.First()); + break; + case "debug": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.Debug = (argument.Value.First().Length > 0); + Logger.IsDebug = RuntimeData.Debug; + } + break; + case "verbose": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.Verbose = (argument.Value.First().Length > 0); + Logger.IsVerbose = RuntimeData.Verbose; + } + break; + case "pmc": + RuntimeData.PMCFile = argument.Value.First().Trim(); + break; + case "pml": + RuntimeData.PMLFile = argument.Value.First().Trim(); + break; + case "csv": + RuntimeData.CSVFile = argument.Value.First().Trim(); + break; + case "procmon": + RuntimeData.ProcMonExecutable = argument.Value.First(); + break; + case "existing": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.IsExistingLog = (argument.Value.First().Length > 0); + } + break; + case "all": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.All = (argument.Value.First().Length > 0); + } + break; + case "overwrite": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.Overwrite = (argument.Value.First().Length > 0); + } + break; + case "dll": + // Here load all --dll properties, currently used only with the PROXY mode. + RuntimeData.BatchDLLFiles = argument.Value.Select(v => v.ToLower().Trim()).Distinct().ToList(); + + // If there's only 1 --dll, add it to the original value. + if (RuntimeData.BatchDLLFiles.Count == 1) + { + RuntimeData.DLLFile = RuntimeData.BatchDLLFiles.First(); + } + break; + case "solution": + RuntimeData.Solution = argument.Value.First().Trim(); + break; + case "ghidra": + RuntimeData.GhidraHeadlessPath = argument.Value.First().Trim(); + break; + case "only": + RuntimeData.FunctionsToProxy = argument.Value.First().Trim().Split(',').ToList(); + break; + case "external-resources": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.UseExternalResources = (argument.Value.First().Length > 0); + } + break; + case "acl": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.isACL = (argument.Value.First().Length > 0); + } + break; + case "help": + if (argument.Value.First().ToLower() != "false") + { + RuntimeData.isHelp = (argument.Value.First().Length > 0); + } + break; + case "action": + RuntimeData.Action = argument.Value.First().Trim(); + break; + case "path": + RuntimeData.Path = argument.Value.First().Trim(); + break; + case "dllpath": + RuntimeData.DLLPath = argument.Value.First().Trim(); + break; + case "prototypes": + RuntimeData.PrototypesFile = argument.Value.First().Trim(); + break; + case "pfx": + RuntimeData.Certificate.PFXFile = argument.Value.First().Trim(); + break; + case "password": + RuntimeData.Certificate.Password = argument.Value.First().Trim(); + break; + case "copy-from": + RuntimeData.Certificate.CopyFrom = argument.Value.First().Trim(); + break; + case "not-before": + try + { + RuntimeData.Certificate.NotBefore = DateTime.ParseExact(argument.Value.First().Trim(), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); + } + catch (Exception e) + { + // Nothing. + } + break; + case "not-after": + try + { + RuntimeData.Certificate.NotAfter = DateTime.ParseExact(argument.Value.First().Trim(), "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); + } + catch (Exception e) + { + // Nothing. + } + break; + case "subject": + RuntimeData.Certificate.Subject = argument.Value.First().Trim(); + break; + case "issuer": + RuntimeData.Certificate.Issuer = argument.Value.First().Trim(); + break; + case "algorithm": + RuntimeData.Certificate.Algorithm = argument.Value.First().Trim(); + break; + case "timestamp": + RuntimeData.Certificate.Timestamp = argument.Value.First().Trim(); + break; + default: + throw new Exception("Unknown argument: " + argument.Key); + } + } + + // For debug. + foreach (KeyValuePair> argument in arguments) + { + foreach (string v in argument.Value) + { + Logger.Debug(String.Format("Command Line (raw): {0} = {1}", argument.Key, v)); + } + + } + + // If --help has been passed, there's no reason to validate arguments. + if (!RuntimeData.isHelp) + { + SanitiseAndValidateRuntimeData(); + } + } + + private RuntimeData.SpartacusMode ParseSpartacusMode(string mode) + { + return mode.ToLower() switch + { + "dll" => RuntimeData.SpartacusMode.DLL, + "detect" => RuntimeData.SpartacusMode.DETECT, + "proxy" => RuntimeData.SpartacusMode.PROXY, + "com" => RuntimeData.SpartacusMode.COM, + "sign" => RuntimeData.SpartacusMode.SIGN, + "local" => RuntimeData.SpartacusMode.LOCAL, + _ => RuntimeData.SpartacusMode.NONE, + }; + } + + private void SanitiseAndValidateRuntimeData() + { + // If Debug is enabled, force-enable Verbose. + if (RuntimeData.Debug) + { + RuntimeData.Verbose = Logger.IsVerbose = Logger.IsDebug = true; + } + + RuntimeData.ModeObject = RuntimeData.Mode switch + { + RuntimeData.SpartacusMode.DLL => new ModeDLL(), + RuntimeData.SpartacusMode.DETECT => new ModeDetect(), + RuntimeData.SpartacusMode.PROXY => new ModeProxy(), + RuntimeData.SpartacusMode.COM => new ModeCOM(), + RuntimeData.SpartacusMode.SIGN => new ModeSign(), + RuntimeData.SpartacusMode.LOCAL => new ModeLocal(), + _ => throw new Exception("--mode is not valid"), + }; + RuntimeData.ModeObject.SanitiseAndValidateRuntimeData(); + } + } +} diff --git a/Spartacus/CommandLine/RuntimeData.cs b/Spartacus/CommandLine/RuntimeData.cs index d40d926..9a02743 100644 --- a/Spartacus/CommandLine/RuntimeData.cs +++ b/Spartacus/CommandLine/RuntimeData.cs @@ -1,83 +1,88 @@ -using Spartacus.Modes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Spartacus.Spartacus.CommandLine -{ - class RuntimeData - { - public enum SpartacusMode - { - NONE = 0, - DLL = 1, - DETECT = 2, - PROXY = 3, - COM = 4, - SIGN = 5, - }; - - public struct SignCertificate - { - public string Subject; - public string Issuer; - public DateTime NotBefore; - public DateTime NotAfter; - public string Password; - public string CopyFrom; - public string PFXFile; - public string Algorithm; - public string Timestamp; - } - - public static SpartacusMode Mode = SpartacusMode.NONE; - - public static bool Verbose = false; - - public static bool Debug = false; - - public static ModeBase ModeObject = null; - - public static string PMLFile = ""; - - public static string PMCFile = ""; - - public static string CSVFile = ""; - - public static string ProcMonExecutable = ""; - - public static bool IsExistingLog = false; - - public static bool InjectBackingFileIntoConfig = false; - - public static bool All = false; - - public static string Solution = ""; - - public static string DLLFile = ""; - - public static List FunctionsToProxy = new(); - - public static string GhidraHeadlessPath = ""; - - public static bool Overwrite = false; - - public static bool UseExternalResources = false; - - public static bool isACL = false; - - public static bool isHelp = false; - - public static List BatchDLLFiles = new(); - - public static string Action = ""; - - public static string Path = ""; - - public static string PrototypesFile = ""; - - public static SignCertificate Certificate = new(); - } -} +using Spartacus.Modes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Spartacus.Spartacus.CommandLine +{ + class RuntimeData + { + public enum SpartacusMode + { + NONE = 0, + DLL = 1, + DETECT = 2, + PROXY = 3, + COM = 4, + SIGN = 5, + LOCAL = 6 + }; + + public struct SignCertificate + { + public string Subject; + public string Issuer; + public DateTime NotBefore; + public DateTime NotAfter; + public string Password; + public string CopyFrom; + public string PFXFile; + public string Algorithm; + public string Timestamp; + } + + public static SpartacusMode Mode = SpartacusMode.NONE; + + public static bool Verbose = false; + + public static bool Debug = false; + + public static ModeBase ModeObject = null; + + public static string PMLFile = ""; + + public static string PMCFile = ""; + + public static string CSVFile = ""; + + public static string ProcMonExecutable = ""; + + public static bool IsExistingLog = false; + + public static bool InjectBackingFileIntoConfig = false; + + public static bool All = false; + + public static string Solution = ""; + + public static string DLLFile = ""; + + public static List FunctionsToProxy = new(); + + public static string GhidraHeadlessPath = ""; + + public static bool Overwrite = false; + + public static bool UseExternalResources = false; + + public static bool isACL = false; + + public static bool isLocal = false; + + public static bool isHelp = false; + + public static List BatchDLLFiles = new(); + + public static string Action = ""; + + public static string Path = ""; + + public static string DLLPath = ""; + + public static string PrototypesFile = ""; + + public static SignCertificate Certificate = new(); + } +} diff --git a/Spartacus/Modes/COM/ACLExecution.cs b/Spartacus/Modes/COM/ACLExecution.cs index 1764bd3..843f54a 100644 --- a/Spartacus/Modes/COM/ACLExecution.cs +++ b/Spartacus/Modes/COM/ACLExecution.cs @@ -1,4 +1,5 @@ using Microsoft.Win32; +using Spartacus.Modes.PROXY; using Spartacus.Spartacus.CommandLine; using Spartacus.Spartacus.Models; using Spartacus.Utils; @@ -82,6 +83,12 @@ public void Run() Logger.Warning("Trying to save file again..."); } } while (true); + + // Create solutions for identified DLLs. + if (!String.IsNullOrEmpty(RuntimeData.Solution) && Directory.Exists(RuntimeData.Solution)) + { + CreateSolutionsForDLLs(Findings); + } } protected void ExportToCSV(List findings) @@ -104,6 +111,56 @@ protected void ExportToCSV(List findings) } } + protected void CreateSolutionsForDLLs(List findings) + { + // First we collect which files we need to proxy. + Logger.Verbose("Identifying files to generate solutions for..."); + Dictionary filesToProxy = new(); + foreach (var finding in findings) + { + string dllFilename = Path.GetFileName(finding.filePath).ToLower(); + if (String.IsNullOrEmpty(dllFilename) || filesToProxy.ContainsKey(dllFilename)) + { + continue; + } + + Logger.Debug("File to proxy: " + finding.filePath); + filesToProxy.Add(dllFilename, finding.filePath); + } + + // Now we create the proxies. + ProxyGeneration proxyMode = new(); + foreach (KeyValuePair file in filesToProxy.OrderBy(x => x.Key)) + { + Logger.Info("Processing " + file.Key, false, true); + string solution = Path.Combine(RuntimeData.Solution, Path.GetFileNameWithoutExtension(file.Value)); + string dllFile = Helper.LookForFileIfNeeded(file.Value); + + if (String.IsNullOrEmpty(file.Value) || String.IsNullOrEmpty(dllFile) || !File.Exists(dllFile)) + { + try + { + File.Create(Path.Combine(solution, file.Key + "-file-not-found")).Dispose(); + } + catch (Exception e) + { + Logger.Warning(" - error creating", false, false); + } + Logger.Warning(" - No DLL Found", true, false); + continue; + } + else + { + Logger.Info(" - Found", true, false); + } + + if (!proxyMode.ProcessSingleDLL(dllFile, solution)) + { + Logger.Error("Could not generate proxy DLL for: " + dllFile); + } + } + } + protected string CleanPath(string path) { if (String.IsNullOrEmpty(path)) diff --git a/Spartacus/Modes/COM/ModeCOM.cs b/Spartacus/Modes/COM/ModeCOM.cs index 94f7f67..0347bf7 100644 --- a/Spartacus/Modes/COM/ModeCOM.cs +++ b/Spartacus/Modes/COM/ModeCOM.cs @@ -45,6 +45,24 @@ public override void SanitiseAndValidateRuntimeData() { Logger.Debug("--csv exists and will be overwritten"); } + + // Solution folder. + if (String.IsNullOrEmpty(RuntimeData.Solution)) + { + Logger.Debug("--solution is missing, will skip DLL proxy generation"); + } + else if (Directory.Exists(RuntimeData.Solution)) + { + Logger.Debug("--solution directory already exists"); + } + else + { + Logger.Debug("--solution directory does not exist - creating now"); + if (!Helper.CreateTargetDirectory(RuntimeData.Solution)) + { + throw new Exception("Could not create --solution directory: " + RuntimeData.Solution); + } + } } protected void SanitiseExistingLogProcessing() @@ -61,7 +79,20 @@ protected void SanitiseExistingLogProcessing() } protected void SanitiseNewLogProcessing() - { + { + // Validate directory for files output exists. + string directoryPath = Path.GetDirectoryName(RuntimeData.PMLFile); + if (!Directory.Exists(directoryPath)) + { + // If the directory doesn't exist, create it + Directory.CreateDirectory(directoryPath); + Console.WriteLine("Directory created: " + directoryPath); + } + else + { + Console.WriteLine("Directory already exists: " + directoryPath); + } + // Check for ProcMon. if (String.IsNullOrEmpty(RuntimeData.ProcMonExecutable)) { diff --git a/Spartacus/Modes/COM/StandardExecution.cs b/Spartacus/Modes/COM/StandardExecution.cs index da7799c..c82c5dd 100644 --- a/Spartacus/Modes/COM/StandardExecution.cs +++ b/Spartacus/Modes/COM/StandardExecution.cs @@ -1,4 +1,5 @@ using Microsoft.Win32; +using Spartacus.Modes.PROXY; using Spartacus.ProcMon; using Spartacus.Spartacus.CommandLine; using Spartacus.Utils; @@ -76,6 +77,12 @@ public void Run() Logger.Warning("Trying to save file again..."); } } while (true); + + // Create solutions for identified DLLs. + if (!String.IsNullOrEmpty(RuntimeData.Solution) && Directory.Exists(RuntimeData.Solution)) + { + CreateSolutionsForDLLs(findings); + } } protected void GatherEvents() @@ -111,6 +118,58 @@ protected void GatherEvents() procMon.Terminate(); } + protected void CreateSolutionsForDLLs(Dictionary findings) + { + // First we collect which files we need to proxy. + Logger.Verbose("Identifying files to generate solutions for..."); + Dictionary filesToProxy = new(); + foreach (KeyValuePair f in findings) + { + Logger.Debug("FullPath Registry COM:" + f.Value.ExistingRegistryCOM); + string dllFilename = Path.GetFileName(!String.IsNullOrEmpty(f.Value.ExistingRegistryCOM) ? f.Value.ExistingRegistryCOM.Replace("\"", "\"\"") : "").ToLower(); + Logger.Debug("dllFilename:" + dllFilename); + if (String.IsNullOrEmpty(dllFilename) || filesToProxy.ContainsKey(dllFilename)) + { + continue; + } + + Logger.Debug("File to dll: " + f.Value.ExistingRegistryCOM); + filesToProxy.Add(dllFilename, f.Value.ExistingRegistryCOM); + } + + // Now we create the proxies. + ProxyGeneration proxyMode = new(); + foreach (KeyValuePair file in filesToProxy.OrderBy(x => x.Key)) + { + Logger.Info("Processing " + file.Key, false, true); + string solution = Path.Combine(RuntimeData.Solution, Path.GetFileNameWithoutExtension(file.Value)); + string dllFile = Helper.LookForFileIfNeeded(file.Value); + + if (String.IsNullOrEmpty(file.Value) || String.IsNullOrEmpty(dllFile) || !File.Exists(dllFile)) + { + try + { + File.Create(Path.Combine(solution, file.Key + "-file-not-found")).Dispose(); + } + catch (Exception e) + { + Logger.Warning(" - error creating", false, false); + } + Logger.Warning(" - No DLL Found", true, false); + continue; + } + else + { + Logger.Info(" - Found", true, false); + } + + if (!proxyMode.ProcessSingleDLL(dllFile, solution)) + { + Logger.Error("Could not generate proxy DLL for: " + dllFile); + } + } + } + protected Dictionary FindInterestingEvents(ProcMonPML log) { UInt32 counter = 0; diff --git a/Spartacus/Modes/DLL/ModeDLL.cs b/Spartacus/Modes/DLL/ModeDLL.cs index 41a0cef..58fc016 100644 --- a/Spartacus/Modes/DLL/ModeDLL.cs +++ b/Spartacus/Modes/DLL/ModeDLL.cs @@ -1,16 +1,11 @@ using Spartacus.Modes.PROXY; using Spartacus.ProcMon; -using Spartacus.Properties; -using Spartacus.Spartacus; using Spartacus.Spartacus.CommandLine; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; using static Spartacus.ProcMon.ProcMonConstants; namespace Spartacus.Modes.DLL @@ -75,6 +70,28 @@ public override void Run() } } + protected void CreateSingleSolutionForDLL(string dllPath) + { + string solution = Path.Combine(RuntimeData.Solution, Path.GetFileNameWithoutExtension(dllPath)); + string dllFile = Helper.LookForFileIfNeeded(dllPath); + + ProxyGeneration proxyMode = new(); + if (String.IsNullOrEmpty(dllPath) || String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) + { + Logger.Warning(" - No DLL Found", true, false); + return; + } + else + { + Logger.Info(" - Found", true, false); + } + + if (!proxyMode.ProcessSingleDLL(dllPath, solution)) + { + Logger.Error("Could not generate proxy DLL for: " + dllFile); + } + } + protected void CreateSolutionsForDLLs(Dictionary events) { // First we collect which files we need to proxy. diff --git a/Spartacus/Modes/DLL/ModeLocal.cs b/Spartacus/Modes/DLL/ModeLocal.cs new file mode 100644 index 0000000..9809d21 --- /dev/null +++ b/Spartacus/Modes/DLL/ModeLocal.cs @@ -0,0 +1,154 @@ +using Spartacus.Modes.PROXY; +using Spartacus.ProcMon; +using Spartacus.Spartacus.CommandLine; +using System; +using System.IO; + +namespace Spartacus.Modes.LOCAL +{ + class ModeLocal : ModeBase + { + public override void Run() + { + Logger.Info("Running is local mode..."); + Logger.Info("DLL path for local mode:" + RuntimeData.DLLPath); + CreateSingleSolutionForDLL(RuntimeData.DLLPath); + + Logger.Info("End of running local mode."); + return; + } + + protected void CreateSingleSolutionForDLL(string dllPath) + { + string solution = Path.Combine(RuntimeData.Solution, Path.GetFileNameWithoutExtension(dllPath)); + string dllFile = Helper.LookForFileIfNeeded(dllPath); + + ProxyGeneration proxyMode = new(); + if (String.IsNullOrEmpty(dllPath) || String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) + { + Logger.Warning(" - No DLL Found", true, false); + return; + } + else + { + Logger.Info(" - Found", true, false); + } + + if (!proxyMode.ProcessSingleDLL(dllPath, solution)) + { + Logger.Error("Could not generate proxy DLL for: " + dllFile); + } + } + + public override void SanitiseAndValidateRuntimeData() + { + if (RuntimeData.IsExistingLog) + { + SanitiseExistingLogProcessing(); + } + else + { + SanitiseNewLogProcessing(); + } + + // Check for CSV output file. + if (String.IsNullOrEmpty(RuntimeData.CSVFile)) + { + throw new Exception("--csv is missing"); + } + else if (File.Exists(RuntimeData.CSVFile)) + { + Logger.Debug("--csv exists and will be overwritten"); + } + + // Solution folder. + if (String.IsNullOrEmpty(RuntimeData.Solution)) + { + Logger.Debug("--solution is missing, will skip DLL proxy generation"); + } + else if (Directory.Exists(RuntimeData.Solution)) + { + Logger.Debug("--solution directory already exists"); + } + else + { + Logger.Debug("--solution directory does not exist - creating now"); + if (!Helper.CreateTargetDirectory(RuntimeData.Solution)) + { + throw new Exception("Could not create --solution directory: " + RuntimeData.Solution); + } + } + } + + protected void SanitiseExistingLogProcessing() + { + // Check if the PML file exists. + if (String.IsNullOrEmpty(RuntimeData.PMLFile)) + { + throw new Exception("--pml is missing"); + } + else if (!File.Exists(RuntimeData.PMLFile)) + { + throw new Exception("--pml does not exist: " + RuntimeData.PMLFile); + } + Logger.Debug("--pml is " + RuntimeData.PMLFile); + } + + protected void SanitiseNewLogProcessing() + { + // Check for ProcMon. + if (String.IsNullOrEmpty(RuntimeData.ProcMonExecutable)) + { + throw new Exception("--procmon is missing"); + } + else if (!File.Exists(RuntimeData.ProcMonExecutable)) + { + throw new Exception("--procmon does not exist: " + RuntimeData.ProcMonExecutable); + } + + // Check for ProcMon config & log file. + if (String.IsNullOrEmpty(RuntimeData.PMCFile)) + { + // Since no --pmc has been passed, it means that we can't load the --pml file automatically from + // that configuration. This means that we _must_ have --pml passed here. + if (String.IsNullOrEmpty(RuntimeData.PMLFile)) + { + throw new Exception("--pml is missing"); + } + else if (File.Exists(RuntimeData.PMLFile)) + { + Logger.Debug("--pml exists and will be overwritten"); + } + } + else if (!File.Exists(RuntimeData.PMCFile)) + { + // If the argument was passed but does not exist, exit. + throw new Exception("--pmc does not exist: " + RuntimeData.PMCFile); + } + else + { + // If we reach this, it means that --pmc has been passed through and exists. + ProcMonPMC pmc = new(RuntimeData.PMCFile); + + // If the existing PMC file has no logfile/backing file, check to see if --pml has been set. + if (String.IsNullOrEmpty(pmc.GetConfiguration().Logfile)) + { + if (String.IsNullOrEmpty(RuntimeData.PMLFile)) + { + throw new Exception("The passed --pmc file that has no log/backing file configured and no --pml argument was passed to set it. " + + "Either setup the backing file in the existing PMC file or pass a --pml parameter"); + } + + // Here, the --pmc config has no PML path for log/backing, but we've passed a --pml argument. + // Therefore, we'll inject our new PML location into the existing PMC config. + RuntimeData.InjectBackingFileIntoConfig = true; + } + else + { + // The PMC file has a backing file, so we don't need the --pml argument. + RuntimeData.PMLFile = pmc.GetConfiguration().Logfile; + } + } + } + } +} diff --git a/Spartacus/Program.cs b/Spartacus/Program.cs index 6964119..e5c9179 100644 --- a/Spartacus/Program.cs +++ b/Spartacus/Program.cs @@ -16,8 +16,8 @@ static void Main(string[] args) { string appVersion = String.Format("{0}.{1}.{2}", Assembly.GetExecutingAssembly().GetName().Version.Major.ToString(), Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString(), Assembly.GetExecutingAssembly().GetName().Version.Build.ToString()); - Logger.Info($@"Spartacus v{appVersion} [ Pavel Tsakalidis ]", true, false); - Logger.Info($@"- For more information visit https://github.com/sadreck/Spartacus", true, false); + Logger.Info($@"Spartacus v{appVersion} [ Accenture & EY Security ]", true, false); + Logger.Info($@"- For more information visit https://github.com/binarytrails/Spartacus", true, false); Logger.Info("", true, false); Helper helper = new(); diff --git a/Spartacus/Properties/AssemblyInfo.cs b/Spartacus/Properties/AssemblyInfo.cs index d3f0a84..9c48efd 100644 --- a/Spartacus/Properties/AssemblyInfo.cs +++ b/Spartacus/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.2.2.0")] -[assembly: AssemblyFileVersion("2.2.2.0")] +[assembly: AssemblyVersion("2.3.1.0")] +[assembly: AssemblyFileVersion("2.3.1.0")] diff --git a/Spartacus/Spartacus.csproj b/Spartacus/Spartacus.csproj index 54def3f..5bec0f9 100644 --- a/Spartacus/Spartacus.csproj +++ b/Spartacus/Spartacus.csproj @@ -87,6 +87,7 @@ +