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 } }