using System; using System.IO; using SabreTools.IO.Extensions; using SabreTools.Matching; using SabreTools.Numerics.Extensions; namespace SabreTools.Wrappers { public partial class NewExecutable : IExtractable { /// /// /// This extracts the following data: /// - Archives and executables in the overlay /// - Wise installers /// public bool Extract(string outputDirectory, bool includeDebug) { bool overlay = ExtractFromOverlay(outputDirectory, includeDebug); bool wise = ExtractWise(outputDirectory, includeDebug); return overlay | wise; } /// /// Extract data from the overlay /// /// Output directory to write to /// True to include debug data, false otherwise /// True if extraction succeeded, false otherwise public bool ExtractFromOverlay(string outputDirectory, bool includeDebug) { try { // Cache the overlay data for easier reading var overlayData = OverlayData; if (overlayData.Length == 0) return false; // Set the output variables int overlayOffset = 0; string extension = string.Empty; // Only process the overlay if it is recognized for (; overlayOffset < 0x400 && overlayOffset < overlayData.Length - 0x10; overlayOffset++) { int temp = overlayOffset; byte[] overlaySample = overlayData.ReadBytes(ref temp, 0x10); if (overlaySample.StartsWith(Data.Models.SevenZip.Constants.SignatureBytes)) { extension = "7z"; break; } else if (overlaySample.StartsWith([0x3B, 0x21, 0x40, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C])) { // 7-zip SFX script -- ";!@Install" to ";!@InstallEnd@!" overlayOffset = overlayData.FirstPosition([0x3B, 0x21, 0x40, 0x49, 0x6E, 0x73, 0x74, 0x61, 0x6C, 0x6C, 0x45, 0x6E, 0x64, 0x40, 0x21]); if (overlayOffset == -1) return false; overlayOffset += 15; extension = "7z"; break; } else if (overlaySample.StartsWith(Data.Models.BZip2.Constants.SignatureBytes)) { extension = "bz2"; break; } else if (overlaySample.StartsWith(Data.Models.CFB.Constants.SignatureBytes)) { // Assume embedded CFB files are MSI extension = "msi"; break; } else if (overlaySample.StartsWith([0x1F, 0x8B])) { extension = "gz"; break; } else if (overlaySample.StartsWith(Data.Models.MicrosoftCabinet.Constants.SignatureBytes)) { extension = "cab"; break; } else if (overlaySample.StartsWith(Data.Models.PKZIP.Constants.LocalFileHeaderSignatureBytes)) { extension = "zip"; break; } else if (overlaySample.StartsWith(Data.Models.PKZIP.Constants.EndOfCentralDirectoryRecordSignatureBytes)) { extension = "zip"; break; } else if (overlaySample.StartsWith(Data.Models.PKZIP.Constants.EndOfCentralDirectoryRecord64SignatureBytes)) { extension = "zip"; break; } else if (overlaySample.StartsWith(Data.Models.PKZIP.Constants.DataDescriptorSignatureBytes)) { extension = "zip"; break; } else if (overlaySample.StartsWith(Data.Models.RAR.Constants.OldSignatureBytes)) { extension = "rar"; break; } else if (overlaySample.StartsWith(Data.Models.RAR.Constants.NewSignatureBytes)) { extension = "rar"; break; } else if (overlaySample.StartsWith([0x55, 0x48, 0x41, 0x06])) { extension = "uha"; break; } else if (overlaySample.StartsWith([0x3C, 0x3F, 0x78, 0x6D, 0x6C])) { extension = "xml"; break; } else if (overlaySample.StartsWith([0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00])) { extension = "xml"; break; } else if (overlaySample.StartsWith([0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00])) { extension = "xml"; break; } else if (overlaySample.StartsWith(Data.Models.XZ.Constants.HeaderSignatureBytes)) { extension = "xz"; break; } else if (overlaySample.StartsWith(Data.Models.MSDOS.Constants.SignatureBytes)) { extension = "bin"; // exe/dll break; } } // If the extension is unset if (extension.Length == 0) return false; // Create the temp filename string tempFile = $"embedded_overlay.{extension}"; if (Filename is not null) tempFile = $"{Path.GetFileName(Filename)}-{tempFile}"; tempFile = Path.Combine(outputDirectory, tempFile); var directoryName = Path.GetDirectoryName(tempFile); if (directoryName is not null && !Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName); // Write the resource data to a temp file using var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); // If the overlay is partially cached, read it from the source in blocks if (OverlaySize > overlayData.Length) { long currentOffset = OverlayAddress + overlayOffset; long bytesLeft = OverlaySize - overlayOffset; while (bytesLeft > 0) { int bytesToRead = (int)Math.Min(0x4000, bytesLeft); byte[] buffer = ReadRangeFromSource(currentOffset, bytesToRead); if (buffer.Length == 0) break; tempStream.Write(buffer, 0, buffer.Length); tempStream.Flush(); currentOffset += bytesToRead; bytesLeft -= bytesToRead; } } // Otherwise, read from the cached data else { tempStream.Write(overlayData, overlayOffset, overlayData.Length - overlayOffset); tempStream.Flush(); } return true; } catch (Exception ex) { if (includeDebug) Console.Error.WriteLine(ex); return false; } } /// /// Extract data from a Wise installer /// /// Output directory to write to /// True to include debug data, false otherwise /// True if extraction succeeded, false otherwise public bool ExtractWise(string outputDirectory, bool includeDebug) { // Get the source data for reading Stream source = _dataSource; if (Filename is not null) { // Try to open a multipart file if (WiseOverlayHeader.OpenFile(Filename, includeDebug, out var temp) && temp is not null) source = temp; } // Try to find the overlay header long offset = FindWiseOverlayHeader(); if (offset > 0 && offset < Length) return ExtractWiseOverlay(outputDirectory, includeDebug, source, offset); // Everything else could not extract return false; } /// /// Extract using Wise overlay /// /// Output directory to write to /// True to include debug data, false otherwise /// Potentially multi-part stream to read /// Offset to the start of the overlay header /// True if extraction succeeded, false otherwise private bool ExtractWiseOverlay(string outputDirectory, bool includeDebug, Stream source, long offset) { // Seek to the overlay and parse source.SeekIfPossible(offset, SeekOrigin.Begin); var header = WiseOverlayHeader.Create(source); if (header is null) { if (includeDebug) Console.Error.WriteLine("Could not parse the overlay header"); return false; } // Extract the header-defined files bool extracted = header.ExtractHeaderDefinedFiles(outputDirectory, includeDebug); if (!extracted) { if (includeDebug) Console.Error.WriteLine("Could not extract header-defined files"); return false; } // Open the script file from the output directory var scriptStream = File.OpenRead(Path.Combine(outputDirectory, "WiseScript.bin")); var script = WiseScript.Create(scriptStream); if (script is null) { if (includeDebug) Console.Error.WriteLine("Could not parse WiseScript.bin"); return false; } // Get the source directory string? sourceDirectory = null; if (Filename is not null) sourceDirectory = Path.GetDirectoryName(Path.GetFullPath(Filename)); // Process the state machine return script.ProcessStateMachine(header, sourceDirectory, outputDirectory, includeDebug); } } }