diff --git a/BinaryObjectScanner/FileType/Executable.cs b/BinaryObjectScanner/FileType/Executable.cs
index faca897c..4890f08a 100644
--- a/BinaryObjectScanner/FileType/Executable.cs
+++ b/BinaryObjectScanner/FileType/Executable.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using BinaryObjectScanner.Data;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
+using SabreTools.Serialization.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner.FileType
@@ -44,43 +46,94 @@ namespace BinaryObjectScanner.FileType
///
public string? Detect(Stream stream, string file, bool includeDebug)
{
- // Try to create a wrapper for the proper executable type
- var wrapper = WrapperFactory.CreateExecutableWrapper(stream);
- if (wrapper == null)
+ // Get all non-nested protections
+ var protections = DetectDict(stream, file, scanner: null, includeDebug);
+ if (protections.Count == 0)
return null;
// Create the internal list
- var protections = new List();
+ var protectionList = new List();
+ foreach (string key in protections.Keys)
+ {
+ protectionList.AddRange(protections[key]);
+ }
+
+ return string.Join(";", [.. protections]);
+ }
+
+ ///
+ ///
+ /// Ideally, we wouldn't need to circumvent the proper handling of file types just for Executable,
+ /// but due to the complexity of scanning, this is not currently possible.
+ ///
+ public ProtectionDictionary DetectDict(Stream stream, string file, Scanner? scanner, bool includeDebug)
+ {
+ // Create the output dictionary
+ var protections = new ProtectionDictionary();
+
+ // Try to create a wrapper for the proper executable type
+ IWrapper? wrapper;
+ try
+ {
+ wrapper = WrapperFactory.CreateExecutableWrapper(stream);
+ if (wrapper == null)
+ return protections;
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.WriteLine(ex);
+ return protections;
+ }
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
- var contentProtections = RunContentChecks(file, stream, includeDebug);
- protections.AddRange(contentProtections.Values);
+ var subProtections = RunContentChecks(file, stream, includeDebug);
+ protections.Append(file, subProtections.Values);
}
if (wrapper is MSDOS mz)
{
+ // Standard checks
var subProtections = RunExecutableChecks(file, mz, StaticChecks.MSDOSExecutableCheckClasses, includeDebug);
- protections.AddRange(subProtections.Values);
+ protections.Append(file, subProtections.Values);
+
+ // Extractable checks
+ var extractedProtections = HandleExtractableProtections(file, mz, subProtections.Keys, scanner, includeDebug);
+ protections.Append(extractedProtections);
}
else if (wrapper is LinearExecutable lex)
{
+ // Standard checks
var subProtections = RunExecutableChecks(file, lex, StaticChecks.LinearExecutableCheckClasses, includeDebug);
- protections.AddRange(subProtections.Values);
+ protections.Append(file, subProtections.Values);
+
+ // Extractable checks
+ var extractedProtections = HandleExtractableProtections(file, lex, subProtections.Keys, scanner, includeDebug);
+ protections.Append(extractedProtections);
}
else if (wrapper is NewExecutable nex)
{
+ // Standard checks
var subProtections = RunExecutableChecks(file, nex, StaticChecks.NewExecutableCheckClasses, includeDebug);
- protections.AddRange(subProtections.Values);
+ protections.Append(file, subProtections.Values);
+
+ // Extractable checks
+ var extractedProtections = HandleExtractableProtections(file, nex, subProtections.Keys, scanner, includeDebug);
+ protections.Append(extractedProtections);
}
else if (wrapper is PortableExecutable pex)
{
+ // Standard checks
var subProtections = RunExecutableChecks(file, pex, StaticChecks.PortableExecutableCheckClasses, includeDebug);
- protections.AddRange(subProtections.Values);
+ protections.Append(file, subProtections.Values);
+
+ // Extractable checks
+ var extractedProtections = HandleExtractableProtections(file, pex, subProtections.Keys, scanner, includeDebug);
+ protections.Append(extractedProtections);
}
- return string.Join(";", [.. protections]);
+ return protections;
}
#region Check Runners
@@ -145,6 +198,7 @@ namespace BinaryObjectScanner.FileType
/// Name of the source file of the executable, for tracking
/// Executable to scan
/// Set of checks to use
+ /// Scanner for handling recursive protections
/// True to include debug data, false otherwise
/// Set of protections in file, empty on error
public IDictionary RunExecutableChecks(string file, T exe, List checks, bool includeDebug)
@@ -176,6 +230,74 @@ namespace BinaryObjectScanner.FileType
return protections;
}
+ ///
+ /// Handle extractable protections, such as executable packers
+ ///
+ /// Name of the source file of the stream, for tracking
+ /// Executable to scan the contents of
+ /// Set of classes returned from Exectuable scans
+ /// Scanner for handling recursive protections
+ /// True to include debug data, false otherwise
+ /// Set of protections found from extraction, empty on error
+ private ProtectionDictionary HandleExtractableProtections(string file, T exe, IEnumerable checks, Scanner? scanner, bool includeDebug)
+ where T : WrapperBase
+ where U : IExecutableCheck
+ {
+ // Create the output dictionary
+ var protections = new ProtectionDictionary();
+
+ // If we have an invalid set of classes
+ if (checks == null || !checks.Any())
+ return protections;
+
+ // If we have any extractable packers
+ var extractables = checks
+ .Where(c => c is IExtractableExecutable)
+ .Select(c => c as IExtractableExecutable);
+ extractables.IterateWithAction(extractable =>
+ {
+ // If we have an invalid extractable somehow
+ if (extractable == null)
+ return;
+
+ // If the extractable file itself fails
+ try
+ {
+ // Extract and get the output path
+ string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ bool extracted = extractable.Extract(file, exe, tempPath, includeDebug);
+
+ // Collect and format all found protections
+ ProtectionDictionary? subProtections = null;
+ if (extracted)
+ subProtections = scanner?.GetProtections(tempPath);
+
+ // If temp directory cleanup fails
+ try
+ {
+ if (Directory.Exists(tempPath))
+ Directory.Delete(tempPath, true);
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.WriteLine(ex);
+ }
+
+ // Prepare the returned protections
+ subProtections?.StripFromKeys(tempPath);
+ subProtections?.PrependToKeys(file);
+ if (subProtections != null)
+ protections.Append(subProtections);
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.WriteLine(ex);
+ }
+ });
+
+ return protections;
+ }
+
#endregion
#region Helpers
diff --git a/BinaryObjectScanner/Handler.cs b/BinaryObjectScanner/Handler.cs
deleted file mode 100644
index 1ccfc436..00000000
--- a/BinaryObjectScanner/Handler.cs
+++ /dev/null
@@ -1,126 +0,0 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using BinaryObjectScanner.Data;
-using BinaryObjectScanner.Interfaces;
-
-namespace BinaryObjectScanner
-{
- internal static class Handler
- {
- #region Multiple Implementation Wrappers
-
- ///
- /// Handle a single path based on all path check implementations
- ///
- /// Path of the file or directory to check
- /// Scanner object to use for options and scanning
- /// Set of protections in file, null on error
- public static ProtectionDictionary HandlePathChecks(string path, IEnumerable? files)
- {
- // Create the output dictionary
- var protections = new ProtectionDictionary();
-
- // Preprocess the list of files
- files = files?
- .Select(f => f.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))?
- .ToList();
-
- // Iterate through all checks
- StaticChecks.PathCheckClasses.IterateWithAction(checkClass =>
- {
- var subProtections = checkClass.PerformCheck(path, files);
- protections.Append(path, subProtections);
- });
-
- return protections;
- }
-
- #endregion
-
- #region Single Implementation Handlers
-
- ///
- /// 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 List? HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug)
- {
- var protection = impl.Detect(stream, fileName, includeDebug);
- return ProcessProtectionString(protection);
- }
-
- ///
- /// Handle files based on an IPathCheck implementation
- ///
- /// IPathCheck class representing the file type
- /// Path of the file or directory to check
- /// Set of protections in path, empty on error
- private static List PerformCheck(this IPathCheck impl, string? path, IEnumerable? files)
- {
- // If we have an invalid path
- if (string.IsNullOrEmpty(path))
- return [];
-
- // Setup the list
- var protections = new List();
-
- // If we have a file path
- if (File.Exists(path))
- {
- var protection = impl.CheckFilePath(path!);
- var subProtections = ProcessProtectionString(protection);
- if (subProtections != null)
- protections.AddRange(subProtections);
- }
-
- // If we have a directory path
- if (Directory.Exists(path) && files?.Any() == true)
- {
- var subProtections = impl.CheckDirectoryPath(path!, files);
- if (subProtections != null)
- protections.AddRange(subProtections);
- }
-
- return protections;
- }
-
- #endregion
-
- #region Helpers
-
- ///
- /// Process a protection string if it includes multiple protections
- ///
- /// Protection string to process
- /// Set of protections parsed, null on error
- private static List? ProcessProtectionString(string? protection)
- {
- // If we have an invalid protection string
- if (string.IsNullOrEmpty(protection))
- return null;
-
- // Setup the output queue
- var protections = new List();
-
- // If we have an indicator of multiple protections
- if (protection!.Contains(";"))
- {
- var splitProtections = protection.Split(';');
- protections.AddRange(splitProtections);
- }
- else
- {
- protections.Add(protection);
- }
-
- return protections;
- }
-
- #endregion
- }
-}
diff --git a/BinaryObjectScanner/Scanner.cs b/BinaryObjectScanner/Scanner.cs
index c5288265..2a71b4ae 100644
--- a/BinaryObjectScanner/Scanner.cs
+++ b/BinaryObjectScanner/Scanner.cs
@@ -6,7 +6,6 @@ using BinaryObjectScanner.Data;
using BinaryObjectScanner.FileType;
using BinaryObjectScanner.Interfaces;
using SabreTools.IO.Extensions;
-using SabreTools.Serialization.Interfaces;
using SabreTools.Serialization.Wrappers;
namespace BinaryObjectScanner
@@ -70,18 +69,18 @@ namespace BinaryObjectScanner
///
/// Path to scan
/// Dictionary of list of strings representing the found protections
- public ProtectionDictionary? GetProtections(string path)
+ public ProtectionDictionary GetProtections(string path)
=> GetProtections([path]);
///
/// Scan the list of paths and get all found protections
///
/// Dictionary of list of strings representing the found protections
- public ProtectionDictionary? GetProtections(List? paths)
+ public ProtectionDictionary GetProtections(List? paths)
{
// If we have no paths, we can't scan
if (paths == null || !paths.Any())
- return null;
+ return [];
// Set a starting starting time for debug output
DateTime startTime = DateTime.UtcNow;
@@ -106,7 +105,7 @@ namespace BinaryObjectScanner
// Scan for path-detectable protections
if (_options.ScanPaths)
{
- var directoryPathProtections = Handler.HandlePathChecks(path, files);
+ var directoryPathProtections = HandlePathChecks(path, files);
protections.Append(directoryPathProtections);
}
@@ -127,7 +126,7 @@ namespace BinaryObjectScanner
// Scan for path-detectable protections
if (_options.ScanPaths)
{
- var filePathProtections = Handler.HandlePathChecks(file, files: null);
+ var filePathProtections = HandlePathChecks(file, files: null);
if (filePathProtections != null && filePathProtections.Any())
protections.Append(filePathProtections);
}
@@ -158,7 +157,7 @@ namespace BinaryObjectScanner
// Scan for path-detectable protections
if (_options.ScanPaths)
{
- var filePathProtections = Handler.HandlePathChecks(path, files: null);
+ var filePathProtections = HandlePathChecks(path, files: null);
if (filePathProtections != null && filePathProtections.Any())
protections.Append(filePathProtections);
}
@@ -197,11 +196,11 @@ namespace BinaryObjectScanner
///
/// Path to the file to scan
/// Dictionary of list of strings representing the found protections
- private ProtectionDictionary? GetInternalProtections(string file)
+ private ProtectionDictionary GetInternalProtections(string file)
{
// Quick sanity check before continuing
if (!File.Exists(file))
- return null;
+ return [];
// Open the file and begin scanning
try
@@ -226,11 +225,11 @@ namespace BinaryObjectScanner
/// Name of the source file of the stream, for tracking
/// Stream to scan the contents of
/// Dictionary of list of strings representing the found protections
- private ProtectionDictionary? GetInternalProtections(string fileName, Stream stream)
+ private ProtectionDictionary GetInternalProtections(string fileName, Stream stream)
{
// Quick sanity check before continuing
if (stream == null || !stream.CanRead || !stream.CanSeek)
- return null;
+ return [];
// Initialize the protections found
var protections = new ProtectionDictionary();
@@ -252,13 +251,13 @@ namespace BinaryObjectScanner
{
if (_options.IncludeDebug) Console.WriteLine(ex);
- return null;
+ return [];
}
// Get the file type either from magic number or extension
WrapperType fileType = WrapperFactory.GetFileType(magic, extension);
if (fileType == WrapperType.UNKNOWN)
- return null;
+ return [];
#region Non-Archive File Types
@@ -273,32 +272,16 @@ namespace BinaryObjectScanner
{
executable.IncludeGameEngines = _options.ScanGameEngines;
executable.IncludePackers = _options.ScanPackers;
- var subProtections = ProcessExecutable(executable, fileName, stream);
- if (subProtections != null)
- protections.Append(subProtections);
+
+ var subProtections = executable.DetectDict(stream, fileName, this, _options.IncludeDebug);
+ protections.Append(subProtections);
}
// Otherwise, use the default implementation
else
{
- var subProtections = Handler.HandleDetectable(detectable, fileName, stream, _options.IncludeDebug);
- if (subProtections != null)
- protections.Append(fileName, subProtections);
- }
-
- var subProtection = detectable.Detect(stream, fileName, _options.IncludeDebug);
- if (!string.IsNullOrEmpty(subProtection))
- {
- // If we have an indicator of multiple protections
- if (subProtection.Contains(';'))
- {
- var splitProtections = subProtection!.Split(';');
- protections.Append(fileName, splitProtections);
- }
- else
- {
- protections.Append(fileName, subProtection!);
- }
+ var subProtection = detectable.Detect(stream, fileName, _options.IncludeDebug);
+ protections.Append(fileName, ProcessProtectionString(subProtection));
}
}
@@ -363,150 +346,96 @@ namespace BinaryObjectScanner
#endregion
- #region Executable Handling
+ #region Path Handling
///
- /// Process scanning for an Executable type
+ /// Handle a single path based on all path check implementations
///
- /// Executable instance for processing
- /// Name of the source file of the stream, for tracking
- /// Stream to scan the contents of
- ///
- /// Ideally, we wouldn't need to circumvent the proper handling of file types just for Executable,
- /// but due to the complexity of scanning, this is not currently possible.
- ///
- private ProtectionDictionary? ProcessExecutable(Executable executable, string fileName, Stream stream)
+ /// Path of the file or directory to check
+ /// Scanner object to use for options and scanning
+ /// Set of protections in file, null on error
+ private static ProtectionDictionary HandlePathChecks(string path, IEnumerable? files)
{
- // Try to create a wrapper for the proper executable type
- IWrapper? wrapper;
- try
- {
- wrapper = WrapperFactory.CreateExecutableWrapper(stream);
- if (wrapper == null)
- return null;
- }
- catch (Exception ex)
- {
- if (_options.IncludeDebug) Console.WriteLine(ex);
- return null;
- }
-
// Create the output dictionary
var protections = new ProtectionDictionary();
- // Only use generic content checks if we're in debug mode
- if (_options.IncludeDebug)
- {
- var subProtections = executable.RunContentChecks(fileName, stream, _options.IncludeDebug);
- protections.Append(fileName, subProtections.Values);
- }
+ // Preprocess the list of files
+ files = files?
+ .Select(f => f.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))?
+ .ToList();
- if (wrapper is MSDOS mz)
+ // Iterate through all checks
+ StaticChecks.PathCheckClasses.IterateWithAction(checkClass =>
{
- // Standard checks
- var subProtections = executable.RunExecutableChecks(fileName, mz, StaticChecks.MSDOSExecutableCheckClasses, _options.IncludeDebug);
- protections.Append(fileName, subProtections.Values);
-
- // Extractable checks
- var extractedProtections = HandleExtractableProtections(fileName, mz, subProtections.Keys);
- protections.Append(extractedProtections);
- }
- else if (wrapper is LinearExecutable lex)
- {
- // Standard checks
- var subProtections = executable.RunExecutableChecks(fileName, lex, StaticChecks.LinearExecutableCheckClasses, _options.IncludeDebug);
- protections.Append(fileName, subProtections.Values);
-
- // Extractable checks
- var extractedProtections = HandleExtractableProtections(fileName, lex, subProtections.Keys);
- protections.Append(extractedProtections);
- }
- else if (wrapper is NewExecutable nex)
- {
- // Standard checks
- var subProtections = executable.RunExecutableChecks(fileName, nex, StaticChecks.NewExecutableCheckClasses, _options.IncludeDebug);
- protections.Append(fileName, subProtections.Values);
-
- // Extractable checks
- var extractedProtections = HandleExtractableProtections(fileName, nex, subProtections.Keys);
- protections.Append(extractedProtections);
- }
- else if (wrapper is PortableExecutable pex)
- {
- // Standard checks
- var subProtections = executable.RunExecutableChecks(fileName, pex, StaticChecks.PortableExecutableCheckClasses, _options.IncludeDebug);
- protections.Append(fileName, subProtections.Values);
-
- // Extractable checks
- var extractedProtections = HandleExtractableProtections(fileName, pex, subProtections.Keys);
- protections.Append(extractedProtections);
- }
+ var subProtections = PerformCheck(checkClass, path, files);
+ protections.Append(path, subProtections);
+ });
return protections;
}
///
- /// Handle extractable protections, such as executable packers
+ /// Handle files based on an IPathCheck implementation
///
- /// Name of the source file of the stream, for tracking
- /// Executable to scan the contents of
- /// Set of classes returned from Exectuable scans
- /// Set of protections found from extraction, null on error
- private ProtectionDictionary HandleExtractableProtections(string file, T exe, IEnumerable checks)
- where T : WrapperBase
- where U : IExecutableCheck
+ /// IPathCheck class representing the file type
+ /// Path of the file or directory to check
+ /// Set of protections in path, empty on error
+ private static List PerformCheck(IPathCheck impl, string? path, IEnumerable? files)
{
- // Create the output dictionary
- var protections = new ProtectionDictionary();
+ // If we have an invalid path
+ if (string.IsNullOrEmpty(path))
+ return [];
- // If we have an invalid set of classes
- if (checks == null || !checks.Any())
- return protections;
+ // Setup the list
+ var protections = new List();
- // If we have any extractable packers
- var extractables = checks
- .Where(c => c is IExtractableExecutable)
- .Select(c => c as IExtractableExecutable);
- extractables.IterateWithAction(extractable =>
+ // If we have a file path
+ if (File.Exists(path))
{
- // If we have an invalid extractable somehow
- if (extractable == null)
- return;
+ var protection = impl.CheckFilePath(path!);
+ var subProtections = ProcessProtectionString(protection);
+ protections.AddRange(subProtections);
+ }
- // If the extractable file itself fails
- try
- {
- // Extract and get the output path
- string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- bool extracted = extractable.Extract(file, exe, tempPath, _options.IncludeDebug);
+ // If we have a directory path
+ if (Directory.Exists(path) && files?.Any() == true)
+ {
+ var subProtections = impl.CheckDirectoryPath(path!, files);
+ if (subProtections != null)
+ protections.AddRange(subProtections);
+ }
- // Collect and format all found protections
- ProtectionDictionary? subProtections = null;
- if (extracted)
- subProtections = GetProtections(tempPath);
+ return protections;
+ }
- // If temp directory cleanup fails
- try
- {
- if (Directory.Exists(tempPath))
- Directory.Delete(tempPath, true);
- }
- catch (Exception ex)
- {
- if (_options.IncludeDebug) Console.WriteLine(ex);
- }
+ #endregion
- // Prepare the returned protections
- subProtections?.StripFromKeys(tempPath);
- subProtections?.PrependToKeys(file);
- if (subProtections != null)
- protections.Append(subProtections);
- }
- catch (Exception ex)
- {
- if (_options.IncludeDebug) Console.WriteLine(ex);
- }
- });
+ #region Helpers
+
+ ///
+ /// Process a protection string if it includes multiple protections
+ ///
+ /// Protection string to process
+ /// Set of protections parsed, empty on error
+ internal static List ProcessProtectionString(string? protection)
+ {
+ // If we have an invalid protection string
+ if (string.IsNullOrEmpty(protection))
+ return [];
+
+ // Setup the output queue
+ var protections = new List();
+
+ // If we have an indicator of multiple protections
+ if (protection!.Contains(";"))
+ {
+ var splitProtections = protection.Split(';');
+ protections.AddRange(splitProtections);
+ }
+ else
+ {
+ protections.Add(protection);
+ }
return protections;
}