diff --git a/BinaryObjectScanner/Handler.cs b/BinaryObjectScanner/Handler.cs index 8a726099..ba76668d 100644 --- a/BinaryObjectScanner/Handler.cs +++ b/BinaryObjectScanner/Handler.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Threading.Tasks; using BinaryObjectScanner.Interfaces; using BinaryObjectScanner.Utilities; +using SabreTools.Serialization.Wrappers; using static BinaryObjectScanner.Utilities.Dictionary; namespace BinaryObjectScanner @@ -108,7 +109,7 @@ namespace BinaryObjectScanner /// /// Handle files based on an IExtractable implementation /// - /// IDetectable class representing the file type + /// IExtractable 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 @@ -153,6 +154,198 @@ namespace BinaryObjectScanner return null; } + /// + /// Handle files based on an IExtractableMSDOSExecutable implementation + /// + /// IExtractableMSDOSExecutable class representing the file type + /// Name of the source file of the stream, for tracking + /// MSDOS to scan the contents of + /// Scanner object to use on extractable contents + /// Set of protections in file, null on error +#if NET20 || NET35 + public static Dictionary>? HandleExtractable(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner) +#else + public static ConcurrentDictionary>? HandleExtractable(IExtractableMSDOSExecutable impl, string fileName, MSDOS mz, Scanner scanner) +#endif + { + // If the extractable file itself fails + try + { + // Extract and get the output path + var tempPath = impl.Extract(fileName, mz, 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 IExtractableLinearExecutable implementation + /// + /// IExtractableLinearExecutable class representing the file type + /// Name of the source file of the stream, for tracking + /// LinearExecutable to scan the contents of + /// Scanner object to use on extractable contents + /// Set of protections in file, null on error +#if NET20 || NET35 + public static Dictionary>? HandleExtractable(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner) +#else + public static ConcurrentDictionary>? HandleExtractable(IExtractableLinearExecutable impl, string fileName, LinearExecutable lex, Scanner scanner) +#endif + { + // If the extractable file itself fails + try + { + // Extract and get the output path + var tempPath = impl.Extract(fileName, lex, 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 IExtractableNewExecutable implementation + /// + /// IExtractableNewExecutable class representing the file type + /// Name of the source file of the stream, for tracking + /// NewExecutable to scan the contents of + /// Scanner object to use on extractable contents + /// Set of protections in file, null on error +#if NET20 || NET35 + public static Dictionary>? HandleExtractable(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner) +#else + public static ConcurrentDictionary>? HandleExtractable(IExtractableNewExecutable impl, string fileName, NewExecutable nex, Scanner scanner) +#endif + { + // If the extractable file itself fails + try + { + // Extract and get the output path + var tempPath = impl.Extract(fileName, nex, 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 IExtractablePortableExecutable implementation + /// + /// IExtractablePortableExecutable class representing the file type + /// Name of the source file of the stream, for tracking + /// PortableExecutable to scan the contents of + /// Scanner object to use on extractable contents + /// Set of protections in file, null on error +#if NET20 || NET35 + public static Dictionary>? HandleExtractable(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner) +#else + public static ConcurrentDictionary>? HandleExtractable(IExtractablePortableExecutable impl, string fileName, PortableExecutable pex, Scanner scanner) +#endif + { + // If the extractable file itself fails + try + { + // Extract and get the output path + var tempPath = impl.Extract(fileName, pex, 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 /// diff --git a/BinaryObjectScanner/Interfaces/IExtractableLinearExecutable.cs b/BinaryObjectScanner/Interfaces/IExtractableLinearExecutable.cs new file mode 100644 index 00000000..3e96bcb1 --- /dev/null +++ b/BinaryObjectScanner/Interfaces/IExtractableLinearExecutable.cs @@ -0,0 +1,19 @@ +using SabreTools.Serialization.Wrappers; + +namespace BinaryObjectScanner.Interfaces +{ + /// + /// Mark a LinearExecutable type as being able to be extracted + /// + public interface IExtractableLinearExecutable + { + /// + /// Extract a LinearExecutable to a temporary path, if possible + /// + /// Path to the input file + /// LinearExecutable representing the read-in file + /// True to include debug data, false otherwise + /// Path to extracted files, null on error + string? Extract(string file, LinearExecutable lex, bool includeDebug); + } +} diff --git a/BinaryObjectScanner/Interfaces/IExtractableMSDOSExecutable.cs b/BinaryObjectScanner/Interfaces/IExtractableMSDOSExecutable.cs new file mode 100644 index 00000000..40b85ac7 --- /dev/null +++ b/BinaryObjectScanner/Interfaces/IExtractableMSDOSExecutable.cs @@ -0,0 +1,19 @@ +using SabreTools.Serialization.Wrappers; + +namespace BinaryObjectScanner.Interfaces +{ + /// + /// Mark a MSDOS type as being able to be extracted + /// + public interface IExtractableMSDOSExecutable + { + /// + /// Extract a MSDOS to a temporary path, if possible + /// + /// Path to the input file + /// MSDOS representing the read-in file + /// True to include debug data, false otherwise + /// Path to extracted files, null on error + string? Extract(string file, MSDOS mz, bool includeDebug); + } +} diff --git a/BinaryObjectScanner/Interfaces/IExtractableNewExecutable.cs b/BinaryObjectScanner/Interfaces/IExtractableNewExecutable.cs new file mode 100644 index 00000000..c288d78b --- /dev/null +++ b/BinaryObjectScanner/Interfaces/IExtractableNewExecutable.cs @@ -0,0 +1,19 @@ +using SabreTools.Serialization.Wrappers; + +namespace BinaryObjectScanner.Interfaces +{ + /// + /// Mark a NewExecutable type as being able to be extracted + /// + public interface IExtractableNewExecutable + { + /// + /// Extract a NewExecutable to a temporary path, if possible + /// + /// Path to the input file + /// NewExecutable representing the read-in file + /// True to include debug data, false otherwise + /// Path to extracted files, null on error + string? Extract(string file, NewExecutable nex, bool includeDebug); + } +} diff --git a/BinaryObjectScanner/Interfaces/IExtractablePortableExecutable.cs b/BinaryObjectScanner/Interfaces/IExtractablePortableExecutable.cs new file mode 100644 index 00000000..7e7b85bd --- /dev/null +++ b/BinaryObjectScanner/Interfaces/IExtractablePortableExecutable.cs @@ -0,0 +1,19 @@ +using SabreTools.Serialization.Wrappers; + +namespace BinaryObjectScanner.Interfaces +{ + /// + /// Mark a PortableExecutable type as being able to be extracted + /// + public interface IExtractablePortableExecutable + { + /// + /// Extract a PortableExecutable to a temporary path, if possible + /// + /// Path to the input file + /// PortableExecutable representing the read-in file + /// True to include debug data, false otherwise + /// Path to extracted files, null on error + string? Extract(string file, PortableExecutable pex, bool includeDebug); + } +} diff --git a/BinaryObjectScanner/Packer/ASPack.cs b/BinaryObjectScanner/Packer/ASPack.cs index 856e4d3b..85b699f1 100644 --- a/BinaryObjectScanner/Packer/ASPack.cs +++ b/BinaryObjectScanner/Packer/ASPack.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Text; using BinaryObjectScanner.Interfaces; using SabreTools.Matching; @@ -8,7 +7,7 @@ using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction - public class ASPack : IExtractable, IPortableExecutableCheck + public class ASPack : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -51,19 +50,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/AdvancedInstaller.cs b/BinaryObjectScanner/Packer/AdvancedInstaller.cs index bae483c4..6469b2c6 100644 --- a/BinaryObjectScanner/Packer/AdvancedInstaller.cs +++ b/BinaryObjectScanner/Packer/AdvancedInstaller.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +6,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction // TODO: Verify that all versions are detected - public class AdvancedInstaller : IExtractable, IPortableExecutableCheck + public class AdvancedInstaller : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -30,19 +28,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/Armadillo.cs b/BinaryObjectScanner/Packer/Armadillo.cs index 756c3f44..21c3776e 100644 --- a/BinaryObjectScanner/Packer/Armadillo.cs +++ b/BinaryObjectScanner/Packer/Armadillo.cs @@ -1,5 +1,4 @@ -using System.IO; -using System.Linq; +using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer // TODO: Add extraction // TODO: Add version checking, if possible // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class Armadillo : IExtractable, IPortableExecutableCheck + public class Armadillo : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -42,19 +41,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs b/BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs index 51ede304..287a6ef5 100644 --- a/BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs +++ b/BinaryObjectScanner/Packer/AutoPlayMediaStudio.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer // Created by IndigoRose (creators of Setup Factory), primarily to be used to create autorun menus for various media. // Official website: https://www.autoplay.org/ // TODO: Add extraction - public class AutoPlayMediaStudio : IExtractable, IPortableExecutableCheck + public class AutoPlayMediaStudio : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -34,19 +33,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/CExe.cs b/BinaryObjectScanner/Packer/CExe.cs index 4365b1c5..0be6ad4a 100644 --- a/BinaryObjectScanner/Packer/CExe.cs +++ b/BinaryObjectScanner/Packer/CExe.cs @@ -12,7 +12,7 @@ namespace BinaryObjectScanner.Packer // The official website for CExe also includes the source code (which does have to be retrieved by the Wayback Machine) // http://www.scottlu.com/Content/CExe.html // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class CExe : IExtractable, IPortableExecutableCheck + public class CExe : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -50,26 +50,10 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Extract(fs, file, includeDebug); - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { try { - // Parse into an executable again for easier extraction - stream?.Seek(0, SeekOrigin.Begin); - var pex = PortableExecutable.Create(stream); - if (pex == null) - return null; - // Get the first resource of type 99 with index 2 var payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault(); if (payload == null || payload.Length == 0) diff --git a/BinaryObjectScanner/Packer/EXEStealth.cs b/BinaryObjectScanner/Packer/EXEStealth.cs index ae0d1a8a..dfa0c583 100644 --- a/BinaryObjectScanner/Packer/EXEStealth.cs +++ b/BinaryObjectScanner/Packer/EXEStealth.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Matching; using SabreTools.Serialization.Wrappers; @@ -10,7 +9,7 @@ namespace BinaryObjectScanner.Packer // TODO: Detect 3.15 and up (maybe looking for `Metamorphism`) // TODO: Add extraction // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class EXEStealth : IContentCheck, IExtractable, IPortableExecutableCheck + public class EXEStealth : IContentCheck, IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckContents(string file, byte[] fileContent, bool includeDebug) @@ -75,19 +74,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/EmbeddedExecutable.cs b/BinaryObjectScanner/Packer/EmbeddedExecutable.cs index 69939083..ac07a02b 100644 --- a/BinaryObjectScanner/Packer/EmbeddedExecutable.cs +++ b/BinaryObjectScanner/Packer/EmbeddedExecutable.cs @@ -11,7 +11,7 @@ namespace BinaryObjectScanner.Packer /// Though not technically a packer, this detection is for any executables that include /// others in their resources in some uncompressed manner to be used at runtime. /// - public class EmbeddedExecutable : IExtractable, IPortableExecutableCheck + public class EmbeddedExecutable : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -29,28 +29,10 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { try { - // Parse into an executable again for easier extraction - stream?.Seek(0, SeekOrigin.Begin); - var pex = PortableExecutable.Create(stream); - if (pex?.ResourceData == null) - return null; - // Get the resources that have an executable signature var resources = pex.ResourceData .Where(kvp => kvp.Value != null && kvp.Value is byte[]) diff --git a/BinaryObjectScanner/Packer/GenteeInstaller.cs b/BinaryObjectScanner/Packer/GenteeInstaller.cs index 8de4adfe..eaa6d955 100644 --- a/BinaryObjectScanner/Packer/GenteeInstaller.cs +++ b/BinaryObjectScanner/Packer/GenteeInstaller.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +6,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class GenteeInstaller : IExtractable, IPortableExecutableCheck + public class GenteeInstaller : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -33,19 +31,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/HyperTechCrackProof.cs b/BinaryObjectScanner/Packer/HyperTechCrackProof.cs index 1785ecb2..0d3b3bd8 100644 --- a/BinaryObjectScanner/Packer/HyperTechCrackProof.cs +++ b/BinaryObjectScanner/Packer/HyperTechCrackProof.cs @@ -1,5 +1,4 @@ -using System.IO; -using System.Linq; +using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -10,7 +9,7 @@ namespace BinaryObjectScanner.Packer // https://www.reddit.com/r/riseofincarnates/comments/m3vbnm/subreddit_revival_does_anyone_still_have_rise_of/ // https://steamcommunity.com/app/310950/discussions/0/4224890554455490819/ // https://github.com/horsicq/Detect-It-Easy/blob/63a1aa8bb23ca02d8a7fd5936db8dbc5c5d52dea/db/PE/HyperTech%20Crackproof.2.sg - public class HyperTechCrackProof : IExtractable, IPortableExecutableCheck + public class HyperTechCrackProof : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -32,19 +31,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/InnoSetup.cs b/BinaryObjectScanner/Packer/InnoSetup.cs index 2626a73e..a2b791a0 100644 --- a/BinaryObjectScanner/Packer/InnoSetup.cs +++ b/BinaryObjectScanner/Packer/InnoSetup.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Matching; @@ -9,7 +8,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction - https://github.com/dscharrer/InnoExtract // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class InnoSetup : IExtractable, INewExecutableCheck, IPortableExecutableCheck + public class InnoSetup : IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck { /// public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug) @@ -54,19 +53,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/InstallAnywhere.cs b/BinaryObjectScanner/Packer/InstallAnywhere.cs index 795e0885..454d17d4 100644 --- a/BinaryObjectScanner/Packer/InstallAnywhere.cs +++ b/BinaryObjectScanner/Packer/InstallAnywhere.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -7,7 +6,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction, which may be possible with the current libraries but needs to be investigated further. // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class InstallAnywhere : IExtractable, IPortableExecutableCheck + public class InstallAnywhere : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -29,19 +28,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/InstallerVISE.cs b/BinaryObjectScanner/Packer/InstallerVISE.cs index bc4dec8a..8b99bfb6 100644 --- a/BinaryObjectScanner/Packer/InstallerVISE.cs +++ b/BinaryObjectScanner/Packer/InstallerVISE.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +6,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction - https://github.com/Bioruebe/UniExtract2 // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class InstallerVISE : IExtractable, IPortableExecutableCheck + public class InstallerVISE : IExtractablePortableExecutable, IPortableExecutableCheck { //TODO: Add exact version detection for Windows builds, make sure versions before 3.X are detected as well, and detect the Mac builds. /// @@ -31,19 +29,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/IntelInstallationFramework.cs b/BinaryObjectScanner/Packer/IntelInstallationFramework.cs index b6405e19..89e40e16 100644 --- a/BinaryObjectScanner/Packer/IntelInstallationFramework.cs +++ b/BinaryObjectScanner/Packer/IntelInstallationFramework.cs @@ -1,12 +1,11 @@ using System; -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction, seems to primarily use MSZip compression. - public class IntelInstallationFramework : IExtractable, IPortableExecutableCheck + public class IntelInstallationFramework : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -34,19 +33,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/MicrosoftCABSFX.cs b/BinaryObjectScanner/Packer/MicrosoftCABSFX.cs index 4aec2120..0637e750 100644 --- a/BinaryObjectScanner/Packer/MicrosoftCABSFX.cs +++ b/BinaryObjectScanner/Packer/MicrosoftCABSFX.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction, which should be possible with LibMSPackN, but it refuses to extract due to SFX files lacking the typical CAB identifiers. // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class MicrosoftCABSFX : IExtractable, IPortableExecutableCheck + public class MicrosoftCABSFX : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -48,23 +47,11 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } - + private string GetVersion(PortableExecutable pex) { // Check the internal versions diff --git a/BinaryObjectScanner/Packer/NSIS.cs b/BinaryObjectScanner/Packer/NSIS.cs index eae6c31c..712ff773 100644 --- a/BinaryObjectScanner/Packer/NSIS.cs +++ b/BinaryObjectScanner/Packer/NSIS.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -7,7 +5,7 @@ using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction - public class NSIS : IExtractable, IPortableExecutableCheck + public class NSIS : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -33,19 +31,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/NeoLite.cs b/BinaryObjectScanner/Packer/NeoLite.cs index df4ef338..c550754e 100644 --- a/BinaryObjectScanner/Packer/NeoLite.cs +++ b/BinaryObjectScanner/Packer/NeoLite.cs @@ -14,7 +14,7 @@ namespace BinaryObjectScanner.Packer /// PEiD scanning definitions that include NeoLite: https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt /// Website listing various packers, including NeoLite: http://protools.narod.ru/packers.htm /// - public class NeoLite : IExtractable, IPortableExecutableCheck + public class NeoLite : IExtractablePortableExecutable, IPortableExecutableCheck { // TODO: Find samples of NeoLite 1.X. /// @@ -37,20 +37,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - // TODO: Add extraction - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/PECompact.cs b/BinaryObjectScanner/Packer/PECompact.cs index d9449a39..2e7fbbe1 100644 --- a/BinaryObjectScanner/Packer/PECompact.cs +++ b/BinaryObjectScanner/Packer/PECompact.cs @@ -1,12 +1,11 @@ -using System.IO; -using BinaryObjectScanner.Interfaces; +using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Better version detection - https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt // TODO: Add extraction - public class PECompact : IExtractable, IPortableExecutableCheck + public class PECompact : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -42,19 +41,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/Petite.cs b/BinaryObjectScanner/Packer/Petite.cs index d8592382..541182b5 100644 --- a/BinaryObjectScanner/Packer/Petite.cs +++ b/BinaryObjectScanner/Packer/Petite.cs @@ -1,12 +1,11 @@ -using System.IO; -using BinaryObjectScanner.Interfaces; +using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class PEtite : IExtractable, IPortableExecutableCheck + public class PEtite : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -25,19 +24,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/SetupFactory.cs b/BinaryObjectScanner/Packer/SetupFactory.cs index 7bb0e3f0..a310dcb9 100644 --- a/BinaryObjectScanner/Packer/SetupFactory.cs +++ b/BinaryObjectScanner/Packer/SetupFactory.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -8,7 +7,7 @@ namespace BinaryObjectScanner.Packer // TODO: Add extraction, which is possible but the only tools available that can // do this seem to be Universal Extractor 2 and InstallExplorer (https://totalcmd.net/plugring/InstallExplorer.html) // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class SetupFactory : IExtractable, IPortableExecutableCheck + public class SetupFactory : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -39,23 +38,11 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } - + private string GetVersion(PortableExecutable pex) { // Check the product version explicitly diff --git a/BinaryObjectScanner/Packer/SevenZipSFX.cs b/BinaryObjectScanner/Packer/SevenZipSFX.cs index 06ff29f8..29614da0 100644 --- a/BinaryObjectScanner/Packer/SevenZipSFX.cs +++ b/BinaryObjectScanner/Packer/SevenZipSFX.cs @@ -1,4 +1,3 @@ -using System.IO; using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -6,7 +5,7 @@ using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction - public class SevenZipSFX : IExtractable, IPortableExecutableCheck + public class SevenZipSFX : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -46,19 +45,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/Shrinker.cs b/BinaryObjectScanner/Packer/Shrinker.cs index 4814e19b..ba0458c8 100644 --- a/BinaryObjectScanner/Packer/Shrinker.cs +++ b/BinaryObjectScanner/Packer/Shrinker.cs @@ -1,4 +1,3 @@ -using System.IO; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; @@ -6,7 +5,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class Shrinker : IExtractable, IPortableExecutableCheck + public class Shrinker : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -26,19 +25,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/UPX.cs b/BinaryObjectScanner/Packer/UPX.cs index d82ea555..d1a4c317 100644 --- a/BinaryObjectScanner/Packer/UPX.cs +++ b/BinaryObjectScanner/Packer/UPX.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -10,7 +9,7 @@ namespace BinaryObjectScanner.Packer { // TODO: Add extraction // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class UPX : IExtractable, IPortableExecutableCheck + public class UPX : IExtractablePortableExecutable, IPortableExecutableCheck { private static readonly Regex _oldUpxVersionMatch = new Regex(@"\$Id: UPX (.*?) Copyright \(C\)", RegexOptions.Compiled); @@ -64,19 +63,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Packer/WinRARSFX.cs b/BinaryObjectScanner/Packer/WinRARSFX.cs index 7eeac8dc..c6e6aa49 100644 --- a/BinaryObjectScanner/Packer/WinRARSFX.cs +++ b/BinaryObjectScanner/Packer/WinRARSFX.cs @@ -11,7 +11,7 @@ using SharpCompress.Readers; namespace BinaryObjectScanner.Packer { - public class WinRARSFX : IExtractable, IPortableExecutableCheck + public class WinRARSFX : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -33,17 +33,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Extract(fs, file, includeDebug); - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { #if NET462_OR_GREATER || NETCOREAPP try diff --git a/BinaryObjectScanner/Packer/WinZipSFX.cs b/BinaryObjectScanner/Packer/WinZipSFX.cs index 91d5270f..7fbf8c23 100644 --- a/BinaryObjectScanner/Packer/WinZipSFX.cs +++ b/BinaryObjectScanner/Packer/WinZipSFX.cs @@ -11,7 +11,7 @@ using SharpCompress.Archives.Zip; namespace BinaryObjectScanner.Packer { - public class WinZipSFX : IExtractable, INewExecutableCheck, IPortableExecutableCheck + public class WinZipSFX : IExtractableNewExecutable, IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck { /// public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug) @@ -63,17 +63,17 @@ namespace BinaryObjectScanner.Packer // TODO: Find a way to generically detect 2.X versions and improve exact version detection for SFX PE versions bundled with WinZip 11+ /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Extract(fs, file, includeDebug); - } + public string? Extract(string file, NewExecutable nex, bool includeDebug) + => Extract(file, includeDebug); /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) + => Extract(file, includeDebug); + + /// + /// Handle common extraction between executable types + /// + private static string? Extract(string file, bool includeDebug) { #if NET462_OR_GREATER || NETCOREAPP try @@ -122,7 +122,7 @@ namespace BinaryObjectScanner.Packer /// /// TODO: Reduce the checks to only the ones that differ between versions /// TODO: Research to see if the versions are embedded elsewhere in these files - private string? GetNEHeaderVersion(NewExecutable nex) + private static string? GetNEHeaderVersion(NewExecutable nex) { #region 2.0 Variants @@ -680,7 +680,7 @@ namespace BinaryObjectScanner.Packer /// Get the version from the PE export directory table value combinations /// /// TODO: Research to see if the versions are embedded elsewhere in these files - private string? GetPEExportDirectoryVersion(PortableExecutable pex) + private static string? GetPEExportDirectoryVersion(PortableExecutable pex) { string sfxFileName = pex.Model.ExportTable?.ExportDirectoryTable?.Name ?? string.Empty; uint sfxTimeDateStamp = pex.Model.ExportTable?.ExportDirectoryTable?.TimeDateStamp ?? uint.MaxValue; diff --git a/BinaryObjectScanner/Packer/WiseInstaller.cs b/BinaryObjectScanner/Packer/WiseInstaller.cs index 750f740e..87a4bdb0 100644 --- a/BinaryObjectScanner/Packer/WiseInstaller.cs +++ b/BinaryObjectScanner/Packer/WiseInstaller.cs @@ -11,7 +11,7 @@ using Wise = WiseUnpacker.WiseUnpacker; namespace BinaryObjectScanner.Packer { // https://raw.githubusercontent.com/wolfram77web/app-peid/master/userdb.txt - public class WiseInstaller : IExtractable, INewExecutableCheck, IPortableExecutableCheck + public class WiseInstaller : IExtractableNewExecutable, IExtractablePortableExecutable, INewExecutableCheck, IPortableExecutableCheck { /// public string? CheckNewExecutable(string file, NewExecutable nex, bool includeDebug) @@ -73,33 +73,140 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) + public string? Extract(string file, NewExecutable nex, bool includeDebug) { - if (!File.Exists(file)) - return null; + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + try { - return Extract(fs, file, includeDebug); + // TODO: Try to find where the file data lives and how to get it + var unpacker = new Wise(); + if (!unpacker.ExtractTo(file, tempPath)) + { + try + { + Directory.Delete(tempPath, true); + } + catch (Exception ex) + { + if (includeDebug) Console.WriteLine(ex); + } + + return null; + } } + catch (Exception ex) + { + if (includeDebug) Console.WriteLine(ex); + return null; + } + + return tempPath; } /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { try { - // Try to parse as a New Executable - var nex = NewExecutable.Create(stream); - if (nex != null) - return ExtractNewExecutable(nex, file, includeDebug); + // Get the matching PE format + var format = GetPEFormat(pex); + if (format == null) + return null; - // Try to parse as a Portable Executable - var pex = PortableExecutable.Create(stream); - if (pex != null) - return ExtractPortableExecutable(pex, file, includeDebug); + // Get the overlay data for easier reading + int overlayOffset = 0, dataStart = 0; + var overlayData = pex.OverlayData; + if (overlayData == null) + return null; - return null; + // Skip over the additional DLL name, if we expect it + if (format.Dll) + { + // Read the name length + byte dllNameLength = overlayData.ReadByte(ref overlayOffset); + dataStart++; + + // Read the name, if it exists + if (dllNameLength != 0) + { + // Ignore the name for now + _ = overlayData.ReadBytes(ref overlayOffset, dllNameLength); + dataStart += dllNameLength; + + // Named DLLs also have a DLL length that we ignore + _ = overlayData.ReadUInt32(ref overlayOffset); + dataStart += 4; + } + } + + // Check if flags are consistent + if (!format.NoCrc) + { + // Unlike WiseUnpacker, we ignore the flag value here + _ = overlayData.ReadUInt32(ref overlayOffset); + } + + // Ensure that we have an archive end + if (format.ArchiveEnd > 0) + { + overlayOffset = dataStart + format.ArchiveEnd; + int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset); + if (archiveEndLoaded != 0) + format.ArchiveEnd = archiveEndLoaded; + } + + // Skip to the start of the archive + overlayOffset = dataStart + format.ArchiveStart; + + // Skip over the initialization text, if we expect it + if (format.InitText) + { + int initTextLength = overlayData.ReadByte(ref overlayOffset); + _ = overlayData.ReadBytes(ref overlayOffset, initTextLength); + } + + // Cache the current offset in the overlay as the "start of data" + int offsetReal = overlayOffset; + + // If the first entry is PKZIP, we assume it's an embedded zipfile + var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4; + bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false; + + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // If we have PKZIP + if (pkzip) + { + string tempFile = Path.Combine(tempPath, "WISEDATA.zip"); + using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset); + } + } + + // If we have DEFLATE -- TODO: Port implementation here or use DeflateStream + else + { + Wise unpacker = new Wise(); + if (!unpacker.ExtractTo(file, tempPath)) + { + try + { + Directory.Delete(tempPath, true); + } + catch (Exception ex) + { + if (includeDebug) Console.WriteLine(ex); + } + + return null; + } + } + + return tempPath; } catch (Exception ex) { @@ -210,161 +317,6 @@ namespace BinaryObjectScanner.Packer return null; } - /// - /// Attempt to extract Wise data from a New Executable - /// - /// New executable to check - /// Path to the input file - /// True to include debug data, false otherwise - /// True if it matches a known version, false otherwise - private string? ExtractNewExecutable(NewExecutable nex, string file, bool includeDebug) - { - string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempPath); - - try - { - // TODO: Try to find where the file data lives and how to get it - Wise unpacker = new Wise(); - if (!unpacker.ExtractTo(file, tempPath)) - { - try - { - Directory.Delete(tempPath, true); - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - } - - return null; - } - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - return null; - } - - return tempPath; - } - - /// - /// Attempt to extract Wise data from a Portable Executable - /// - /// Portable executable to check - /// Path to the input file - /// True to include debug data, false otherwise - /// True if it matches a known version, false otherwise - private string? ExtractPortableExecutable(PortableExecutable pex, string file, bool includeDebug) - { - try - { - // Get the matching PE format - var format = GetPEFormat(pex); - if (format == null) - return null; - - // Get the overlay data for easier reading - int overlayOffset = 0, dataStart = 0; - var overlayData = pex.OverlayData; - if (overlayData == null) - return null; - - // Skip over the additional DLL name, if we expect it - if (format.Dll) - { - // Read the name length - byte dllNameLength = overlayData.ReadByte(ref overlayOffset); - dataStart++; - - // Read the name, if it exists - if (dllNameLength != 0) - { - // Ignore the name for now - _ = overlayData.ReadBytes(ref overlayOffset, dllNameLength); - dataStart += dllNameLength; - - // Named DLLs also have a DLL length that we ignore - _ = overlayData.ReadUInt32(ref overlayOffset); - dataStart += 4; - } - } - - // Check if flags are consistent - if (!format.NoCrc) - { - // Unlike WiseUnpacker, we ignore the flag value here - _ = overlayData.ReadUInt32(ref overlayOffset); - } - - // Ensure that we have an archive end - if (format.ArchiveEnd > 0) - { - overlayOffset = dataStart + format.ArchiveEnd; - int archiveEndLoaded = overlayData.ReadInt32(ref overlayOffset); - if (archiveEndLoaded != 0) - format.ArchiveEnd = archiveEndLoaded; - } - - // Skip to the start of the archive - overlayOffset = dataStart + format.ArchiveStart; - - // Skip over the initialization text, if we expect it - if (format.InitText) - { - int initTextLength = overlayData.ReadByte(ref overlayOffset); - _ = overlayData.ReadBytes(ref overlayOffset, initTextLength); - } - - // Cache the current offset in the overlay as the "start of data" - int offsetReal = overlayOffset; - - // If the first entry is PKZIP, we assume it's an embedded zipfile - var magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4; - bool pkzip = magic?.StartsWith(new byte?[] { (byte)'P', (byte)'K' }) ?? false; - - string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempPath); - - // If we have PKZIP - if (pkzip) - { - string tempFile = Path.Combine(tempPath, "WISEDATA.zip"); - using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - { - tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset); - } - } - - // If we have DEFLATE -- TODO: Port implementation here or use DeflateStream - else - { - Wise unpacker = new Wise(); - if (!unpacker.ExtractTo(file, tempPath)) - { - try - { - Directory.Delete(tempPath, true); - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - } - - return null; - } - } - - return tempPath; - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - return null; - } - } - /// /// Class representing the properties of each recognized Wise installer format /// diff --git a/BinaryObjectScanner/Packer/dotFuscator.cs b/BinaryObjectScanner/Packer/dotFuscator.cs index 2a05ff04..8b1505fc 100644 --- a/BinaryObjectScanner/Packer/dotFuscator.cs +++ b/BinaryObjectScanner/Packer/dotFuscator.cs @@ -1,13 +1,11 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Linq; using BinaryObjectScanner.Interfaces; using SabreTools.Serialization.Wrappers; namespace BinaryObjectScanner.Packer { // TODO: Add extraction - public class dotFuscator : IExtractable, IPortableExecutableCheck + public class dotFuscator : IExtractablePortableExecutable, IPortableExecutableCheck { /// public string? CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -29,19 +27,7 @@ namespace BinaryObjectScanner.Packer } /// - public string? Extract(string file, bool includeDebug) - { - if (!File.Exists(file)) - return null; - - using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return Extract(fs, file, includeDebug); - } - } - - /// - public string? Extract(Stream? stream, string file, bool includeDebug) + public string? Extract(string file, PortableExecutable pex, bool includeDebug) { return null; } diff --git a/BinaryObjectScanner/Scanner.cs b/BinaryObjectScanner/Scanner.cs index 24f5be91..1f3c6b40 100644 --- a/BinaryObjectScanner/Scanner.cs +++ b/BinaryObjectScanner/Scanner.cs @@ -457,7 +457,7 @@ namespace BinaryObjectScanner AppendToDictionary(protections, fileName, subProtections.Values.ToArray()); // If we have any extractable packers - var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream); + var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, mz); if (extractedProtections != null) AppendToDictionary(protections, extractedProtections); } @@ -471,7 +471,7 @@ namespace BinaryObjectScanner AppendToDictionary(protections, fileName, subProtections.Values.ToArray()); // If we have any extractable packers - var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream); + var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, lex); if (extractedProtections != null) AppendToDictionary(protections, extractedProtections); } @@ -485,7 +485,7 @@ namespace BinaryObjectScanner AppendToDictionary(protections, fileName, subProtections.Values.ToArray()); // If we have any extractable packers - var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream); + var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, nex); if (extractedProtections != null) AppendToDictionary(protections, extractedProtections); } @@ -499,7 +499,7 @@ namespace BinaryObjectScanner AppendToDictionary(protections, fileName, subProtections.Values.ToArray()); // If we have any extractable packers - var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, stream); + var extractedProtections = HandleExtractableProtections(subProtections.Keys, fileName, pex); if (extractedProtections != null) AppendToDictionary(protections, extractedProtections); } @@ -512,12 +512,12 @@ namespace BinaryObjectScanner /// /// Set of classes returned from Exectuable scans /// Name of the source file of the stream, for tracking - /// Stream to scan the contents of + /// MSDOS to scan the contents of /// Set of protections found from extraction, null on error #if NET20 || NET35 - private Dictionary>? HandleExtractableProtections(Dictionary.KeyCollection? classes, string fileName, Stream stream) + private Dictionary>? HandleExtractableProtections(Dictionary.KeyCollection? classes, string fileName, MSDOS mz) #else - private ConcurrentDictionary>? HandleExtractableProtections(IEnumerable? classes, string fileName, Stream stream) + private ConcurrentDictionary>? HandleExtractableProtections(IEnumerable? classes, string fileName, MSDOS mz) #endif { // If we have an invalid set of classes @@ -532,7 +532,7 @@ namespace BinaryObjectScanner #endif // If we have any extractable packers - var extractables = classes.Where(c => c is IExtractable).Select(c => c as IExtractable); + var extractables = classes.Where(c => c is IExtractableMSDOSExecutable).Select(c => c as IExtractableMSDOSExecutable); #if NET20 || NET35 foreach (var extractable in extractables) #else @@ -548,7 +548,166 @@ namespace BinaryObjectScanner #endif // Get the protection for the class, if possible - var extractedProtections = Handler.HandleExtractable(extractable, fileName, stream, this); + var extractedProtections = Handler.HandleExtractable(extractable, fileName, mz, this); + if (extractedProtections != null) + AppendToDictionary(protections, extractedProtections); +#if NET20 || NET35 + } +#else + }); +#endif + + return protections; + } + + /// + /// Handle extractable protections, such as executable packers + /// + /// Set of classes returned from Exectuable scans + /// Name of the source file of the stream, for tracking + /// LinearExecutable to scan the contents of + /// Set of protections found from extraction, null on error +#if NET20 || NET35 + private Dictionary>? HandleExtractableProtections(Dictionary.KeyCollection? classes, string fileName, LinearExecutable lex) +#else + private ConcurrentDictionary>? HandleExtractableProtections(IEnumerable? classes, string fileName, LinearExecutable lex) +#endif + { + // If we have an invalid set of classes + if (classes == null || !classes.Any()) + return null; + + // Create the output dictionary +#if NET20 || NET35 + var protections = new Dictionary>(); +#else + var protections = new ConcurrentDictionary>(); +#endif + + // If we have any extractable packers + var extractables = classes.Where(c => c is IExtractableLinearExecutable).Select(c => c as IExtractableLinearExecutable); +#if NET20 || NET35 + foreach (var extractable in extractables) +#else + Parallel.ForEach(extractables, extractable => +#endif + { + // If we have an invalid extractable somehow + if (extractable == null) +#if NET20 || NET35 + continue; +#else + return; +#endif + + // Get the protection for the class, if possible + var extractedProtections = Handler.HandleExtractable(extractable, fileName, lex, this); + if (extractedProtections != null) + AppendToDictionary(protections, extractedProtections); +#if NET20 || NET35 + } +#else + }); +#endif + + return protections; + } + + /// + /// Handle extractable protections, such as executable packers + /// + /// Set of classes returned from Exectuable scans + /// Name of the source file of the stream, for tracking + /// NewExecutable to scan the contents of + /// Set of protections found from extraction, null on error +#if NET20 || NET35 + private Dictionary>? HandleExtractableProtections(Dictionary.KeyCollection? classes, string fileName, NewExecutable nex) +#else + private ConcurrentDictionary>? HandleExtractableProtections(IEnumerable? classes, string fileName, NewExecutable nex) +#endif + { + // If we have an invalid set of classes + if (classes == null || !classes.Any()) + return null; + + // Create the output dictionary +#if NET20 || NET35 + var protections = new Dictionary>(); +#else + var protections = new ConcurrentDictionary>(); +#endif + + // If we have any extractable packers + var extractables = classes.Where(c => c is IExtractableNewExecutable).Select(c => c as IExtractableNewExecutable); +#if NET20 || NET35 + foreach (var extractable in extractables) +#else + Parallel.ForEach(extractables, extractable => +#endif + { + // If we have an invalid extractable somehow + if (extractable == null) +#if NET20 || NET35 + continue; +#else + return; +#endif + + // Get the protection for the class, if possible + var extractedProtections = Handler.HandleExtractable(extractable, fileName, nex, this); + if (extractedProtections != null) + AppendToDictionary(protections, extractedProtections); +#if NET20 || NET35 + } +#else + }); +#endif + + return protections; + } + + /// + /// Handle extractable protections, such as executable packers + /// + /// Set of classes returned from Exectuable scans + /// Name of the source file of the stream, for tracking + /// PortableExecutable to scan the contents of + /// Set of protections found from extraction, null on error +#if NET20 || NET35 + private Dictionary>? HandleExtractableProtections(Dictionary.KeyCollection? classes, string fileName, PortableExecutable pex) +#else + private ConcurrentDictionary>? HandleExtractableProtections(IEnumerable? classes, string fileName, PortableExecutable pex) +#endif + { + // If we have an invalid set of classes + if (classes == null || !classes.Any()) + return null; + + // Create the output dictionary +#if NET20 || NET35 + var protections = new Dictionary>(); +#else + var protections = new ConcurrentDictionary>(); +#endif + + // If we have any extractable packers + var extractables = classes.Where(c => c is IExtractablePortableExecutable).Select(c => c as IExtractablePortableExecutable); +#if NET20 || NET35 + foreach (var extractable in extractables) +#else + Parallel.ForEach(extractables, extractable => +#endif + { + // If we have an invalid extractable somehow + if (extractable == null) +#if NET20 || NET35 + continue; +#else + return; +#endif + + // Get the protection for the class, if possible + var extractedProtections = Handler.HandleExtractable(extractable, fileName, pex, this); if (extractedProtections != null) AppendToDictionary(protections, extractedProtections); #if NET20 || NET35