From 9e917e2bb960ce7a96654b96abbdf930feeeb77e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 15 Mar 2023 11:06:29 -0400 Subject: [PATCH] Create new Scanner helper classes --- BurnOutSharp/Factory.cs | 83 ++++++++ BurnOutSharp/FileType/Executable.cs | 7 +- BurnOutSharp/Handler.cs | 292 ++++++++++++++++++++++++++++ BurnOutSharp/Scanner.cs | 128 +++--------- 4 files changed, 403 insertions(+), 107 deletions(-) create mode 100644 BurnOutSharp/Factory.cs create mode 100644 BurnOutSharp/Handler.cs diff --git a/BurnOutSharp/Factory.cs b/BurnOutSharp/Factory.cs new file mode 100644 index 00000000..6a8997ba --- /dev/null +++ b/BurnOutSharp/Factory.cs @@ -0,0 +1,83 @@ +using BinaryObjectScanner.Interfaces; +using BinaryObjectScanner.Utilities; + +namespace BurnOutSharp +{ + internal static class Factory + { + /// + /// Create an instance of a detectable based on file type + /// + public static IDetectable CreateDetectable(SupportedFileType fileType) + { + switch (fileType) + { + case SupportedFileType.AACSMediaKeyBlock: return new BinaryObjectScanner.FileType.AACSMediaKeyBlock(); + case SupportedFileType.BDPlusSVM: return new BinaryObjectScanner.FileType.BDPlusSVM(); + //case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA(); + case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); + case SupportedFileType.LDSCRYPT: return new BinaryObjectScanner.FileType.LDSCRYPT(); + //case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS(); + //case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro(); + case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ(); + case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS(); + case SupportedFileType.Textfile: return new BinaryObjectScanner.FileType.Textfile(); + default: return null; + } + } + + /// + /// Create an instance of an extractable based on file type + /// + public static IExtractable CreateExtractable(SupportedFileType fileType) + { + switch (fileType) + { + case SupportedFileType.BFPK: return new BinaryObjectScanner.FileType.BFPK(); + case SupportedFileType.BSP: return new BinaryObjectScanner.FileType.BSP(); + case SupportedFileType.BZip2: return new BinaryObjectScanner.FileType.BZip2(); + case SupportedFileType.CFB: return new BinaryObjectScanner.FileType.CFB(); + //case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA(); + case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); + case SupportedFileType.GCF: return new BinaryObjectScanner.FileType.GCF(); + case SupportedFileType.GZIP: return new BinaryObjectScanner.FileType.GZIP(); + case SupportedFileType.InstallShieldArchiveV3: return new BinaryObjectScanner.FileType.InstallShieldArchiveV3(); + case SupportedFileType.InstallShieldCAB: return new BinaryObjectScanner.FileType.InstallShieldCAB(); + case SupportedFileType.MicrosoftCAB: return new BinaryObjectScanner.FileType.MicrosoftCAB(); + case SupportedFileType.MicrosoftLZ: return new BinaryObjectScanner.FileType.MicrosoftLZ(); + case SupportedFileType.MPQ: return new BinaryObjectScanner.FileType.MPQ(); + //case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS(); + //case SupportedFileType.NCF: return new BinaryObjectScanner.FileType.NCF(); + //case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro(); + case SupportedFileType.PAK: return new BinaryObjectScanner.FileType.PAK(); + case SupportedFileType.PFF: return new BinaryObjectScanner.FileType.PFF(); + case SupportedFileType.PKZIP: return new BinaryObjectScanner.FileType.PKZIP(); + //case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ(); + //case SupportedFileType.Quantum: return new BinaryObjectScanner.FileType.Quantum(); + case SupportedFileType.RAR: return new BinaryObjectScanner.FileType.RAR(); + case SupportedFileType.SevenZip: return new BinaryObjectScanner.FileType.SevenZip(); + case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS(); + case SupportedFileType.SGA: return new BinaryObjectScanner.FileType.SGA(); + case SupportedFileType.TapeArchive: return new BinaryObjectScanner.FileType.TapeArchive(); + case SupportedFileType.VBSP: return new BinaryObjectScanner.FileType.VBSP(); + case SupportedFileType.VPK: return new BinaryObjectScanner.FileType.VPK(); + case SupportedFileType.WAD: return new BinaryObjectScanner.FileType.WAD(); + case SupportedFileType.XZ: return new BinaryObjectScanner.FileType.XZ(); + case SupportedFileType.XZP: return new BinaryObjectScanner.FileType.XZP(); + default: return null; + } + } + + /// + /// Create an instance of a scannable based on file type + /// + public static IScannable CreateScannable(SupportedFileType fileType) + { + switch (fileType) + { + case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); + default: return null; + } + } + } +} diff --git a/BurnOutSharp/FileType/Executable.cs b/BurnOutSharp/FileType/Executable.cs index 6f8160e2..1a8d4940 100644 --- a/BurnOutSharp/FileType/Executable.cs +++ b/BurnOutSharp/FileType/Executable.cs @@ -201,12 +201,7 @@ namespace BinaryObjectScanner.FileType { // Get the protection for the class, if possible string protection = checkClass.CheckContents(file, fileContent, scanner.IncludeDebug); - if (ShouldAddProtection(checkClass, scanner.ScanPackers, protection)) - AppendToDictionary(protections, file, protection); - - // If we had a protection, check if it is extractable - if (!string.IsNullOrWhiteSpace(protection)) - HandleExtractable(scanner, stream, file, checkClass, protections); + AppendToDictionary(protections, file, protection); }); } diff --git a/BurnOutSharp/Handler.cs b/BurnOutSharp/Handler.cs new file mode 100644 index 00000000..8ece76c3 --- /dev/null +++ b/BurnOutSharp/Handler.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using BinaryObjectScanner.Interfaces; +using BinaryObjectScanner.Utilities; +using BinaryObjectScanner.Wrappers; +using static BinaryObjectScanner.Utilities.Dictionary; + +namespace BurnOutSharp +{ + // TODO: Implement IExtractable handler + // TODO: Implement IPathCheck handler + internal static class Handler + { + #region Multiple Implementation Wrappers + + /// + /// Handle a single file based on all content check implementations + /// + /// Name of the source file of the stream, for tracking + /// Stream to scan the contents of + /// True to include packers in the output, false otherwise + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandleContentChecks(string fileName, Stream stream, bool scanPackers, bool includeDebug) + { + // If we have an invalid file + if (string.IsNullOrWhiteSpace(fileName)) + return null; + else if (!File.Exists(fileName)) + return null; + + // Read the file contents + byte[] fileContent = null; + try + { + using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true)) + { + fileContent = br.ReadBytes((int)stream.Length); + if (fileContent == null) + return null; + } + } + catch (Exception ex) + { + if (includeDebug) Console.WriteLine(ex); + return null; + } + + // Create the output queue + var protections = new ConcurrentQueue(); + + // Iterate through all checks + Parallel.ForEach(ScanningClasses.ContentCheckClasses, checkClass => + { + // Get the protection for the class, if possible + var subProtections = HandleContentCheck(checkClass, fileName, fileContent, includeDebug); + if (subProtections != null) + { + // If we are filtering the output of the check + if (!CheckIfPacker(checkClass) || !scanPackers) + return; + + protections.AddRange(subProtections); + } + }); + + return protections; + } + + /// + /// Handle a single file based on all linear executable check implementations + /// + /// Name of the source file of the executable, for tracking + /// Executable to scan + /// True to include extractable contents in the output, false otherwise + /// True to include packers in the output, false otherwise + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentDictionary> HandleNewExecutableChecks(string fileName, LinearExecutable lex, bool scanArchives, bool scanPackers, bool includeDebug) + { + // Create the output dictionary + var protections = new ConcurrentDictionary>(); + + // Iterate through all checks + Parallel.ForEach(ScanningClasses.LinearExecutableCheckClasses, checkClass => + { + // Get the protection for the class, if possible + var subProtections = HandleLinearExecutableCheck(checkClass, fileName, lex, includeDebug); + if (subProtections != null) + { + // If we are filtering the output of the check + if (!CheckIfPacker(checkClass) || !scanPackers) + return; + + AppendToDictionary(protections, fileName, subProtections); + } + + // TODO: Handle extractable implementations + }); + + return protections; + } + + /// + /// Handle a single file based on all new executable check implementations + /// + /// Name of the source file of the executable, for tracking + /// Executable to scan + /// True to include extractable contents in the output, false otherwise + /// True to include packers in the output, false otherwise + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentDictionary> HandleNewExecutableChecks(string fileName, NewExecutable nex, bool scanArchives, bool scanPackers, bool includeDebug) + { + // Create the output dictionary + var protections = new ConcurrentDictionary>(); + + // Iterate through all checks + Parallel.ForEach(ScanningClasses.NewExecutableCheckClasses, checkClass => + { + // Get the protection for the class, if possible + var subProtections = HandleNewExecutableCheck(checkClass, fileName, nex, includeDebug); + if (subProtections != null) + { + // If we are filtering the output of the check + if (!CheckIfPacker(checkClass) || !scanPackers) + return; + + AppendToDictionary(protections, fileName, subProtections); + } + + // TODO: Handle extractable implementations + }); + + return protections; + } + + /// + /// Handle a single file based on all portable executable check implementations + /// + /// Name of the source file of the executable, for tracking + /// Executable to scan + /// True to include extractable contents in the output, false otherwise + /// True to include packers in the output, false otherwise + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentDictionary> HandlePortableExecutableChecks(string fileName, PortableExecutable pex, bool scanArchives, bool scanPackers, bool includeDebug) + { + // Create the output dictionary + var protections = new ConcurrentDictionary>(); + + // Iterate through all checks + Parallel.ForEach(ScanningClasses.PortableExecutableCheckClasses, checkClass => + { + // Get the protection for the class, if possible + var subProtections = HandlePortableExecutableCheck(checkClass, fileName, pex, includeDebug); + if (subProtections != null) + { + // If we are filtering the output of the check + if (!CheckIfPacker(checkClass) || !scanPackers) + return; + + AppendToDictionary(protections, fileName, subProtections); + } + + // TODO: Handle extractable implementations + }); + + return protections; + } + + #endregion + + #region Single Implementation Handlers + + /// + /// Handle files based on an IContentCheck implementation + /// + /// IDetectable class representing the file type + /// Name of the source file of the byte array, for tracking + /// Contents of the source file + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandleContentCheck(IContentCheck impl, string fileName, byte[] fileContent, bool includeDebug) + { + string protection = impl.CheckContents(fileName, fileContent, includeDebug); + return ProcessProtectionString(protection); + } + + /// + /// Handle files based on an IDetectable implementation + /// + /// IDetectable class representing the file type + /// Name of the source file of the stream, for tracking + /// Stream to scan the contents of + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug) + { + string protection = impl.Detect(stream, fileName, includeDebug); + return ProcessProtectionString(protection); + } + + /// + /// Handle files based on an ILinearExecutableCheck implementation + /// + /// ILinearExecutableCheck class representing the file type + /// Name of the source file of the executable, for tracking + /// LinearExecutable to check + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandleLinearExecutableCheck(ILinearExecutableCheck impl, string fileName, LinearExecutable lex, bool includeDebug) + { + string protection = impl.CheckLinearExecutable(fileName, lex, includeDebug); + return ProcessProtectionString(protection); + } + + /// + /// Handle files based on an INewExecutableCheck implementation + /// + /// INewExecutableCheck class representing the file type + /// Name of the source file of the executable, for tracking + /// NewExecutable to check + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandleNewExecutableCheck(INewExecutableCheck impl, string fileName, NewExecutable nex, bool includeDebug) + { + string protection = impl.CheckNewExecutable(fileName, nex, includeDebug); + return ProcessProtectionString(protection); + } + + /// + /// Handle files based on an IPortableExecutableCheck implementation + /// + /// IPortableExecutableCheck class representing the file type + /// Name of the source file of the executable, for tracking + /// NewExecutable to check + /// True to include debug data, false otherwise + /// Set of protections in file, null on error + public static ConcurrentQueue HandlePortableExecutableCheck(IPortableExecutableCheck impl, string fileName, PortableExecutable pex, bool includeDebug) + { + string protection = impl.CheckPortableExecutable(fileName, pex, includeDebug); + return ProcessProtectionString(protection); + } + + #endregion + + #region Helpers + + /// + /// Check to see if an implementation is a packer using reflection + /// + /// Implementation that was last used to check + private static bool CheckIfPacker(object impl) + { + return impl.GetType().Namespace.ToLowerInvariant().Contains("packer"); + } + + /// + /// Process a protection string if it includes multiple protections + /// + /// Protection string to process + /// Set of protections parsed, null on error + private static ConcurrentQueue ProcessProtectionString(string protection) + { + // If we have an invalid protection string + if (string.IsNullOrWhiteSpace(protection)) + return null; + + var protections = new ConcurrentQueue(); + + // If we have an indicator of multiple protections + if (protection.Contains(";")) + { + var splitProtections = protection.Split(';'); + protections.AddRange(splitProtections); + } + else + { + protections.Enqueue(protection); + } + + return protections; + } + + + #endregion + } +} diff --git a/BurnOutSharp/Scanner.cs b/BurnOutSharp/Scanner.cs index 33cb8821..30762d80 100644 --- a/BurnOutSharp/Scanner.cs +++ b/BurnOutSharp/Scanner.cs @@ -136,7 +136,7 @@ namespace BurnOutSharp if (ScanPaths) { var filePathProtections = GetFilePathProtections(file); - AppendToDictionary(protections, filePathProtections); + AppendToDictionary(protections, file, filePathProtections); } // Scan for content-detectable protections @@ -174,7 +174,7 @@ namespace BurnOutSharp if (ScanPaths) { var filePathProtections = GetFilePathProtections(path); - AppendToDictionary(protections, filePathProtections); + AppendToDictionary(protections, path, filePathProtections); } // Scan for content-detectable protections @@ -248,7 +248,7 @@ namespace BurnOutSharp /// /// Path of the file to scan /// Dictionary of list of strings representing the found protections - private ConcurrentDictionary> GetFilePathProtections(string path) + private ConcurrentQueue GetFilePathProtections(string path) { // Create an empty queue for protections var protections = new ConcurrentQueue(); @@ -262,10 +262,7 @@ namespace BurnOutSharp }); // Create and return the dictionary - return new ConcurrentDictionary> - { - [path] = protections - }; + return protections; } /// @@ -345,11 +342,28 @@ namespace BurnOutSharp #region Non-Archive File Types // Create a detectable for the given file type - var detectable = CreateDetectable(fileType); + var detectable = Factory.CreateDetectable(fileType); // If we're scanning file contents if (detectable != null && ScanContents) { + // If we have an executable, it needs to bypass normal handling + // TODO: Write custom executable handling + if (detectable is Executable) + { + var subProtections = Handler.HandleDetectable(detectable, fileName, stream, IncludeDebug); + if (subProtections != null) + AppendToDictionary(protections, fileName, subProtections); + } + + // Otherwise, use the default implementation + else + { + var subProtections = Handler.HandleDetectable(detectable, fileName, stream, IncludeDebug); + if (subProtections != null) + AppendToDictionary(protections, fileName, subProtections); + } + string subProtection = detectable.Detect(stream, fileName, IncludeDebug); if (!string.IsNullOrWhiteSpace(subProtection)) { @@ -367,7 +381,7 @@ namespace BurnOutSharp } // Create a scannable for the given file type - var scannable = CreateScannable(fileType); + var scannable = Factory.CreateScannable(fileType); // If we're scanning file contents if (scannable != null && ScanContents) @@ -381,31 +395,18 @@ namespace BurnOutSharp #region Archive File Types // Create an extractable for the given file type - var extractable = CreateExtractable(fileType); + var extractable = Factory.CreateExtractable(fileType); // If we're scanning archives if (extractable != null && ScanArchives) { // If we have an executable, it needs to bypass normal handling + // TODO: Write custom executable handling if (extractable is Executable) { var subProtections = HandleExtractable(extractable, fileName, stream); if (subProtections != null) AppendToDictionary(protections, subProtections); - - // The following code is disabled because it is untested, though it represents the likely path - // for how this implementation will be completed. - - //Parallel.ForEach(ScanningClasses.ExtractableClasses, extractableClass => - //{ - // string tempDir = extractableClass.Extract(stream, fileName, IncludeDebug); - // if (!string.IsNullOrWhiteSpace(tempDir)) - // { - // var subProtections = HandleExtractable(extractable, fileName, stream); - // if (subProtections != null) - // AppendToDictionary(protections, subProtections); - // } - //}); } // Otherwise, use the default implementation @@ -434,85 +435,10 @@ namespace BurnOutSharp #endregion - #region Helpers + #region Interface Handlers /// - /// Create an instance of a detectable based on file type - /// - private static IDetectable CreateDetectable(SupportedFileType fileType) - { - switch (fileType) - { - case SupportedFileType.AACSMediaKeyBlock: return new BinaryObjectScanner.FileType.AACSMediaKeyBlock(); - case SupportedFileType.BDPlusSVM: return new BinaryObjectScanner.FileType.BDPlusSVM(); - //case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA(); - case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); - case SupportedFileType.LDSCRYPT: return new BinaryObjectScanner.FileType.LDSCRYPT(); - //case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS(); - //case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro(); - case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ(); - case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS(); - case SupportedFileType.Textfile: return new BinaryObjectScanner.FileType.Textfile(); - default: return null; - } - } - - /// - /// Create an instance of an extractable based on file type - /// - private static IExtractable CreateExtractable(SupportedFileType fileType) - { - switch (fileType) - { - case SupportedFileType.BFPK: return new BinaryObjectScanner.FileType.BFPK(); - case SupportedFileType.BSP: return new BinaryObjectScanner.FileType.BSP(); - case SupportedFileType.BZip2: return new BinaryObjectScanner.FileType.BZip2(); - case SupportedFileType.CFB: return new BinaryObjectScanner.FileType.CFB(); - //case SupportedFileType.CIA: return new BinaryObjectScanner.FileType.CIA(); - case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); - case SupportedFileType.GCF: return new BinaryObjectScanner.FileType.GCF(); - case SupportedFileType.GZIP: return new BinaryObjectScanner.FileType.GZIP(); - case SupportedFileType.InstallShieldArchiveV3: return new BinaryObjectScanner.FileType.InstallShieldArchiveV3(); - case SupportedFileType.InstallShieldCAB: return new BinaryObjectScanner.FileType.InstallShieldCAB(); - case SupportedFileType.MicrosoftCAB: return new BinaryObjectScanner.FileType.MicrosoftCAB(); - case SupportedFileType.MicrosoftLZ: return new BinaryObjectScanner.FileType.MicrosoftLZ(); - case SupportedFileType.MPQ: return new BinaryObjectScanner.FileType.MPQ(); - //case SupportedFileType.N3DS: return new BinaryObjectScanner.FileType.N3DS(); - //case SupportedFileType.NCF: return new BinaryObjectScanner.FileType.NCF(); - //case SupportedFileType.Nitro: return new BinaryObjectScanner.FileType.Nitro(); - case SupportedFileType.PAK: return new BinaryObjectScanner.FileType.PAK(); - case SupportedFileType.PFF: return new BinaryObjectScanner.FileType.PFF(); - case SupportedFileType.PKZIP: return new BinaryObjectScanner.FileType.PKZIP(); - //case SupportedFileType.PLJ: return new BinaryObjectScanner.FileType.PLJ(); - //case SupportedFileType.Quantum: return new BinaryObjectScanner.FileType.Quantum(); - case SupportedFileType.RAR: return new BinaryObjectScanner.FileType.RAR(); - case SupportedFileType.SevenZip: return new BinaryObjectScanner.FileType.SevenZip(); - case SupportedFileType.SFFS: return new BinaryObjectScanner.FileType.SFFS(); - case SupportedFileType.SGA: return new BinaryObjectScanner.FileType.SGA(); - case SupportedFileType.TapeArchive: return new BinaryObjectScanner.FileType.TapeArchive(); - case SupportedFileType.VBSP: return new BinaryObjectScanner.FileType.VBSP(); - case SupportedFileType.VPK: return new BinaryObjectScanner.FileType.VPK(); - case SupportedFileType.WAD: return new BinaryObjectScanner.FileType.WAD(); - case SupportedFileType.XZ: return new BinaryObjectScanner.FileType.XZ(); - case SupportedFileType.XZP: return new BinaryObjectScanner.FileType.XZP(); - default: return null; - } - } - - /// - /// Create an instance of a scannable based on file type - /// - private static IScannable CreateScannable(SupportedFileType fileType) - { - switch (fileType) - { - case SupportedFileType.Executable: return new BinaryObjectScanner.FileType.Executable(); - default: return null; - } - } - - /// - /// Handle extractable files based on an IExtractable implementation + /// Handle files based on an IExtractable implementation /// /// IExtractable class representing the file type /// Name of the source file of the stream, for tracking