using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using BinaryObjectScanner.Interfaces; using BinaryObjectScanner.Utilities; using static BinaryObjectScanner.Utilities.Dictionary; namespace BurnOutSharp { internal static class Handler { #region Public Collections /// /// Cache for all IPathCheck types /// public static IEnumerable PathCheckClasses { get { if (pathCheckClasses == null) pathCheckClasses = InitCheckClasses(); return pathCheckClasses; } } #endregion #region Internal Instances /// /// Cache for all IPathCheck types /// private static IEnumerable pathCheckClasses; #endregion #region Multiple Implementation Wrappers /// /// Handle a single path based on all path check implementations /// /// Path of the file or directory to check /// Scanner object to use for options and scanning /// Set of protections in file, null on error public static ConcurrentDictionary> HandlePathChecks(string path, IEnumerable files) { // Create the output dictionary var protections = new ConcurrentDictionary>(); // Preprocess the list of files files = files?.Select(f => f.Replace('\\', '/'))?.ToList(); // Iterate through all checks Parallel.ForEach(PathCheckClasses, checkClass => { var subProtections = checkClass.PerformCheck(path, files); if (subProtections != null) AppendToDictionary(protections, path, subProtections); }); return protections; } #endregion #region Single Implementation Handlers /// /// Handle files based on an IDetectable implementation /// /// IDetectable class representing the file type /// 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 static ConcurrentQueue HandleDetectable(IDetectable impl, string fileName, Stream stream, bool includeDebug) { string protection = impl.Detect(stream, fileName, includeDebug); return ProcessProtectionString(protection); } /// /// Handle files based on an IExtractable implementation /// /// IDetectable class representing the file type /// Name of the source file of the stream, for tracking /// Stream to scan the contents of /// Scanner object to use on extractable contents /// Set of protections in file, null on error public static ConcurrentDictionary> HandleExtractable(IExtractable impl, string fileName, Stream stream, Scanner scanner) { // If the extractable file itself fails try { // Extract and get the output path string tempPath = impl.Extract(stream, fileName, scanner.IncludeDebug); if (tempPath == null) return null; // 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(subProtections, tempPath); PrependToKeys(subProtections, fileName); return subProtections; } catch (Exception ex) { if (scanner.IncludeDebug) Console.WriteLine(ex); } return null; } /// /// Handle files based on an IPathCheck implementation /// /// IPathCheck class representing the file type /// Path of the file or directory to check /// Set of protections in path, null on error private static ConcurrentQueue PerformCheck(this IPathCheck impl, string path, IEnumerable files) { // If we have an invalid path if (string.IsNullOrWhiteSpace(path)) return null; // Setup the output dictionary var protections = new ConcurrentQueue(); // If we have a file path if (File.Exists(path)) { string protection = impl.CheckFilePath(path); var subProtections = ProcessProtectionString(protection); if (subProtections != null) protections.AddRange(subProtections); } // If we have a directory path if (Directory.Exists(path) && files?.Any() == true) { var subProtections = impl.CheckDirectoryPath(path, files); if (subProtections != null) protections.AddRange(subProtections); } return protections; } #endregion #region Initializers /// /// Initialize all implementations of a type /// private static IEnumerable InitCheckClasses() => InitCheckClasses(typeof(BinaryObjectScanner.Packer._DUMMY).Assembly) .Concat(InitCheckClasses(typeof(BinaryObjectScanner.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 /// /// Process a protection string if it includes multiple protections /// /// Protection string to process /// Set of protections parsed, null on error private static ConcurrentQueue ProcessProtectionString(string protection) { // If we have an invalid protection string if (string.IsNullOrWhiteSpace(protection)) return null; // Setup the output queue var protections = new ConcurrentQueue(); // If we have an indicator of multiple protections if (protection.Contains(";")) { var splitProtections = protection.Split(';'); protections.AddRange(splitProtections); } else { protections.Enqueue(protection); } return protections; } #endregion } }