2020-10-30 23:56:27 -07:00
using System ;
2021-07-18 09:44:23 -07:00
using System.Collections.Concurrent ;
2020-10-30 23:56:27 -07:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2022-12-03 22:47:57 -08:00
using System.Text ;
2023-03-15 15:51:38 -04:00
using System.Threading.Tasks ;
2023-03-13 22:36:28 -04:00
using BinaryObjectScanner.FileType ;
2023-03-15 15:51:38 -04:00
using BinaryObjectScanner.Interfaces ;
2023-03-07 12:04:48 -05:00
using BinaryObjectScanner.Utilities ;
2023-03-15 11:37:20 -04:00
using BinaryObjectScanner.Wrappers ;
2023-03-07 12:04:48 -05:00
using static BinaryObjectScanner . Utilities . Dictionary ;
2020-10-30 23:56:27 -07:00
namespace BurnOutSharp
{
public class Scanner
{
2022-05-01 14:16:53 -07:00
#region Options
2023-03-09 13:26:20 -05:00
/// <inheritdoc cref="Options.ScanArchives"/>
public bool ScanArchives = > options ? . ScanArchives ? ? false ;
2020-10-30 23:56:27 -07:00
2023-03-09 13:26:20 -05:00
/// <inheritdoc cref="Options.ScanContents"/>
public bool ScanContents = > options ? . ScanContents ? ? false ;
2023-01-05 10:53:06 -08:00
2023-03-21 10:42:14 -04:00
/// <inheritdoc cref="Options.ScanGameEngines"/>
public bool ScanGameEngines = > options ? . ScanGameEngines ? ? false ;
/// <inheritdoc cref="Options.ScanPackers"/>
2023-03-09 13:26:20 -05:00
public bool ScanPackers = > options ? . ScanPackers ? ? false ;
2020-10-30 23:56:27 -07:00
2023-03-21 10:42:14 -04:00
/// <inheritdoc cref="Options.ScanPaths"/>
2023-03-09 13:26:20 -05:00
public bool ScanPaths = > options ? . ScanPaths ? ? false ;
/// <inheritdoc cref="Options.IncludeDebug"/>
public bool IncludeDebug = > options ? . IncludeDebug ? ? false ;
2023-01-05 10:46:13 -08:00
2020-10-30 23:56:27 -07:00
/// <summary>
2023-03-09 13:26:20 -05:00
/// Options object for configuration
2020-10-30 23:56:27 -07:00
/// </summary>
2023-03-09 13:26:20 -05:00
private readonly Options options ;
2020-10-30 23:56:27 -07:00
2022-05-01 16:59:03 -07:00
#endregion
2020-10-31 14:46:08 -07:00
/// <summary>
2022-05-01 14:11:09 -07:00
/// Optional progress callback during scanning
2020-10-31 14:46:08 -07:00
/// </summary>
2022-05-01 16:59:03 -07:00
private readonly IProgress < ProtectionProgress > fileProgress ;
2022-05-01 14:16:53 -07:00
2020-10-30 23:56:27 -07:00
/// <summary>
/// Constructor
/// </summary>
2022-05-01 14:11:09 -07:00
/// <param name="scanArchives">Enable scanning archive contents</param>
2023-01-05 10:53:06 -08:00
/// <param name="scanContents">Enable including content detections in output</param>
2023-03-21 10:42:14 -04:00
/// <param name="scanGameEngines">Enable including game engines in output</param>
2022-05-01 14:11:09 -07:00
/// <param name="scanPackers">Enable including packers in output</param>
2023-01-05 10:46:13 -08:00
/// <param name="scanPaths">Enable including path detections in output</param>
2022-05-01 14:11:09 -07:00
/// <param name="includeDebug">Enable including debug information</param>
2020-10-30 23:56:27 -07:00
/// <param name="fileProgress">Optional progress callback</param>
2023-03-21 10:42:14 -04:00
public Scanner ( bool scanArchives , bool scanContents , bool scanGameEngines , bool scanPackers , bool scanPaths , bool includeDebug , IProgress < ProtectionProgress > fileProgress = null )
2020-10-30 23:56:27 -07:00
{
2023-03-09 13:26:20 -05:00
this . options = new Options
{
ScanArchives = scanArchives ,
ScanContents = scanContents ,
2023-03-21 10:42:14 -04:00
ScanGameEngines = scanGameEngines ,
2023-03-09 13:26:20 -05:00
ScanPackers = scanPackers ,
ScanPaths = scanPaths ,
IncludeDebug = includeDebug ,
} ;
2022-05-01 16:59:03 -07:00
this . fileProgress = fileProgress ;
2022-12-03 22:47:57 -08:00
// Register the codepages
Encoding . RegisterProvider ( CodePagesEncodingProvider . Instance ) ;
2020-10-30 23:56:27 -07:00
}
2022-05-01 14:16:53 -07:00
#region Scanning
2020-10-31 00:06:41 -07:00
/// <summary>
2020-10-31 13:20:54 -07:00
/// Scan a single path and get all found protections
2020-10-31 00:06:41 -07:00
/// </summary>
2020-10-31 13:20:54 -07:00
/// <param name="path">Path to scan</param>
/// <returns>Dictionary of list of strings representing the found protections</returns>
2021-07-18 09:44:23 -07:00
public ConcurrentDictionary < string , ConcurrentQueue < string > > GetProtections ( string path )
2020-10-31 00:06:41 -07:00
{
2020-10-31 13:20:54 -07:00
return GetProtections ( new List < string > { path } ) ;
2020-10-31 00:06:41 -07:00
}
2020-10-30 23:56:27 -07:00
/// <summary>
/// Scan the list of paths and get all found protections
/// </summary>
/// <returns>Dictionary of list of strings representing the found protections</returns>
2021-07-18 09:44:23 -07:00
public ConcurrentDictionary < string , ConcurrentQueue < string > > GetProtections ( List < string > paths )
2020-10-30 23:56:27 -07:00
{
// If we have no paths, we can't scan
2020-10-31 13:20:54 -07:00
if ( paths = = null | | ! paths . Any ( ) )
2020-10-30 23:56:27 -07:00
return null ;
2021-08-25 20:25:45 -07:00
// Set a starting starting time for debug output
DateTime startTime = DateTime . UtcNow ;
2020-10-31 14:00:31 -07:00
// Checkpoint
2022-05-01 16:59:03 -07:00
this . fileProgress ? . Report ( new ProtectionProgress ( null , 0 , null ) ) ;
2020-10-31 14:00:31 -07:00
// Temp variables for reporting
string tempFilePath = Path . GetTempPath ( ) ;
string tempFilePathWithGuid = Path . Combine ( tempFilePath , Guid . NewGuid ( ) . ToString ( ) ) ;
2020-10-30 23:56:27 -07:00
// Loop through each path and get the returned values
2021-07-18 09:44:23 -07:00
var protections = new ConcurrentDictionary < string , ConcurrentQueue < string > > ( ) ;
2020-10-31 13:20:54 -07:00
foreach ( string path in paths )
2020-10-30 23:56:27 -07:00
{
// Directories scan each internal file individually
if ( Directory . Exists ( path ) )
{
// Enumerate all files at first for easier access
var files = Directory . EnumerateFiles ( path , "*" , SearchOption . AllDirectories ) . ToList ( ) ;
// Scan for path-detectable protections
2023-01-05 10:46:13 -08:00
if ( ScanPaths )
{
2023-03-15 12:55:08 -04:00
var directoryPathProtections = Handler . HandlePathChecks ( path , files ) ;
2023-01-05 10:46:13 -08:00
AppendToDictionary ( protections , directoryPathProtections ) ;
}
2020-10-30 23:56:27 -07:00
// Scan each file in directory separately
2021-03-22 21:25:14 -07:00
for ( int i = 0 ; i < files . Count ; i + + )
2020-10-30 23:56:27 -07:00
{
2020-10-31 14:00:31 -07:00
// Get the current file
string file = files . ElementAt ( i ) ;
// Get the reportable file name
string reportableFileName = file ;
if ( reportableFileName . StartsWith ( tempFilePath ) )
reportableFileName = reportableFileName . Substring ( tempFilePathWithGuid . Length ) ;
// Checkpoint
2022-05-01 16:59:03 -07:00
this . fileProgress ? . Report ( new ProtectionProgress ( reportableFileName , i / ( float ) files . Count , "Checking file" + ( file ! = reportableFileName ? " from archive" : string . Empty ) ) ) ;
2020-10-31 14:00:31 -07:00
2020-10-30 23:56:27 -07:00
// Scan for path-detectable protections
2023-01-05 10:46:13 -08:00
if ( ScanPaths )
{
2023-03-15 12:55:08 -04:00
var filePathProtections = Handler . HandlePathChecks ( file , files : null ) ;
AppendToDictionary ( protections , filePathProtections ) ;
2023-01-05 10:46:13 -08:00
}
2020-10-30 23:56:27 -07:00
// Scan for content-detectable protections
2023-01-05 11:00:29 -08:00
var fileProtections = GetInternalProtections ( file ) ;
if ( fileProtections ! = null & & fileProtections . Any ( ) )
2020-10-30 23:56:27 -07:00
{
2023-01-05 11:00:29 -08:00
foreach ( string key in fileProtections . Keys )
2020-10-30 23:56:27 -07:00
{
2023-01-05 11:00:29 -08:00
if ( ! protections . ContainsKey ( key ) )
protections [ key ] = new ConcurrentQueue < string > ( ) ;
2020-10-30 23:56:27 -07:00
2023-01-05 11:00:29 -08:00
protections [ key ] . AddRange ( fileProtections [ key ] ) ;
2020-10-30 23:56:27 -07:00
}
}
2020-10-31 14:00:31 -07:00
// Checkpoint
2021-07-18 09:44:23 -07:00
protections . TryGetValue ( file , out ConcurrentQueue < string > fullProtectionList ) ;
2020-10-31 14:00:31 -07:00
string fullProtection = ( fullProtectionList ! = null & & fullProtectionList . Any ( ) ? string . Join ( ", " , fullProtectionList ) : null ) ;
2022-05-01 16:59:03 -07:00
this . fileProgress ? . Report ( new ProtectionProgress ( reportableFileName , ( i + 1 ) / ( float ) files . Count , fullProtection ? ? string . Empty ) ) ;
2020-10-31 14:00:31 -07:00
}
2020-10-30 23:56:27 -07:00
}
// Scan a single file by itself
else if ( File . Exists ( path ) )
{
2020-10-31 14:00:31 -07:00
// Get the reportable file name
string reportableFileName = path ;
if ( reportableFileName . StartsWith ( tempFilePath ) )
reportableFileName = reportableFileName . Substring ( tempFilePathWithGuid . Length ) ;
// Checkpoint
2022-05-01 16:59:03 -07:00
this . fileProgress ? . Report ( new ProtectionProgress ( reportableFileName , 0 , "Checking file" + ( path ! = reportableFileName ? " from archive" : string . Empty ) ) ) ;
2020-10-31 14:00:31 -07:00
2020-10-30 23:56:27 -07:00
// Scan for path-detectable protections
2023-01-05 10:46:13 -08:00
if ( ScanPaths )
{
2023-03-15 12:55:08 -04:00
var filePathProtections = Handler . HandlePathChecks ( path , files : null ) ;
AppendToDictionary ( protections , filePathProtections ) ;
2023-01-05 10:46:13 -08:00
}
2020-10-30 23:56:27 -07:00
// Scan for content-detectable protections
2023-01-05 11:00:29 -08:00
var fileProtections = GetInternalProtections ( path ) ;
if ( fileProtections ! = null & & fileProtections . Any ( ) )
2020-10-30 23:56:27 -07:00
{
2023-01-05 11:00:29 -08:00
foreach ( string key in fileProtections . Keys )
2020-10-30 23:56:27 -07:00
{
2023-01-05 11:00:29 -08:00
if ( ! protections . ContainsKey ( key ) )
protections [ key ] = new ConcurrentQueue < string > ( ) ;
2020-10-30 23:56:27 -07:00
2023-01-05 11:00:29 -08:00
protections [ key ] . AddRange ( fileProtections [ key ] ) ;
2020-10-30 23:56:27 -07:00
}
}
2020-10-31 14:00:31 -07:00
// Checkpoint
2021-07-18 09:44:23 -07:00
protections . TryGetValue ( path , out ConcurrentQueue < string > fullProtectionList ) ;
2020-10-31 14:00:31 -07:00
string fullProtection = ( fullProtectionList ! = null & & fullProtectionList . Any ( ) ? string . Join ( ", " , fullProtectionList ) : null ) ;
2022-05-01 16:59:03 -07:00
this . fileProgress ? . Report ( new ProtectionProgress ( reportableFileName , 1 , fullProtection ? ? string . Empty ) ) ;
2020-10-30 23:56:27 -07:00
}
// Throw on an invalid path
else
{
2020-10-31 14:14:35 -07:00
Console . WriteLine ( $"{path} is not a directory or file, skipping..." ) ;
//throw new FileNotFoundException($"{path} is not a directory or file, skipping...");
2020-10-30 23:56:27 -07:00
}
}
2020-10-31 21:20:16 -07:00
// Clear out any empty keys
2022-12-15 00:13:24 -08:00
ClearEmptyKeys ( protections ) ;
2020-10-31 21:20:16 -07:00
2021-08-25 20:25:45 -07:00
// If we're in debug, output the elasped time to console
if ( IncludeDebug )
Console . WriteLine ( $"Time elapsed: {DateTime.UtcNow.Subtract(startTime)}" ) ;
2020-10-30 23:56:27 -07:00
return protections ;
}
2021-03-19 15:56:07 -07:00
2020-10-30 23:56:27 -07:00
/// <summary>
/// Get the content-detectable protections associated with a single path
/// </summary>
/// <param name="file">Path to the file to scan</param>
/// <returns>Dictionary of list of strings representing the found protections</returns>
2021-07-18 09:44:23 -07:00
private ConcurrentDictionary < string , ConcurrentQueue < string > > GetInternalProtections ( string file )
2020-10-30 23:56:27 -07:00
{
// Quick sanity check before continuing
if ( ! File . Exists ( file ) )
return null ;
2022-05-01 17:06:46 -07:00
// Open the file and begin scanning
try
{
2022-12-22 22:03:32 -08:00
using ( FileStream fs = File . Open ( file , FileMode . Open , FileAccess . Read , FileShare . Read ) )
2022-05-01 17:06:46 -07:00
{
return GetInternalProtections ( file , fs ) ;
}
}
catch ( Exception ex )
{
2022-05-15 20:58:27 -07:00
if ( IncludeDebug ) Console . WriteLine ( ex ) ;
2022-05-01 17:06:46 -07:00
var protections = new ConcurrentDictionary < string , ConcurrentQueue < string > > ( ) ;
2022-12-15 00:13:24 -08:00
AppendToDictionary ( protections , file , IncludeDebug ? ex . ToString ( ) : "[Exception opening file, please try again]" ) ;
ClearEmptyKeys ( protections ) ;
2022-05-01 17:06:46 -07:00
return protections ;
}
}
/// <summary>
/// Get the content-detectable protections associated with a single path
/// </summary>
/// <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 ConcurrentDictionary < string , ConcurrentQueue < string > > GetInternalProtections ( string fileName , Stream stream )
{
// Quick sanity check before continuing
if ( stream = = null | | ! stream . CanRead | | ! stream . CanSeek )
return null ;
2022-03-15 21:30:46 -07:00
// Initialize the protections found
2021-07-18 09:44:23 -07:00
var protections = new ConcurrentDictionary < string , ConcurrentQueue < string > > ( ) ;
2020-10-30 23:56:27 -07:00
// Get the extension for certain checks
2022-05-01 17:06:46 -07:00
string extension = Path . GetExtension ( fileName ) . ToLower ( ) . TrimStart ( '.' ) ;
2020-10-30 23:56:27 -07:00
// Open the file and begin scanning
2021-09-01 22:22:14 -07:00
try
2020-10-30 23:56:27 -07:00
{
2022-05-01 17:06:46 -07:00
// Get the first 16 bytes for matching
byte [ ] magic = new byte [ 16 ] ;
try
{
stream . Read ( magic , 0 , 16 ) ;
stream . Seek ( 0 , SeekOrigin . Begin ) ;
}
2022-05-15 20:58:27 -07:00
catch ( Exception ex )
2020-10-30 23:56:27 -07:00
{
2022-05-15 20:58:27 -07:00
if ( IncludeDebug ) Console . WriteLine ( ex ) ;
2022-05-01 17:06:46 -07:00
return null ;
}
2022-12-08 21:32:52 -08:00
// Get the file type either from magic number or extension
2023-03-13 22:03:10 -04:00
SupportedFileType fileType = FileTypes . GetFileType ( magic ) ;
2022-12-08 21:46:22 -08:00
if ( fileType = = SupportedFileType . UNKNOWN )
2023-03-13 22:03:10 -04:00
fileType = FileTypes . GetFileType ( extension ) ;
2022-12-08 21:32:52 -08:00
// If we still got unknown, just return null
2022-12-08 21:46:22 -08:00
if ( fileType = = SupportedFileType . UNKNOWN )
2022-12-08 21:32:52 -08:00
return null ;
2023-03-09 15:07:35 -05:00
#region Non - Archive File Types
2023-03-13 16:06:45 -04:00
// Create a detectable for the given file type
2023-03-15 11:06:29 -04:00
var detectable = Factory . CreateDetectable ( fileType ) ;
2023-03-13 16:06:45 -04:00
// If we're scanning file contents
if ( detectable ! = null & & ScanContents )
{
2023-03-15 11:06:29 -04:00
// If we have an executable, it needs to bypass normal handling
2023-03-15 15:51:38 -04:00
if ( detectable is Executable executable )
2023-03-15 11:06:29 -04:00
{
2023-03-21 10:42:14 -04:00
executable . IncludeGameEngines = ScanGameEngines ;
2023-03-15 15:51:38 -04:00
executable . IncludePackers = ScanPackers ;
var subProtections = ProcessExecutable ( executable , fileName , stream ) ;
if ( subProtections ! = null )
AppendToDictionary ( protections , subProtections ) ;
2023-03-15 11:06:29 -04:00
}
// Otherwise, use the default implementation
else
{
var subProtections = Handler . HandleDetectable ( detectable , fileName , stream , IncludeDebug ) ;
if ( subProtections ! = null )
AppendToDictionary ( protections , fileName , subProtections ) ;
}
2023-03-13 16:06:45 -04:00
string subProtection = detectable . Detect ( stream , fileName , IncludeDebug ) ;
if ( ! string . IsNullOrWhiteSpace ( subProtection ) )
2023-03-13 16:17:21 -04:00
{
// If we have an indicator of multiple protections
if ( subProtection . Contains ( ';' ) )
{
var splitProtections = subProtection . Split ( ';' ) ;
AppendToDictionary ( protections , fileName , splitProtections ) ;
}
else
{
AppendToDictionary ( protections , fileName , subProtection ) ;
}
}
2023-03-13 16:06:45 -04:00
}
2022-05-01 17:06:46 -07:00
#endregion
#region Archive File Types
2023-03-09 15:07:35 -05:00
// Create an extractable for the given file type
2023-03-15 11:06:29 -04:00
var extractable = Factory . CreateExtractable ( fileType ) ;
2023-03-09 15:07:35 -05:00
2023-01-05 11:00:29 -08:00
// If we're scanning archives
2023-03-09 15:07:35 -05:00
if ( extractable ! = null & & ScanArchives )
2022-05-01 17:06:46 -07:00
{
2023-03-15 15:53:24 -04:00
var subProtections = Handler . HandleExtractable ( extractable , fileName , stream , this ) ;
if ( subProtections ! = null )
AppendToDictionary ( protections , subProtections ) ;
2021-09-01 22:22:14 -07:00
}
2022-05-01 17:06:46 -07:00
#endregion
2021-09-01 22:22:14 -07:00
}
2021-09-10 13:59:35 -07:00
catch ( Exception ex )
2021-09-01 22:22:14 -07:00
{
2022-05-15 20:58:27 -07:00
if ( IncludeDebug ) Console . WriteLine ( ex ) ;
2022-12-15 00:13:24 -08:00
AppendToDictionary ( protections , fileName , IncludeDebug ? ex . ToString ( ) : "[Exception opening file, please try again]" ) ;
2020-10-30 23:56:27 -07:00
}
2020-10-31 21:20:16 -07:00
// Clear out any empty keys
2022-12-15 00:13:24 -08:00
ClearEmptyKeys ( protections ) ;
2020-10-31 21:20:16 -07:00
2020-10-30 23:56:27 -07:00
return protections ;
}
2022-05-01 14:16:53 -07:00
#endregion
2023-03-10 11:41:08 -05:00
2023-03-15 11:37:20 -04:00
#region Executable Handling
2023-03-10 11:41:08 -05:00
2023-03-13 22:36:28 -04:00
/// <summary>
2023-03-15 11:37:20 -04:00
/// Process scanning for an Executable type
2023-03-13 22:36:28 -04:00
/// </summary>
2023-03-15 15:51:38 -04:00
/// <param name="executable">Executable instance for processing</param>
2023-03-13 22:36:28 -04:00
/// <param name="fileName">Name of the source file of the stream, for tracking</param>
/// <param name="stream">Stream to scan the contents of</param>
2023-03-15 11:37:20 -04:00
/// <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>
2023-03-15 15:51:38 -04:00
private ConcurrentDictionary < string , ConcurrentQueue < string > > ProcessExecutable ( Executable executable , string fileName , Stream stream )
2023-03-13 22:36:28 -04:00
{
2023-03-15 11:37:20 -04:00
// Try to create a wrapper for the proper executable type
var wrapper = WrapperFactory . CreateExecutableWrapper ( stream ) ;
if ( wrapper = = null )
2023-03-15 15:51:38 -04:00
return null ;
// Create the output dictionary
var protections = new ConcurrentDictionary < string , ConcurrentQueue < string > > ( ) ;
2023-03-13 22:36:28 -04:00
2023-03-15 11:37:20 -04:00
// Only use generic content checks if we're in debug mode
if ( IncludeDebug )
{
2023-03-15 15:51:38 -04:00
var subProtections = executable . RunContentChecks ( fileName , stream , IncludeDebug ) ;
2023-03-15 11:37:20 -04:00
if ( subProtections ! = null )
2023-03-15 15:51:38 -04:00
AppendToDictionary ( protections , fileName , subProtections . Values . ToArray ( ) ) ;
2023-03-15 11:37:20 -04:00
}
2023-03-13 22:36:28 -04:00
2023-03-15 16:12:12 -04:00
if ( wrapper is MSDOS mz )
2023-03-15 11:37:20 -04:00
{
2023-03-15 16:12:12 -04:00
var subProtections = executable . RunMSDOSExecutableChecks ( fileName , stream , mz , IncludeDebug ) ;
if ( subProtections = = null )
return protections ;
// Append the returned values
AppendToDictionary ( protections , fileName , subProtections . Values . ToArray ( ) ) ;
// If we have any extractable packers
var extractedProtections = HandleExtractableProtections ( subProtections . Keys , fileName , stream ) ;
if ( extractedProtections ! = null )
AppendToDictionary ( protections , extractedProtections ) ;
2023-03-13 22:36:28 -04:00
}
2023-03-15 11:37:20 -04:00
else if ( wrapper is LinearExecutable lex )
2023-03-13 22:36:28 -04:00
{
2023-03-15 15:51:38 -04:00
var subProtections = executable . RunLinearExecutableChecks ( fileName , stream , lex , IncludeDebug ) ;
if ( subProtections = = null )
return protections ;
// Append the returned values
AppendToDictionary ( protections , fileName , subProtections . Values . ToArray ( ) ) ;
// If we have any extractable packers
2023-03-15 15:58:19 -04:00
var extractedProtections = HandleExtractableProtections ( subProtections . Keys , fileName , stream ) ;
if ( extractedProtections ! = null )
AppendToDictionary ( protections , extractedProtections ) ;
2023-03-15 11:37:20 -04:00
}
else if ( wrapper is NewExecutable nex )
{
2023-03-15 15:51:38 -04:00
var subProtections = executable . RunNewExecutableChecks ( fileName , stream , nex , IncludeDebug ) ;
if ( subProtections = = null )
return protections ;
// Append the returned values
AppendToDictionary ( protections , fileName , subProtections . Values . ToArray ( ) ) ;
// If we have any extractable packers
2023-03-15 15:58:19 -04:00
var extractedProtections = HandleExtractableProtections ( subProtections . Keys , fileName , stream ) ;
if ( extractedProtections ! = null )
AppendToDictionary ( protections , extractedProtections ) ;
2023-03-15 11:37:20 -04:00
}
else if ( wrapper is PortableExecutable pex )
{
2023-03-15 15:51:38 -04:00
var subProtections = executable . RunPortableExecutableChecks ( fileName , stream , pex , IncludeDebug ) ;
if ( subProtections = = null )
return protections ;
// Append the returned values
AppendToDictionary ( protections , fileName , subProtections . Values . ToArray ( ) ) ;
// If we have any extractable packers
2023-03-15 15:58:19 -04:00
var extractedProtections = HandleExtractableProtections ( subProtections . Keys , fileName , stream ) ;
if ( extractedProtections ! = null )
AppendToDictionary ( protections , extractedProtections ) ;
2023-03-13 22:36:28 -04:00
}
2023-03-15 15:51:38 -04:00
return protections ;
2023-03-13 22:36:28 -04:00
}
2023-03-15 15:58:19 -04:00
/// <summary>
/// Handle extractable protections, such as executable packers
/// </summary>
/// <param name="classes">Set of classes returned from Exectuable scans</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>
/// <returns>Set of protections found from extraction, null on error</returns>
private ConcurrentDictionary < string , ConcurrentQueue < string > > HandleExtractableProtections ( IEnumerable < object > classes , string fileName , Stream stream )
{
// If we have an invalid set of classes
if ( classes ? . Any ( ) ! = true )
return null ;
// Create the output dictionary
var protections = new ConcurrentDictionary < string , ConcurrentQueue < string > > ( ) ;
// If we have any extractable packers
var extractables = classes . Where ( c = > c is IExtractable ) . Select ( c = > c as IExtractable ) ;
Parallel . ForEach ( extractables , extractable = >
{
// Get the protection for the class, if possible
var extractedProtections = Handler . HandleExtractable ( extractable , fileName , stream , this ) ;
if ( extractedProtections ! = null )
AppendToDictionary ( protections , extractedProtections ) ;
} ) ;
return protections ;
}
2023-03-10 11:41:08 -05:00
#endregion
2020-10-30 23:56:27 -07:00
}
}