diff --git a/BurnOutSharp.Wrappers/PortableExecutable.cs b/BurnOutSharp.Wrappers/PortableExecutable.cs
index 861e937a..81578207 100644
--- a/BurnOutSharp.Wrappers/PortableExecutable.cs
+++ b/BurnOutSharp.Wrappers/PortableExecutable.cs
@@ -413,6 +413,70 @@ namespace BurnOutSharp.Wrappers
}
}
+ ///
+ /// Address of the overlay, if it exists
+ ///
+ ///
+ public int OverlayAddress
+ {
+ get
+ {
+ lock (_sourceDataLock)
+ {
+ // Use the cached data if possible
+ if (_overlayAddress != null)
+ return _overlayAddress.Value;
+
+ // Get the end of the file, if possible
+ int endOfFile = GetEndOfFile();
+ if (endOfFile == -1)
+ return -1;
+
+ // If we have certificate data, use that as the end
+ if (OH_CertificateTable != null)
+ {
+ var certificateTable = _executable.OptionalHeader.CertificateTable;
+ int certificateTableAddress = (int)certificateTable.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable);
+ if (certificateTableAddress != 0 && certificateTableAddress < endOfFile)
+ endOfFile = certificateTableAddress;
+ }
+
+ // Search through all sections and find the furthest a section goes
+ int endOfSectionData = -1;
+ foreach (var section in _executable.SectionTable)
+ {
+ // If we have an invalid section address
+ int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(_executable.SectionTable);
+ if (sectionAddress == 0)
+ continue;
+
+ // If we have an invalid section size
+ if (section.SizeOfRawData == 0 && section.VirtualSize == 0)
+ continue;
+
+ // Get the real section size
+ int sectionSize;
+ if (section.SizeOfRawData < section.VirtualSize)
+ sectionSize = (int)section.VirtualSize;
+ else
+ sectionSize = (int)section.SizeOfRawData;
+
+ // Compare and set the end of section data
+ if (sectionAddress + sectionSize > endOfSectionData)
+ endOfSectionData = sectionAddress + sectionSize;
+ }
+
+ // If we didn't find the end of section data
+ if (endOfSectionData <= 0)
+ endOfSectionData = -1;
+
+ // Cache and return the position
+ _overlayAddress = endOfSectionData;
+ return _overlayAddress.Value;
+ }
+ }
+ }
+
///
/// Overlay data, if it exists
///
@@ -830,6 +894,11 @@ namespace BurnOutSharp.Wrappers
///
private byte[] _entryPointData = null;
+ ///
+ /// Address of the overlay, if it exists
+ ///
+ private int? _overlayAddress = null;
+
///
/// Overlay data, if it exists
///
diff --git a/BurnOutSharp/PackerType/WiseInstaller.cs b/BurnOutSharp/PackerType/WiseInstaller.cs
index b473e689..bd9c2a0f 100644
--- a/BurnOutSharp/PackerType/WiseInstaller.cs
+++ b/BurnOutSharp/PackerType/WiseInstaller.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using BurnOutSharp.Interfaces;
using BurnOutSharp.Matching;
+using BurnOutSharp.Utilities;
using BurnOutSharp.Wrappers;
using static BurnOutSharp.Utilities.Dictionary;
using Wise = WiseUnpacker.WiseUnpacker;
@@ -50,7 +51,7 @@ namespace BurnOutSharp.PackerType
return null;
// If we match a known header
- if (MatchesPEVersion(pex))
+ if (GetPEFormat(pex) != null)
return "Wise Installation Wizard Module";
// TODO: Investigate STUB32.EXE in export directory table
@@ -95,26 +96,135 @@ namespace BurnOutSharp.PackerType
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempPath);
- Wise unpacker = new Wise();
- unpacker.ExtractTo(file, tempPath);
+ // Parse into an executable again for easier extraction
+ PortableExecutable pex = PortableExecutable.Create(stream);
+ if (pex == null)
+ return null;
- // Collect and format all found protections
- var protections = scanner.GetProtections(tempPath);
+ // Get the matching PE format
+ var format = GetPEFormat(pex);
+ if (format == null)
+ return null;
- // If temp directory cleanup fails
- try
+ // 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)
{
- Directory.Delete(tempPath, true);
- }
- catch (Exception ex)
- {
- if (scanner.IncludeDebug) Console.WriteLine(ex);
+ // 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;
+ }
}
- // Remove temporary path references
- StripFromKeys(protections, tempPath);
+ // Check if flags are consistent
+ if (!format.NoCrc)
+ {
+ // Unlike WiseUnpacker, we ignore the flag value here
+ _ = overlayData.ReadUInt32(ref overlayOffset);
+ }
- return protections;
+ // 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' });
+
+ // If we have PKZIP
+ if (pkzip)
+ {
+ try
+ {
+ 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);
+ }
+
+ // Collect and format all found protections
+ var protections = scanner.GetProtections(tempPath);
+
+ // If temp directory cleanup fails
+ try
+ {
+ Directory.Delete(tempPath, true);
+ }
+ catch (Exception ex)
+ {
+ if (scanner.IncludeDebug) Console.WriteLine(ex);
+ }
+
+ // Remove temporary path references
+ StripFromKeys(protections, tempPath);
+
+ return protections;
+ }
+ catch (Exception ex)
+ {
+ if (scanner.IncludeDebug) Console.WriteLine(ex);
+ return null;
+ }
+ }
+
+ // If we have DEFLATE -- TODO: Port implementation here or use DeflateStream
+ else
+ {
+ Wise unpacker = new Wise();
+ unpacker.ExtractTo(file, tempPath);
+
+ // Collect and format all found protections
+ var protections = scanner.GetProtections(tempPath);
+
+ // If temp directory cleanup fails
+ try
+ {
+ Directory.Delete(tempPath, true);
+ }
+ catch (Exception ex)
+ {
+ if (scanner.IncludeDebug) Console.WriteLine(ex);
+ }
+
+ // Remove temporary path references
+ StripFromKeys(protections, tempPath);
+
+ return protections;
+ }
}
catch (Exception ex)
{
@@ -131,6 +241,7 @@ namespace BurnOutSharp.PackerType
/// True if it matches a known version, false otherwise
private bool MatchesNEVersion(NewExecutable nex)
{
+ // TODO: Offset is _not_ the EXE header address, rather where the data starts. Fix this.
switch (nex.Stub_NewExeHeaderAddr)
{
// Dll = false, ArchiveStart = 0x11, ArchiveEnd = -1, InitText = false, FilenamePosition = 0x04, NoCrc = true
@@ -184,48 +295,86 @@ namespace BurnOutSharp.PackerType
///
/// Portable executable to check
/// True if it matches a known version, false otherwise
- private bool MatchesPEVersion(PortableExecutable pex)
+ private FormatProperty GetPEFormat(PortableExecutable pex)
{
- // Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- if (pex.Stub_NewExeHeaderAddr == 0x6e00
+ if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1528)
- return true;
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x6e00
+ else if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3cf4
&& pex.GetFirstSection(".data")?.VirtualSize == 0x1568)
- return true;
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x6e00
+ else if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d54)
- return true;
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x6e00
+ else if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d44)
- return true;
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x6e00
+ else if (pex.OverlayAddress == 0x6e00
&& pex.GetFirstSection(".text")?.VirtualSize == 0x3d04)
- return true;
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x3000)
- return true;
+ // Found in Binary.WiseCustomCalla
+ else if (pex.OverlayAddress == 0x6200)
+ return new FormatProperty { Dll = true, ArchiveStart = 0x62, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x3800)
- return true;
+ else if (pex.OverlayAddress == 0x3000)
+ return new FormatProperty { Dll = false, ArchiveStart = 0x50, ArchiveEnd = 0x4c, InitText = false, FilenamePosition = 0x1c, NoCrc = false };
- // Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false
- else if (pex.Stub_NewExeHeaderAddr == 0x3a00)
- return true;
+ else if (pex.OverlayAddress == 0x3800)
+ return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
- return false;
+ else if (pex.OverlayAddress == 0x3a00)
+ return new FormatProperty { Dll = true, ArchiveStart = 0x5a, ArchiveEnd = 0x4c, InitText = true, FilenamePosition = 0x1c, NoCrc = false };
+
+ return null;
+ }
+
+ ///
+ /// Class representing the properties of each recognized Wise installer format
+ ///
+ ///
+ private class FormatProperty
+ {
+ ///
+ /// Offset to the executable data
+ ///
+ public int ExecutableOffset { get; set; }
+
+ ///
+ /// Indicates if this format includes a DLL at the start or not
+ ///
+ public bool Dll { get; set; }
+
+ ///
+ /// Offset within the data where the archive starts
+ ///
+ public int ArchiveStart { get; set; }
+
+ ///
+ /// Position in the archive head of the archive end
+ ///
+ public int ArchiveEnd { get; set; }
+
+ ///
+ /// Format includes initialization text
+ ///
+ public bool InitText { get; set; }
+
+ ///
+ /// Position of the filename within the data
+ ///
+ public int FilenamePosition { get; set; }
+
+ ///
+ /// Format does not include a CRC
+ ///
+ public bool NoCrc { get; set; }
}
}
}