mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-28 17:37:20 +00:00
Move all executable handling to Executable
This commit is contained in:
@@ -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
|
||||
/// <inheritdoc/>
|
||||
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<string>();
|
||||
var protectionList = new List<string>();
|
||||
foreach (string key in protections.Keys)
|
||||
{
|
||||
protectionList.AddRange(protections[key]);
|
||||
}
|
||||
|
||||
return string.Join(";", [.. protections]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IDetectable.Detect(Stream, string, bool)"/>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="exe">Executable to scan</param>
|
||||
/// <param name="checks">Set of checks to use</param>
|
||||
/// <param name="scanner">Scanner for handling recursive protections</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, empty on error</returns>
|
||||
public IDictionary<U, string> RunExecutableChecks<T, U>(string file, T exe, List<U> checks, bool includeDebug)
|
||||
@@ -176,6 +230,74 @@ namespace BinaryObjectScanner.FileType
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle extractable protections, such as executable packers
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="exe">Executable to scan the contents of</param>
|
||||
/// <param name="checks">Set of classes returned from Exectuable scans</param>
|
||||
/// <param name="scanner">Scanner for handling recursive protections</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections found from extraction, empty on error</returns>
|
||||
private ProtectionDictionary HandleExtractableProtections<T, U>(string file, T exe, IEnumerable<U> checks, Scanner? scanner, bool includeDebug)
|
||||
where T : WrapperBase
|
||||
where U : IExecutableCheck<T>
|
||||
{
|
||||
// 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<T>)
|
||||
.Select(c => c as IExtractableExecutable<T>);
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single path based on all path check implementations
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <param name="scanner">Scanner object to use for options and scanning</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public static ProtectionDictionary HandlePathChecks(string path, IEnumerable<string>? 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
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IDetectable implementation
|
||||
/// </summary>
|
||||
/// <param name="impl">IDetectable class representing the file type</param>
|
||||
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public static List<string>? HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug)
|
||||
{
|
||||
var protection = impl.Detect(stream, fileName, includeDebug);
|
||||
return ProcessProtectionString(protection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle files based on an IPathCheck implementation
|
||||
/// </summary>
|
||||
/// <param name="impl">IPathCheck class representing the file type</param>
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <returns>Set of protections in path, empty on error</returns>
|
||||
private static List<string> PerformCheck(this IPathCheck impl, string? path, IEnumerable<string>? files)
|
||||
{
|
||||
// If we have an invalid path
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return [];
|
||||
|
||||
// Setup the list
|
||||
var protections = new List<string>();
|
||||
|
||||
// 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
|
||||
|
||||
/// <summary>
|
||||
/// Process a protection string if it includes multiple protections
|
||||
/// </summary>
|
||||
/// <param name="protection">Protection string to process</param>
|
||||
/// <returns>Set of protections parsed, null on error</returns>
|
||||
private static List<string>? ProcessProtectionString(string? protection)
|
||||
{
|
||||
// If we have an invalid protection string
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
return null;
|
||||
|
||||
// Setup the output queue
|
||||
var protections = new List<string>();
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan</param>
|
||||
/// <returns>Dictionary of list of strings representing the found protections</returns>
|
||||
public ProtectionDictionary? GetProtections(string path)
|
||||
public ProtectionDictionary GetProtections(string path)
|
||||
=> GetProtections([path]);
|
||||
|
||||
/// <summary>
|
||||
/// Scan the list of paths and get all found protections
|
||||
/// </summary>
|
||||
/// <returns>Dictionary of list of strings representing the found protections</returns>
|
||||
public ProtectionDictionary? GetProtections(List<string>? paths)
|
||||
public ProtectionDictionary GetProtections(List<string>? 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
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the file to scan</param>
|
||||
/// <returns>Dictionary of list of strings representing the found protections</returns>
|
||||
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
|
||||
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <returns>Dictionary of list of strings representing the found protections</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Process scanning for an Executable type
|
||||
/// Handle a single path based on all path check implementations
|
||||
/// </summary>
|
||||
/// <param name="executable">Executable instance for processing</param>
|
||||
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
private ProtectionDictionary? ProcessExecutable(Executable executable, string fileName, Stream stream)
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <param name="scanner">Scanner object to use for options and scanning</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
private static ProtectionDictionary HandlePathChecks(string path, IEnumerable<string>? 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle extractable protections, such as executable packers
|
||||
/// Handle files based on an IPathCheck implementation
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="exe">Executable to scan the contents of</param>
|
||||
/// <param name="checks">Set of classes returned from Exectuable scans</param>
|
||||
/// <returns>Set of protections found from extraction, null on error</returns>
|
||||
private ProtectionDictionary HandleExtractableProtections<T, U>(string file, T exe, IEnumerable<U> checks)
|
||||
where T : WrapperBase
|
||||
where U : IExecutableCheck<T>
|
||||
/// <param name="impl">IPathCheck class representing the file type</param>
|
||||
/// <param name="path">Path of the file or directory to check</param>
|
||||
/// <returns>Set of protections in path, empty on error</returns>
|
||||
private static List<string> PerformCheck(IPathCheck impl, string? path, IEnumerable<string>? 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<string>();
|
||||
|
||||
// If we have any extractable packers
|
||||
var extractables = checks
|
||||
.Where(c => c is IExtractableExecutable<T>)
|
||||
.Select(c => c as IExtractableExecutable<T>);
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Process a protection string if it includes multiple protections
|
||||
/// </summary>
|
||||
/// <param name="protection">Protection string to process</param>
|
||||
/// <returns>Set of protections parsed, empty on error</returns>
|
||||
internal static List<string> ProcessProtectionString(string? protection)
|
||||
{
|
||||
// If we have an invalid protection string
|
||||
if (string.IsNullOrEmpty(protection))
|
||||
return [];
|
||||
|
||||
// Setup the output queue
|
||||
var protections = new List<string>();
|
||||
|
||||
// If we have an indicator of multiple protections
|
||||
if (protection!.Contains(";"))
|
||||
{
|
||||
var splitProtections = protection.Split(';');
|
||||
protections.AddRange(splitProtections);
|
||||
}
|
||||
else
|
||||
{
|
||||
protections.Add(protection);
|
||||
}
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user