diff --git a/BurnOutSharp/PackerType/ASPack.cs b/BurnOutSharp/PackerType/ASPack.cs index c2d73a50..12a29fd3 100644 --- a/BurnOutSharp/PackerType/ASPack.cs +++ b/BurnOutSharp/PackerType/ASPack.cs @@ -67,7 +67,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/AdvancedInstaller.cs b/BurnOutSharp/PackerType/AdvancedInstaller.cs index 11611f78..498f2784 100644 --- a/BurnOutSharp/PackerType/AdvancedInstaller.cs +++ b/BurnOutSharp/PackerType/AdvancedInstaller.cs @@ -10,7 +10,7 @@ namespace BurnOutSharp.PackerType { // TODO: Add extraction // TODO: Verify that all versions are detected - public class AdvancedInstaller : IPortableExecutableCheck, IScannable + public class AdvancedInstaller : IExtractable, IPortableExecutableCheck, IScannable { /// public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug) @@ -31,6 +31,24 @@ namespace BurnOutSharp.PackerType return null; } + /// + public string Extract(string file) + { + if (!File.Exists(file)) + return null; + + using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + return Extract(fs, file); + } + } + + /// + public string Extract(Stream stream, string file) + { + return null; + } + /// public ConcurrentDictionary> Scan(Scanner scanner, string file) { diff --git a/BurnOutSharp/PackerType/Armadillo.cs b/BurnOutSharp/PackerType/Armadillo.cs index a9fd4f07..e20b3e91 100644 --- a/BurnOutSharp/PackerType/Armadillo.cs +++ b/BurnOutSharp/PackerType/Armadillo.cs @@ -56,7 +56,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/AutoPlayMediaStudio.cs b/BurnOutSharp/PackerType/AutoPlayMediaStudio.cs index 1a0b4a83..daa4118f 100644 --- a/BurnOutSharp/PackerType/AutoPlayMediaStudio.cs +++ b/BurnOutSharp/PackerType/AutoPlayMediaStudio.cs @@ -50,7 +50,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/CExe.cs b/BurnOutSharp/PackerType/CExe.cs index 9fec7f35..99fbbeed 100644 --- a/BurnOutSharp/PackerType/CExe.cs +++ b/BurnOutSharp/PackerType/CExe.cs @@ -64,8 +64,75 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan - return null; + // Parse into an executable again for easier extraction + PortableExecutable pex = PortableExecutable.Create(stream); + if (pex == null) + return null; + + // Get the first resource of type 99 with index 2 + byte[] payload = pex.FindResourceByNamedType("99, 2").FirstOrDefault(); + if (payload == null || payload.Length == 0) + return null; + + // Determine which compression was used + bool zlib = pex.FindResourceByNamedType("99, 1").Any(); + + // Create the output data buffer + byte[] data; + + // If we had the decompression DLL included, it's zlib + if (zlib) + { + try + { + // Inflate the data into the buffer + Inflater inflater = new Inflater(); + inflater.SetInput(payload); + data = new byte[payload.Length * 4]; + int read = inflater.Inflate(data); + + // Trim the buffer to the proper size + data = new ReadOnlySpan(data, 0, read).ToArray(); + } + catch + { + // Reset the data + data = null; + } + } + + // Otherwise, LZ is used via the Windows API + else + { + try + { + data = LZ.Decompress(payload); + } + catch + { + // Reset the data + data = null; + } + } + + // If we have no data + if (data == null) + return null; + + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Create the temp filename + string tempFile = string.IsNullOrEmpty(file) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(file)}.sxe"; + tempFile = Path.Combine(tempPath, tempFile); + + // Write the file data to a temp file + using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + tempStream.Write(data, 0, data.Length); + } + + return tempPath; } /// diff --git a/BurnOutSharp/PackerType/EXEStealth.cs b/BurnOutSharp/PackerType/EXEStealth.cs index 59298a56..4d473629 100644 --- a/BurnOutSharp/PackerType/EXEStealth.cs +++ b/BurnOutSharp/PackerType/EXEStealth.cs @@ -91,7 +91,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/EmbeddedExecutable.cs b/BurnOutSharp/PackerType/EmbeddedExecutable.cs index d3fb62c0..338637d9 100644 --- a/BurnOutSharp/PackerType/EmbeddedExecutable.cs +++ b/BurnOutSharp/PackerType/EmbeddedExecutable.cs @@ -45,8 +45,38 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan - return null; + // Parse into an executable again for easier extraction + PortableExecutable 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[]) + .Where(kvp => (kvp.Value as byte[]).StartsWith(BinaryObjectScanner.Models.MSDOS.Constants.SignatureBytes)) + .ToList(); + + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + for (int i = 0; i < resources.Count; i++) + { + // Get the resource data + var resource = resources[i]; + byte[] data = resource.Value as byte[]; + + // Create the temp filename + string tempFile = $"embedded_resource_{i}.bin"; + tempFile = Path.Combine(tempPath, tempFile); + + // Write the resource data to a temp file + using (Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + tempStream.Write(data, 0, data.Length); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/PackerType/GenteeInstaller.cs b/BurnOutSharp/PackerType/GenteeInstaller.cs index 78342ece..ce97aa24 100644 --- a/BurnOutSharp/PackerType/GenteeInstaller.cs +++ b/BurnOutSharp/PackerType/GenteeInstaller.cs @@ -49,7 +49,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/HyperTechCrackProof.cs b/BurnOutSharp/PackerType/HyperTechCrackProof.cs index 85edd451..0412de3d 100644 --- a/BurnOutSharp/PackerType/HyperTechCrackProof.cs +++ b/BurnOutSharp/PackerType/HyperTechCrackProof.cs @@ -48,7 +48,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/InnoSetup.cs b/BurnOutSharp/PackerType/InnoSetup.cs index 1c98bf2e..028f9ebc 100644 --- a/BurnOutSharp/PackerType/InnoSetup.cs +++ b/BurnOutSharp/PackerType/InnoSetup.cs @@ -74,7 +74,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/InstallAnywhere.cs b/BurnOutSharp/PackerType/InstallAnywhere.cs index 45cfd2e5..7ec3a48f 100644 --- a/BurnOutSharp/PackerType/InstallAnywhere.cs +++ b/BurnOutSharp/PackerType/InstallAnywhere.cs @@ -45,7 +45,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/InstallerVISE.cs b/BurnOutSharp/PackerType/InstallerVISE.cs index 684c8535..c680543a 100644 --- a/BurnOutSharp/PackerType/InstallerVISE.cs +++ b/BurnOutSharp/PackerType/InstallerVISE.cs @@ -47,7 +47,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/IntelInstallationFramework.cs b/BurnOutSharp/PackerType/IntelInstallationFramework.cs index 819584fe..46e9986a 100644 --- a/BurnOutSharp/PackerType/IntelInstallationFramework.cs +++ b/BurnOutSharp/PackerType/IntelInstallationFramework.cs @@ -50,7 +50,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/MicrosoftCABSFX.cs b/BurnOutSharp/PackerType/MicrosoftCABSFX.cs index f3dd9c7b..a6edf93e 100644 --- a/BurnOutSharp/PackerType/MicrosoftCABSFX.cs +++ b/BurnOutSharp/PackerType/MicrosoftCABSFX.cs @@ -65,7 +65,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/NSIS.cs b/BurnOutSharp/PackerType/NSIS.cs index 4e386257..656b0bc0 100644 --- a/BurnOutSharp/PackerType/NSIS.cs +++ b/BurnOutSharp/PackerType/NSIS.cs @@ -49,7 +49,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/PECompact.cs b/BurnOutSharp/PackerType/PECompact.cs index 61749636..03c647eb 100644 --- a/BurnOutSharp/PackerType/PECompact.cs +++ b/BurnOutSharp/PackerType/PECompact.cs @@ -58,7 +58,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/Petite.cs b/BurnOutSharp/PackerType/Petite.cs index 74e1e6b8..43aeb431 100644 --- a/BurnOutSharp/PackerType/Petite.cs +++ b/BurnOutSharp/PackerType/Petite.cs @@ -41,7 +41,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/SetupFactory.cs b/BurnOutSharp/PackerType/SetupFactory.cs index f8010e00..22ac51b3 100644 --- a/BurnOutSharp/PackerType/SetupFactory.cs +++ b/BurnOutSharp/PackerType/SetupFactory.cs @@ -55,7 +55,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/SevenZipSFX.cs b/BurnOutSharp/PackerType/SevenZipSFX.cs index c3d12932..86b58a57 100644 --- a/BurnOutSharp/PackerType/SevenZipSFX.cs +++ b/BurnOutSharp/PackerType/SevenZipSFX.cs @@ -62,7 +62,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/Shrinker.cs b/BurnOutSharp/PackerType/Shrinker.cs index 9b8e2927..9ae8aa0f 100644 --- a/BurnOutSharp/PackerType/Shrinker.cs +++ b/BurnOutSharp/PackerType/Shrinker.cs @@ -42,7 +42,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/UPX.cs b/BurnOutSharp/PackerType/UPX.cs index cad3e7f1..d46b0804 100644 --- a/BurnOutSharp/PackerType/UPX.cs +++ b/BurnOutSharp/PackerType/UPX.cs @@ -80,7 +80,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; } diff --git a/BurnOutSharp/PackerType/WinRARSFX.cs b/BurnOutSharp/PackerType/WinRARSFX.cs index c49328d3..8fc9d8ce 100644 --- a/BurnOutSharp/PackerType/WinRARSFX.cs +++ b/BurnOutSharp/PackerType/WinRARSFX.cs @@ -47,8 +47,24 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan - return null; + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Should be using stream instead of file, but stream fails to extract anything. My guess is that the executable portion of the archive is causing stream to fail, but not file. + using (RarArchive zipFile = RarArchive.Open(file, new SharpCompress.Readers.ReaderOptions() { LookForHeader = true })) + { + foreach (var entry in zipFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/PackerType/WinZipSFX.cs b/BurnOutSharp/PackerType/WinZipSFX.cs index 3433ee2b..13e55dda 100644 --- a/BurnOutSharp/PackerType/WinZipSFX.cs +++ b/BurnOutSharp/PackerType/WinZipSFX.cs @@ -82,8 +82,24 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan - return null; + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // Should be using stream instead of file, but stream fails to extract anything. My guess is that the executable portion of the archive is causing stream to fail, but not file. + using (ZipArchive zipFile = ZipArchive.Open(file)) + { + foreach (var entry in zipFile.Entries) + { + // If we have a directory, skip it + if (entry.IsDirectory) + continue; + + string tempFile = Path.Combine(tempPath, entry.Key); + entry.WriteToFile(tempFile); + } + } + + return tempPath; } /// diff --git a/BurnOutSharp/PackerType/WiseInstaller.cs b/BurnOutSharp/PackerType/WiseInstaller.cs index c1a192fa..14356261 100644 --- a/BurnOutSharp/PackerType/WiseInstaller.cs +++ b/BurnOutSharp/PackerType/WiseInstaller.cs @@ -94,7 +94,16 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan + // Try to parse as a New Executable + NewExecutable nex = NewExecutable.Create(stream); + if (nex != null) + return ExtractNewExecutable(nex, file); + + // Try to parse as a Portable Executable + PortableExecutable pex = PortableExecutable.Create(stream); + if (pex != null) + return ExtractPortableExecutable(pex, file); + return null; } @@ -283,6 +292,24 @@ namespace BurnOutSharp.PackerType return null; } + /// + /// Attempt to extract Wise data from a New Executable + /// + /// New executable to check + /// Path to the input file + /// True if it matches a known version, false otherwise + private string ExtractNewExecutable(NewExecutable nex, string file) + { + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + // TODO: Try to find where the file data lives and how to get it + Wise unpacker = new Wise(); + unpacker.ExtractTo(file, tempPath); + + return tempPath; + } + /// /// Attempt to extract Wise data from a Portable Executable /// @@ -431,6 +458,101 @@ namespace BurnOutSharp.PackerType return null; } + /// + /// Attempt to extract Wise data from a Portable Executable + /// + /// Portable executable to check + /// Path to the input file + /// True if it matches a known version, false otherwise + private string ExtractPortableExecutable(PortableExecutable pex, string file) + { + // 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; + byte[] 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 + byte[] magic = overlayData.ReadBytes(ref overlayOffset, 4); overlayOffset -= 4; + bool pkzip = magic.StartsWith(new byte?[] { (byte)'P', (byte)'K' }); + + 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(); + unpacker.ExtractTo(file, tempPath); + } + + return tempPath; + } + /// /// Class representing the properties of each recognized Wise installer format /// diff --git a/BurnOutSharp/PackerType/dotFuscator.cs b/BurnOutSharp/PackerType/dotFuscator.cs index 5299a03f..832dae05 100644 --- a/BurnOutSharp/PackerType/dotFuscator.cs +++ b/BurnOutSharp/PackerType/dotFuscator.cs @@ -45,7 +45,6 @@ namespace BurnOutSharp.PackerType /// public string Extract(Stream stream, string file) { - // Create extraction based off Scan return null; }