Support PKZIP-compressed Wise installer data

This commit is contained in:
Matt Nadareski
2023-01-04 22:37:44 -08:00
parent fdad3b0c87
commit 7d14eb35ba
2 changed files with 259 additions and 41 deletions

View File

@@ -413,6 +413,70 @@ namespace BurnOutSharp.Wrappers
}
}
/// <summary>
/// Address of the overlay, if it exists
/// </summary>
/// <see href="https://www.autoitscript.com/forum/topic/153277-pe-file-overlay-extraction/"/>
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;
}
}
}
/// <summary>
/// Overlay data, if it exists
/// </summary>
@@ -830,6 +894,11 @@ namespace BurnOutSharp.Wrappers
/// </summary>
private byte[] _entryPointData = null;
/// <summary>
/// Address of the overlay, if it exists
/// </summary>
private int? _overlayAddress = null;
/// <summary>
/// Overlay data, if it exists
/// </summary>

View File

@@ -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
/// <returns>True if it matches a known version, false otherwise</returns>
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
/// </summary>
/// <param name="pex">Portable executable to check</param>
/// <returns>True if it matches a known version, false otherwise</returns>
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;
}
/// <summary>
/// Class representing the properties of each recognized Wise installer format
/// </summary>
/// <see href="https://github.com/mnadareski/WiseUnpacker/blob/master/WiseUnpacker/FormatProperty.cs"/>
private class FormatProperty
{
/// <summary>
/// Offset to the executable data
/// </summary>
public int ExecutableOffset { get; set; }
/// <summary>
/// Indicates if this format includes a DLL at the start or not
/// </summary>
public bool Dll { get; set; }
/// <summary>
/// Offset within the data where the archive starts
/// </summary>
public int ArchiveStart { get; set; }
/// <summary>
/// Position in the archive head of the archive end
/// </summary>
public int ArchiveEnd { get; set; }
/// <summary>
/// Format includes initialization text
/// </summary>
public bool InitText { get; set; }
/// <summary>
/// Position of the filename within the data
/// </summary>
public int FilenamePosition { get; set; }
/// <summary>
/// Format does not include a CRC
/// </summary>
public bool NoCrc { get; set; }
}
}
}