Implement unused packer extractions

This commit is contained in:
Matt Nadareski
2023-03-09 15:46:48 -05:00
parent 41d77085ae
commit cd2673d1ba
25 changed files with 279 additions and 29 deletions

View File

@@ -67,7 +67,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -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
{
/// <inheritdoc/>
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
@@ -31,6 +31,24 @@ namespace BurnOutSharp.PackerType
return null;
}
/// <inheritdoc/>
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);
}
}
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
return null;
}
/// <inheritdoc/>
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
{

View File

@@ -56,7 +56,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -50,7 +50,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -64,8 +64,75 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
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<byte>(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;
}
/// <inheritdoc/>

View File

@@ -91,7 +91,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -45,8 +45,38 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
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;
}
/// <inheritdoc/>

View File

@@ -49,7 +49,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -48,7 +48,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -74,7 +74,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -45,7 +45,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -47,7 +47,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -50,7 +50,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -65,7 +65,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -49,7 +49,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -58,7 +58,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -41,7 +41,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -55,7 +55,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -62,7 +62,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -42,7 +42,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -80,7 +80,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}

View File

@@ -47,8 +47,24 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
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;
}
/// <inheritdoc/>

View File

@@ -82,8 +82,24 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
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;
}
/// <inheritdoc/>

View File

@@ -94,7 +94,16 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
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;
}
/// <summary>
/// Attempt to extract Wise data from a New Executable
/// </summary>
/// <param name="nex">New executable to check</param>
/// <param name="file">Path to the input file</param>
/// <returns>True if it matches a known version, false otherwise</returns>
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;
}
/// <summary>
/// Attempt to extract Wise data from a Portable Executable
/// </summary>
@@ -431,6 +458,101 @@ namespace BurnOutSharp.PackerType
return null;
}
/// <summary>
/// Attempt to extract Wise data from a Portable Executable
/// </summary>
/// <param name="pex">Portable executable to check</param>
/// <param name="file">Path to the input file</param>
/// <returns>True if it matches a known version, false otherwise</returns>
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;
}
/// <summary>
/// Class representing the properties of each recognized Wise installer format
/// </summary>

View File

@@ -45,7 +45,6 @@ namespace BurnOutSharp.PackerType
/// <inheritdoc/>
public string Extract(Stream stream, string file)
{
// Create extraction based off Scan
return null;
}