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 BurnOutSharp.ExecutableType.Microsoft.NE; using BurnOutSharp.ExecutableType.Microsoft.PE; using BurnOutSharp.Tools; namespace BurnOutSharp.FileType { public class Executable : IScannable { /// /// Cache for all IContentCheck types /// private static readonly IEnumerable contentCheckClasses = InitContentCheckClasses(); /// /// Cache for all INEContentCheck types /// private static readonly IEnumerable neContentCheckClasses = InitNEContentCheckClasses(); /// /// Cache for all IPEContentCheck types /// private static readonly IEnumerable peContentCheckClasses = InitPEContentCheckClasses(); /// public bool ShouldScan(byte[] magic) { // DOS MZ executable file format (and descendants) if (magic.StartsWith(new byte?[] { 0x4d, 0x5a })) return true; // Executable and Linkable Format if (magic.StartsWith(new byte?[] { 0x7f, 0x45, 0x4c, 0x46 })) return true; // Mach-O binary (32-bit) if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xce })) return true; // Mach-O binary (32-bit, reverse byte ordering scheme) if (magic.StartsWith(new byte?[] { 0xce, 0xfa, 0xed, 0xfe })) return true; // Mach-O binary (64-bit) if (magic.StartsWith(new byte?[] { 0xfe, 0xed, 0xfa, 0xcf })) return true; // Mach-O binary (64-bit, reverse byte ordering scheme) if (magic.StartsWith(new byte?[] { 0xcf, 0xfa, 0xed, 0xfe })) return true; // Prefrred Executable File Format if (magic.StartsWith(new byte?[] { 0x4a, 0x6f, 0x79, 0x21, 0x70, 0x65, 0x66, 0x66 })) return true; return false; } /// public ConcurrentDictionary> Scan(Scanner scanner, string file) { if (!File.Exists(file)) return null; using (var fs = File.OpenRead(file)) { return Scan(scanner, fs, file); } } /// public ConcurrentDictionary> Scan(Scanner scanner, Stream stream, string file) { // Files can be protected in multiple ways var protections = new ConcurrentDictionary>(); // Load the current file content for debug only byte[] fileContent = null; if (scanner.IncludeDebug) { try { using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true)) { fileContent = br.ReadBytes((int)stream.Length); } } catch { Utilities.AppendToDictionary(protections, file, "[Out of memory attempting to open]"); return protections; } } // Create PortableExecutable and NewExecutable objects for use in the checks stream.Seek(0, SeekOrigin.Begin); PortableExecutable pex = new PortableExecutable(stream); stream.Seek(0, SeekOrigin.Begin); NewExecutable nex = new NewExecutable(stream); stream.Seek(0, SeekOrigin.Begin); // Iterate through all generic content checks if (fileContent != null) { Parallel.ForEach(contentCheckClasses, contentCheckClass => { string protection = contentCheckClass.CheckContents(file, fileContent, scanner.IncludeDebug, pex, nex); if (ShouldAddProtection(contentCheckClass, scanner, protection)) Utilities.AppendToDictionary(protections, file, protection); // If we have an IScannable implementation if (contentCheckClass is IScannable scannable) { if (file != null && !string.IsNullOrEmpty(protection)) { var subProtections = scannable.Scan(scanner, null, file); Utilities.PrependToKeys(subProtections, file); Utilities.AppendToDictionary(protections, subProtections); } } }); } // If we have a NE executable, iterate through all NE content checks if (nex?.Initialized == true) { Parallel.ForEach(neContentCheckClasses, contentCheckClass => { // Check using custom content checks first string protection = contentCheckClass.CheckNEContents(file, nex, scanner.IncludeDebug); if (ShouldAddProtection(contentCheckClass, scanner, protection)) Utilities.AppendToDictionary(protections, file, protection); // If we have an IScannable implementation if (contentCheckClass is IScannable scannable) { if (file != null && !string.IsNullOrEmpty(protection)) { var subProtections = scannable.Scan(scanner, null, file); Utilities.PrependToKeys(subProtections, file); Utilities.AppendToDictionary(protections, subProtections); } } }); } // If we have a PE executable, iterate through all PE content checks if (pex?.Initialized == true) { Parallel.ForEach(peContentCheckClasses, contentCheckClass => { // Check using custom content checks first string protection = contentCheckClass.CheckPEContents(file, pex, scanner.IncludeDebug); if (ShouldAddProtection(contentCheckClass, scanner, protection)) Utilities.AppendToDictionary(protections, file, protection); // If we have an IScannable implementation if (contentCheckClass is IScannable scannable) { if (file != null && !string.IsNullOrEmpty(protection)) { var subProtections = scannable.Scan(scanner, null, file); Utilities.PrependToKeys(subProtections, file); Utilities.AppendToDictionary(protections, subProtections); } } }); } return protections; } #region Helpers /// /// Initialize all IContentCheck implementations /// private static IEnumerable InitContentCheckClasses() { return Assembly.GetExecutingAssembly().GetTypes() .Where(t => t.IsClass && t.GetInterface(nameof(IContentCheck)) != null) .Select(t => Activator.CreateInstance(t) as IContentCheck); } /// /// Initialize all INEContentCheck implementations /// private static IEnumerable InitNEContentCheckClasses() { return Assembly.GetExecutingAssembly().GetTypes() .Where(t => t.IsClass && t.GetInterface(nameof(INEContentCheck)) != null) .Select(t => Activator.CreateInstance(t) as INEContentCheck); } /// /// Initialize all IPEContentCheck implementations /// private static IEnumerable InitPEContentCheckClasses() { return Assembly.GetExecutingAssembly().GetTypes() .Where(t => t.IsClass && t.GetInterface(nameof(IPEContentCheck)) != null) .Select(t => Activator.CreateInstance(t) as IPEContentCheck); } /// /// Check to see if a protection should be added or not /// /// Class that was last used to check /// Scanner object for state tracking /// The protection result to be checked private bool ShouldAddProtection(IContentCheck contentCheckClass, Scanner scanner, string protection) { // If we have a valid content check based on settings if (!contentCheckClass.GetType().Namespace.ToLowerInvariant().Contains("packertype") || scanner.ScanPackers) { if (!string.IsNullOrWhiteSpace(protection)) return true; } return false; } /// /// Check to see if a protection should be added or not /// /// Class that was last used to check /// Scanner object for state tracking /// The protection result to be checked private bool ShouldAddProtection(INEContentCheck neContentCheckClass, Scanner scanner, string protection) { // If we have a valid content check based on settings if (!neContentCheckClass.GetType().Namespace.ToLowerInvariant().Contains("packertype") || scanner.ScanPackers) { if (!string.IsNullOrWhiteSpace(protection)) return true; } return false; } /// /// Check to see if a protection should be added or not /// /// Class that was last used to check /// Scanner object for state tracking /// The protection result to be checked private bool ShouldAddProtection(IPEContentCheck peContentCheckClass, Scanner scanner, string protection) { // If we have a valid content check based on settings if (!peContentCheckClass.GetType().Namespace.ToLowerInvariant().Contains("packertype") || scanner.ScanPackers) { if (!string.IsNullOrWhiteSpace(protection)) return true; } return false; } #endregion } }