using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using BinaryObjectScanner.Interfaces;
using BinaryObjectScanner.Utilities;
using BinaryObjectScanner.Wrappers;
namespace BinaryObjectScanner.FileType
{
///
/// Executable or library
///
///
/// Due to the complexity of executables, all extraction handling
/// another class that is used by the scanner
///
public class Executable : IDetectable
{
#region Properties
///
/// Determines if game engines are counted as detected protections or not
///
public bool IncludeGameEngines { get; set; }
///
/// Determines if packers are counted as detected protections or not
///
public bool IncludePackers { get; set; }
///
/// Cache for all IContentCheck types
///
public static IEnumerable ContentCheckClasses
{
get
{
if (contentCheckClasses == null)
contentCheckClasses = InitCheckClasses();
return contentCheckClasses;
}
}
///
/// Cache for all ILinearExecutableCheck types
///
public static IEnumerable LinearExecutableCheckClasses
{
get
{
if (linearExecutableCheckClasses == null)
linearExecutableCheckClasses = InitCheckClasses();
return linearExecutableCheckClasses;
}
}
///
/// Cache for all IMSDOSExecutableCheck types
///
public static IEnumerable MSDOSExecutableCheckClasses
{
get
{
if (msdosExecutableCheckClasses == null)
msdosExecutableCheckClasses = InitCheckClasses();
return msdosExecutableCheckClasses;
}
}
///
/// Cache for all INewExecutableCheck types
///
public static IEnumerable NewExecutableCheckClasses
{
get
{
if (newExecutableCheckClasses == null)
newExecutableCheckClasses = InitCheckClasses();
return newExecutableCheckClasses;
}
}
///
/// Cache for all IPortableExecutableCheck types
///
public static IEnumerable PortableExecutableCheckClasses
{
get
{
if (portableExecutableCheckClasses == null)
portableExecutableCheckClasses = InitCheckClasses();
return portableExecutableCheckClasses;
}
}
#endregion
#region Internal Instances
///
/// Cache for all IContentCheck types
///
private static IEnumerable contentCheckClasses;
///
/// Cache for all ILinearExecutableCheck types
///
private static IEnumerable linearExecutableCheckClasses;
///
/// Cache for all IMSDOSExecutableCheck types
///
private static IEnumerable msdosExecutableCheckClasses;
///
/// Cache for all INewExecutableCheck types
///
private static IEnumerable newExecutableCheckClasses;
///
/// Cache for all IPortableExecutableCheck types
///
private static IEnumerable portableExecutableCheckClasses;
#endregion
///
public string Detect(string file, bool includeDebug)
{
if (!File.Exists(file))
return null;
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return Detect(fs, file, includeDebug);
}
}
///
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)
return null;
// Create the internal queue
var protections = new ConcurrentQueue();
// Only use generic content checks if we're in debug mode
if (includeDebug)
{
var subProtections = RunContentChecks(file, stream, includeDebug);
if (subProtections != null)
protections.AddRange(subProtections.Values.ToArray());
}
if (wrapper is MSDOS mz)
{
var subProtections = RunMSDOSExecutableChecks(file, stream, mz, includeDebug);
if (subProtections != null)
protections.AddRange(subProtections.Values.ToArray());
}
else if (wrapper is LinearExecutable lex)
{
var subProtections = RunLinearExecutableChecks(file, stream, lex, includeDebug);
if (subProtections != null)
protections.AddRange(subProtections.Values.ToArray());
}
else if (wrapper is NewExecutable nex)
{
var subProtections = RunNewExecutableChecks(file, stream, nex, includeDebug);
if (subProtections != null)
protections.AddRange(subProtections.Values.ToArray());
}
else if (wrapper is PortableExecutable pex)
{
var subProtections = RunPortableExecutableChecks(file, stream, pex, includeDebug);
if (subProtections != null)
protections.AddRange(subProtections.Values.ToArray());
}
return string.Join(";", protections);
}
#region Check Runners
///
/// 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 debug data, false otherwise
/// Set of protections in file, null on error
public ConcurrentDictionary RunContentChecks(string file, Stream stream, bool includeDebug)
{
// If we have an invalid file
if (string.IsNullOrWhiteSpace(file))
return null;
else if (!File.Exists(file))
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 dictionary
var protections = new ConcurrentDictionary();
// Iterate through all checks
Parallel.ForEach(ContentCheckClasses, checkClass =>
{
// Get the protection for the class, if possible
string protection = checkClass.CheckContents(file, fileContent, includeDebug);
if (string.IsNullOrWhiteSpace(protection))
return;
// If we are filtering on game engines
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
return;
// If we are filtering on packers
if (CheckIfPacker(checkClass) && !IncludePackers)
return;
protections.TryAdd(checkClass, protection);
});
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 debug data, false otherwise
/// Set of protections in file, null on error
public ConcurrentDictionary RunLinearExecutableChecks(string file, Stream stream, LinearExecutable lex, bool includeDebug)
{
// Create the output dictionary
var protections = new ConcurrentDictionary();
// Iterate through all checks
Parallel.ForEach(LinearExecutableCheckClasses, checkClass =>
{
// Get the protection for the class, if possible
string protection = checkClass.CheckLinearExecutable(file, lex, includeDebug);
if (string.IsNullOrWhiteSpace(protection))
return;
// If we are filtering on game engines
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
return;
// If we are filtering on packers
if (CheckIfPacker(checkClass) && !IncludePackers)
return;
protections.TryAdd(checkClass, protection);
});
return protections;
}
///
/// Handle a single file based on all MS-DOS executable check implementations
///
/// Name of the source file of the executable, for tracking
/// Executable to scan
/// True to include debug data, false otherwise
/// Set of protections in file, null on error
public ConcurrentDictionary RunMSDOSExecutableChecks(string file, Stream stream, MSDOS mz, bool includeDebug)
{
// Create the output dictionary
var protections = new ConcurrentDictionary();
// Iterate through all checks
Parallel.ForEach(MSDOSExecutableCheckClasses, checkClass =>
{
// Get the protection for the class, if possible
string protection = checkClass.CheckMSDOSExecutable(file, mz, includeDebug);
if (string.IsNullOrWhiteSpace(protection))
return;
// If we are filtering on game engines
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
return;
// If we are filtering on packers
if (CheckIfPacker(checkClass) && !IncludePackers)
return;
protections.TryAdd(checkClass, protection);
});
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 debug data, false otherwise
/// Set of protections in file, null on error
public ConcurrentDictionary RunNewExecutableChecks(string file, Stream stream, NewExecutable nex, bool includeDebug)
{
// Create the output dictionary
var protections = new ConcurrentDictionary();
// Iterate through all checks
Parallel.ForEach(NewExecutableCheckClasses, checkClass =>
{
// Get the protection for the class, if possible
string protection = checkClass.CheckNewExecutable(file, nex, includeDebug);
if (string.IsNullOrWhiteSpace(protection))
return;
// If we are filtering on game engines
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
return;
// If we are filtering on packers
if (CheckIfPacker(checkClass) && !IncludePackers)
return;
protections.TryAdd(checkClass, protection);
});
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 debug data, false otherwise
/// Set of protections in file, null on error
public ConcurrentDictionary RunPortableExecutableChecks(string file, Stream stream, PortableExecutable pex, bool includeDebug)
{
// Create the output dictionary
var protections = new ConcurrentDictionary();
// Iterate through all checks
Parallel.ForEach(PortableExecutableCheckClasses, checkClass =>
{
// Get the protection for the class, if possible
string protection = checkClass.CheckPortableExecutable(file, pex, includeDebug);
if (string.IsNullOrWhiteSpace(protection))
return;
// If we are filtering on game engines
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
return;
// If we are filtering on packers
if (CheckIfPacker(checkClass) && !IncludePackers)
return;
protections.TryAdd(checkClass, protection);
});
return protections;
}
#endregion
#region Initializers
///
/// Initialize all implementations of a type
///
private static IEnumerable InitCheckClasses()
=> InitCheckClasses(typeof(GameEngine._DUMMY).Assembly)
.Concat(InitCheckClasses(typeof(Packer._DUMMY).Assembly))
.Concat(InitCheckClasses(typeof(Protection._DUMMY).Assembly));
///
/// Initialize all implementations of a type
///
private static IEnumerable InitCheckClasses(Assembly assembly)
{
return assembly.GetTypes()
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)
.Select(t => (T)Activator.CreateInstance(t));
}
#endregion
#region Helpers
///
/// Check to see if an implementation is a game engine using reflection
///
/// Implementation that was last used to check
private static bool CheckIfGameEngine(object impl)
{
return impl.GetType().Namespace.ToLowerInvariant().Contains("gameengine");
}
///
/// 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");
}
#endregion
}
}