2021-03-02 15:10:52 -08:00
|
|
|
|
using System;
|
2021-07-18 09:44:23 -07:00
|
|
|
|
using System.Collections.Concurrent;
|
2020-09-10 21:10:32 -07:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Text;
|
2021-07-18 09:44:23 -07:00
|
|
|
|
using System.Threading.Tasks;
|
2022-05-01 17:41:50 -07:00
|
|
|
|
using BurnOutSharp.Interfaces;
|
2023-03-09 16:02:51 -05:00
|
|
|
|
using BinaryObjectScanner.Interfaces;
|
2023-03-07 16:59:14 -05:00
|
|
|
|
using BinaryObjectScanner.Wrappers;
|
2023-03-07 12:04:48 -05:00
|
|
|
|
using static BinaryObjectScanner.Utilities.Dictionary;
|
2020-09-10 21:10:32 -07:00
|
|
|
|
|
|
|
|
|
|
namespace BurnOutSharp.FileType
|
|
|
|
|
|
{
|
2022-12-08 21:32:52 -08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Executable or library
|
|
|
|
|
|
/// </summary>
|
2023-03-09 16:12:21 -05:00
|
|
|
|
/// TODO: We need to figure out a way to avoid passing in the Scanner object to all IScannable implementations.
|
|
|
|
|
|
/// In order to achiveve this, Executable specifically needs a way of supporting the Packer types (IExtractable) in such
|
|
|
|
|
|
/// a way that we don't scan the files two times over. Somehow, we need to make Executable IExtractable as well and then
|
|
|
|
|
|
/// take the outputs of `Scan` and figure out if we need to try extracting or not.
|
2023-03-09 17:16:39 -05:00
|
|
|
|
///
|
|
|
|
|
|
/// Since Options is a separate class now, that should be passed in instead of Scanner, so that we only have to worry about
|
|
|
|
|
|
/// what the user or implementer was requesting.
|
2021-09-10 16:10:15 -07:00
|
|
|
|
public class Executable : IScannable
|
2020-09-10 21:10:32 -07:00
|
|
|
|
{
|
2021-02-26 09:26:23 -08:00
|
|
|
|
/// <inheritdoc/>
|
2021-07-18 09:44:23 -07:00
|
|
|
|
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
2021-02-26 09:26:23 -08:00
|
|
|
|
{
|
|
|
|
|
|
if (!File.Exists(file))
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
2022-12-22 22:03:32 -08:00
|
|
|
|
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
2021-02-26 09:26:23 -08:00
|
|
|
|
{
|
|
|
|
|
|
return Scan(scanner, fs, file);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
2021-07-18 09:44:23 -07:00
|
|
|
|
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
2020-09-10 21:10:32 -07:00
|
|
|
|
{
|
2021-07-15 09:57:06 -07:00
|
|
|
|
// Files can be protected in multiple ways
|
2021-07-18 09:44:23 -07:00
|
|
|
|
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
2021-07-15 09:57:06 -07:00
|
|
|
|
|
2022-03-15 12:39:22 -07:00
|
|
|
|
// Load the current file content for debug only
|
2020-09-10 21:10:32 -07:00
|
|
|
|
byte[] fileContent = null;
|
2022-03-15 12:39:22 -07:00
|
|
|
|
if (scanner.IncludeDebug)
|
2021-07-15 09:57:06 -07:00
|
|
|
|
{
|
2022-03-15 12:39:22 -07:00
|
|
|
|
try
|
2021-07-15 09:57:06 -07:00
|
|
|
|
{
|
2022-03-15 12:39:22 -07:00
|
|
|
|
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
|
|
|
|
|
{
|
|
|
|
|
|
fileContent = br.ReadBytes((int)stream.Length);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-05-15 20:58:27 -07:00
|
|
|
|
catch (Exception ex)
|
2022-03-15 12:39:22 -07:00
|
|
|
|
{
|
2022-05-15 20:58:27 -07:00
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
|
2023-01-17 20:21:25 -08:00
|
|
|
|
// Enable for odd files, keep disabled otherwise
|
|
|
|
|
|
// AppendToDictionary(protections, file, "[Out of memory attempting to open]");
|
|
|
|
|
|
// return protections;
|
2021-07-15 09:57:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-09-10 16:15:20 -07:00
|
|
|
|
|
2023-01-18 08:36:16 -08:00
|
|
|
|
// Get the wrapper for the appropriate executable type
|
2023-03-10 11:20:35 -05:00
|
|
|
|
WrapperBase wrapper = Tools.FileTypeTools.DetermineExecutableType(stream);
|
2023-01-18 08:36:16 -08:00
|
|
|
|
if (wrapper == null)
|
|
|
|
|
|
return protections;
|
2021-09-10 21:45:34 -07:00
|
|
|
|
|
2022-03-14 10:49:02 -07:00
|
|
|
|
// Iterate through all generic content checks
|
2022-03-15 15:47:37 -07:00
|
|
|
|
if (fileContent != null)
|
2021-02-26 09:26:23 -08:00
|
|
|
|
{
|
2022-05-01 14:46:01 -07:00
|
|
|
|
Parallel.ForEach(ScanningClasses.ContentCheckClasses, contentCheckClass =>
|
2021-02-26 09:26:23 -08:00
|
|
|
|
{
|
2022-05-01 17:17:15 -07:00
|
|
|
|
string protection = contentCheckClass.CheckContents(file, fileContent, scanner.IncludeDebug);
|
2022-05-01 14:27:04 -07:00
|
|
|
|
if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection))
|
2022-12-15 00:13:24 -08:00
|
|
|
|
AppendToDictionary(protections, file, protection);
|
2022-03-15 12:39:22 -07:00
|
|
|
|
|
2023-03-09 15:50:14 -05:00
|
|
|
|
// If we have an IExtractable implementation
|
|
|
|
|
|
if (contentCheckClass is IExtractable extractable)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (file == null || string.IsNullOrEmpty(protection))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// If the extractable file itself fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Extract and get the output path
|
2023-03-09 17:16:39 -05:00
|
|
|
|
string tempPath = extractable.Extract(stream, file, scanner.IncludeDebug);
|
2023-03-09 15:50:14 -05:00
|
|
|
|
if (tempPath != null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// Collect and format all found protections
|
|
|
|
|
|
var subProtections = scanner.GetProtections(tempPath);
|
|
|
|
|
|
|
|
|
|
|
|
// If temp directory cleanup fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.Delete(tempPath, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the returned protections
|
|
|
|
|
|
StripFromKeys(protections, tempPath);
|
|
|
|
|
|
PrependToKeys(subProtections, file);
|
|
|
|
|
|
AppendToDictionary(protections, subProtections);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-15 15:47:37 -07:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2021-02-26 09:26:23 -08:00
|
|
|
|
|
2023-01-18 08:36:16 -08:00
|
|
|
|
// If we have an MS-DOS executable
|
|
|
|
|
|
if (wrapper is MSDOS mz)
|
|
|
|
|
|
{
|
|
|
|
|
|
// No-op
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we have a New Executable
|
2023-01-18 08:39:27 -08:00
|
|
|
|
else if (wrapper is NewExecutable nex)
|
2022-03-14 10:49:02 -07:00
|
|
|
|
{
|
2022-05-01 17:17:15 -07:00
|
|
|
|
Parallel.ForEach(ScanningClasses.NewExecutableCheckClasses, contentCheckClass =>
|
2022-03-14 10:49:02 -07:00
|
|
|
|
{
|
|
|
|
|
|
// Check using custom content checks first
|
2022-05-01 17:17:15 -07:00
|
|
|
|
string protection = contentCheckClass.CheckNewExecutable(file, nex, scanner.IncludeDebug);
|
2022-05-01 14:27:04 -07:00
|
|
|
|
if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection))
|
2022-12-15 00:13:24 -08:00
|
|
|
|
AppendToDictionary(protections, file, protection);
|
2022-03-14 10:49:02 -07:00
|
|
|
|
|
2023-03-09 15:50:14 -05:00
|
|
|
|
// If we have an IExtractable implementation
|
|
|
|
|
|
if (contentCheckClass is IExtractable extractable)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (file == null || string.IsNullOrEmpty(protection))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// If the extractable file itself fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Extract and get the output path
|
2023-03-09 17:16:39 -05:00
|
|
|
|
string tempPath = extractable.Extract(stream, file, scanner.IncludeDebug);
|
2023-03-09 15:50:14 -05:00
|
|
|
|
if (tempPath != null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// Collect and format all found protections
|
|
|
|
|
|
var subProtections = scanner.GetProtections(tempPath);
|
|
|
|
|
|
|
|
|
|
|
|
// If temp directory cleanup fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.Delete(tempPath, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the returned protections
|
|
|
|
|
|
StripFromKeys(protections, tempPath);
|
|
|
|
|
|
PrependToKeys(subProtections, file);
|
|
|
|
|
|
AppendToDictionary(protections, subProtections);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-14 10:49:02 -07:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-18 08:36:16 -08:00
|
|
|
|
// If we have a Linear Executable
|
|
|
|
|
|
else if (wrapper is LinearExecutable lex)
|
|
|
|
|
|
{
|
2023-03-09 23:19:27 -05:00
|
|
|
|
Parallel.ForEach(ScanningClasses.LinearExecutableCheckClasses, contentCheckClass =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check using custom content checks first
|
|
|
|
|
|
string protection = contentCheckClass.CheckLinearExecutable(file, lex, scanner.IncludeDebug);
|
|
|
|
|
|
if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection))
|
|
|
|
|
|
AppendToDictionary(protections, file, protection);
|
|
|
|
|
|
|
|
|
|
|
|
// If we have an IExtractable implementation
|
|
|
|
|
|
if (contentCheckClass is IExtractable extractable)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (file == null || string.IsNullOrEmpty(protection))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// If the extractable file itself fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Extract and get the output path
|
|
|
|
|
|
string tempPath = extractable.Extract(stream, file, scanner.IncludeDebug);
|
|
|
|
|
|
if (tempPath != null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// Collect and format all found protections
|
|
|
|
|
|
var subProtections = scanner.GetProtections(tempPath);
|
|
|
|
|
|
|
|
|
|
|
|
// If temp directory cleanup fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.Delete(tempPath, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the returned protections
|
|
|
|
|
|
StripFromKeys(protections, tempPath);
|
|
|
|
|
|
PrependToKeys(subProtections, file);
|
|
|
|
|
|
AppendToDictionary(protections, subProtections);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-01-18 08:36:16 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If we have a Portable Executable
|
|
|
|
|
|
else if (wrapper is PortableExecutable pex)
|
2022-03-14 10:49:02 -07:00
|
|
|
|
{
|
2022-05-01 17:17:15 -07:00
|
|
|
|
Parallel.ForEach(ScanningClasses.PortableExecutableCheckClasses, contentCheckClass =>
|
2022-03-14 10:49:02 -07:00
|
|
|
|
{
|
|
|
|
|
|
// Check using custom content checks first
|
2022-05-01 17:17:15 -07:00
|
|
|
|
string protection = contentCheckClass.CheckPortableExecutable(file, pex, scanner.IncludeDebug);
|
2022-05-01 14:27:04 -07:00
|
|
|
|
if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection))
|
2022-12-15 00:13:24 -08:00
|
|
|
|
AppendToDictionary(protections, file, protection);
|
2022-03-14 10:49:02 -07:00
|
|
|
|
|
2023-03-09 15:50:14 -05:00
|
|
|
|
// If we have an IExtractable implementation
|
|
|
|
|
|
if (contentCheckClass is IExtractable extractable)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (file == null || string.IsNullOrEmpty(protection))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// If the extractable file itself fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Extract and get the output path
|
2023-03-09 17:16:39 -05:00
|
|
|
|
string tempPath = extractable.Extract(stream, file, scanner.IncludeDebug);
|
2023-03-09 15:50:14 -05:00
|
|
|
|
if (tempPath != null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// Collect and format all found protections
|
|
|
|
|
|
var subProtections = scanner.GetProtections(tempPath);
|
|
|
|
|
|
|
|
|
|
|
|
// If temp directory cleanup fails
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
Directory.Delete(tempPath, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Prepare the returned protections
|
|
|
|
|
|
StripFromKeys(protections, tempPath);
|
|
|
|
|
|
PrependToKeys(subProtections, file);
|
|
|
|
|
|
AppendToDictionary(protections, subProtections);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-03-14 10:49:02 -07:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-18 08:39:27 -08:00
|
|
|
|
// No other executable formats currently identified or supported
|
|
|
|
|
|
|
2020-09-10 21:10:32 -07:00
|
|
|
|
return protections;
|
|
|
|
|
|
}
|
2021-03-23 10:04:09 -07:00
|
|
|
|
|
2022-03-14 10:49:02 -07:00
|
|
|
|
#region Helpers
|
|
|
|
|
|
|
2021-08-25 20:26:43 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Check to see if a protection should be added or not
|
|
|
|
|
|
/// </summary>
|
2022-05-01 14:24:46 -07:00
|
|
|
|
/// <param name="checkClass">Class that was last used to check</param>
|
2022-05-01 14:27:04 -07:00
|
|
|
|
/// <param name="scanPackers">Determines if packers should be included in the output</param>
|
2021-08-25 20:26:43 -07:00
|
|
|
|
/// <param name="protection">The protection result to be checked</param>
|
2022-05-01 14:27:04 -07:00
|
|
|
|
private bool ShouldAddProtection(object checkClass, bool scanPackers, string protection)
|
2021-08-25 20:26:43 -07:00
|
|
|
|
{
|
2022-05-01 14:28:28 -07:00
|
|
|
|
// If we have an invalid protection
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(protection))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2021-08-25 20:26:43 -07:00
|
|
|
|
// If we have a valid content check based on settings
|
2022-05-01 14:27:04 -07:00
|
|
|
|
if (scanPackers || !checkClass.GetType().Namespace.ToLowerInvariant().Contains("packertype"))
|
2022-05-01 14:28:28 -07:00
|
|
|
|
return true;
|
2022-03-14 10:49:02 -07:00
|
|
|
|
|
2022-05-01 14:28:28 -07:00
|
|
|
|
// Everything else fails
|
2022-03-14 10:49:02 -07:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
2020-09-10 21:10:32 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|