using System; using System.Collections.Concurrent; using System.IO; using System.Text; using System.Threading.Tasks; using BurnOutSharp.Interfaces; using BurnOutSharp.Matching; using BurnOutSharp.Utilities; using BurnOutSharp.Wrappers; using static BurnOutSharp.Utilities.Dictionary; namespace BurnOutSharp.FileType { /// /// Executable or library /// public class Executable : IScannable { /// public ConcurrentDictionary> Scan(Scanner scanner, string file) { if (!File.Exists(file)) return null; using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) { 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 (Exception ex) { if (scanner.IncludeDebug) Console.WriteLine(ex); // Enable for odd files, keep disabled otherwise // AppendToDictionary(protections, file, "[Out of memory attempting to open]"); // return protections; } } // Get the wrapper for the appropriate executable type WrapperBase wrapper = DetermineExecutableType(stream); if (wrapper == null) return protections; // Iterate through all generic content checks if (fileContent != null) { Parallel.ForEach(ScanningClasses.ContentCheckClasses, contentCheckClass => { string protection = contentCheckClass.CheckContents(file, fileContent, scanner.IncludeDebug); if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection)) 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, file); PrependToKeys(subProtections, file); AppendToDictionary(protections, subProtections); } } }); } // If we have an MS-DOS executable if (wrapper is MSDOS mz) { // No-op } // If we have a New Executable if (wrapper is NewExecutable nex) { Parallel.ForEach(ScanningClasses.NewExecutableCheckClasses, contentCheckClass => { // Check using custom content checks first string protection = contentCheckClass.CheckNewExecutable(file, nex, scanner.IncludeDebug); if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection)) 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, file); PrependToKeys(subProtections, file); AppendToDictionary(protections, subProtections); } } }); } // If we have a Linear Executable else if (wrapper is LinearExecutable lex) { // No-op } // If we have a Portable Executable else if (wrapper is PortableExecutable pex) { Parallel.ForEach(ScanningClasses.PortableExecutableCheckClasses, contentCheckClass => { // Check using custom content checks first string protection = contentCheckClass.CheckPortableExecutable(file, pex, scanner.IncludeDebug); if (ShouldAddProtection(contentCheckClass, scanner.ScanPackers, protection)) 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, file); PrependToKeys(subProtections, file); AppendToDictionary(protections, subProtections); } } }); } return protections; } #region Helpers /// /// Determine the executable type from the stream /// /// Stream data to parse /// WrapperBase representing the executable, null on error private WrapperBase DetermineExecutableType(Stream stream) { // Try to get an MS-DOS wrapper first WrapperBase wrapper = MSDOS.Create(stream); if (wrapper == null) return null; // Check for a valid new executable address if ((wrapper as MSDOS).NewExeHeaderAddr >= stream.Length) return wrapper; // Try to read the executable info stream.Seek((wrapper as MSDOS).NewExeHeaderAddr, SeekOrigin.Begin); byte[] magic = stream.ReadBytes(4); // New Executable if (magic.StartsWith(Models.NewExecutable.Constants.SignatureBytes)) { stream.Seek(0, SeekOrigin.Begin); return NewExecutable.Create(stream); } // Linear Executable else if (magic.StartsWith(Models.LinearExecutable.Constants.LESignatureBytes) || magic.StartsWith(Models.LinearExecutable.Constants.LXSignatureBytes)) { stream.Seek(0, SeekOrigin.Begin); return LinearExecutable.Create(stream); } // Portable Executable else if (magic.StartsWith(Models.PortableExecutable.Constants.SignatureBytes)) { stream.Seek(0, SeekOrigin.Begin); return PortableExecutable.Create(stream); } // Everything else fails return null; } /// /// Check to see if a protection should be added or not /// /// Class that was last used to check /// Determines if packers should be included in the output /// The protection result to be checked private bool ShouldAddProtection(object checkClass, bool scanPackers, string protection) { // If we have an invalid protection if (string.IsNullOrWhiteSpace(protection)) return false; // If we have a valid content check based on settings if (scanPackers || !checkClass.GetType().Namespace.ToLowerInvariant().Contains("packertype")) return true; // Everything else fails return false; } #endregion } }