1
0
Fork 0
mirror of https://github.com/atom0s/Steamless.git synced 2024-12-19 23:07:41 +01:00
Steamless/Steamless.Unpacker.Variant10.x86/Main.cs
atom0s 12c312b4db
API: Add new option to zero the DOS stub data when unpacking.
Core: Add UI option to enable/disable the new zero DOS stub data option.
Unpackers: Add support for new zero DOS stub data option.
API: `DontRealignSections` and `ZeroDosStubData` are now default enabled as this is the general 'correct' way to handle most files. (Some files do require the section alignment to happen and some files may use the DOS stub for self-validation and such. Adjust accordingly when using Steamless.)
2022-03-25 20:09:21 -07:00

341 lines
No EOL
12 KiB
C#

/**
* Steamless - Copyright (c) 2015 - 2022 atom0s [atom0s@live.com]
*
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to
* Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
*
* By using Steamless, you agree to the above license and its terms.
*
* Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were
* made. You must do so in any reasonable manner, but not in any way that suggests the licensor
* endorses you or your use.
*
* Non-Commercial - You may not use the material (Steamless) for commercial purposes.
*
* No-Derivatives - If you remix, transform, or build upon the material (Steamless), you may not distribute the
* modified material. You are, however, allowed to submit the modified works back to the original
* Steamless project in attempt to have it added to the original project.
*
* You may not apply legal terms or technological measures that legally restrict others
* from doing anything the license permits.
*
* No warranties are given.
*/
namespace Steamless.Unpacker.Variant10.x86
{
using API;
using API.Events;
using API.Extensions;
using API.Model;
using API.PE32;
using API.Services;
using Classes;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
[SteamlessApiVersion(1, 0)]
public class Main : SteamlessPlugin
{
/// <summary>
/// Internal logging service instance.
/// </summary>
private LoggingService m_LoggingService;
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "atom0s";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "SteamStub Variant 1.0 Unpacker (x86)";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "Unpacker for the 32bit SteamStub variant 1.0.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => Assembly.GetExecutingAssembly().GetName().Version;
/// <summary>
/// Internal wrapper to log a message.
/// </summary>
/// <param name="msg"></param>
/// <param name="type"></param>
private void Log(string msg, LogMessageType type)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs(msg, type));
}
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
this.m_LoggingService = logService;
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
try
{
// Load the file..
var f = new Pe32File(file);
if (!f.Parse() || f.IsFile64Bit() || !f.HasSection(".bind"))
return false;
// Obtain the bind section data..
var bind = f.GetSectionData(".bind");
// Attempt to locate the known v1.x signature..
var variant = Pe32Helpers.FindPattern(bind, "60 81 EC 00 10 00 00 BE ?? ?? ?? ?? B9 6A");
if (variant == -1)
return false;
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
// Initialize the class members..
this.Options = options;
this.OriginalEntryPoint = 0;
// Parse the file..
this.File = new Pe32File(file);
if (!this.File.Parse())
return false;
// Announce we are being unpacked with this packer..
this.Log("File is packed with SteamStub Variant 1.0!", LogMessageType.Information);
this.Log("Step 1 - Read, decode and validate the SteamStub DRM header.", LogMessageType.Information);
if (!this.Step1())
return false;
this.Log("Step 2 - Handle .bind section.", LogMessageType.Information);
if (!this.Step2())
return false;
this.Log("Step 3 - Rebuild and save the unpacked file.", LogMessageType.Information);
if (!this.Step3())
return false;
return true;
}
/// <summary>
/// Step #1
///
/// Read, decode and validate the SteamStub DRM header.
/// </summary>
/// <returns></returns>
private bool Step1()
{
// Obtain the bind section..
var section = this.File.GetSection(".bind");
if (!section.IsValid)
return false;
// Find the header information from the unpacker call..
var bind = this.File.GetSectionData(".bind");
var offset = Pe32Helpers.FindPattern(bind, "60 81 EC 00 10 00 00 BE ?? ?? ?? ?? B9 6A");
if (offset == -1)
return false;
// Read the needed header information..
var headerPointer = BitConverter.ToUInt32(bind, (int)offset + 8);
var headerSize = BitConverter.ToUInt32(bind, (int)offset + 13) * 4;
// Calculate the file offset from the pointer..
var fileOffset = this.File.GetFileOffsetFromRva(headerPointer - this.File.NtHeaders.OptionalHeader.ImageBase);
// Read the header data..
var headerData = new byte[headerSize];
Array.Copy(this.File.FileData, fileOffset, headerData, 0, headerSize);
// Decrypt the header data..
for (var x = 0; x < headerSize; x++)
headerData[x] ^= (byte)(x * x);
// Store the header and validate it..
this.StubHeader = Pe32Helpers.GetStructure<SteamStub32Var10Header>(headerData);
// Validate the header via the unpacker function matching the file entry point..
if (this.StubHeader.BindFunction - this.File.NtHeaders.OptionalHeader.ImageBase != this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint)
return false;
// Find the OEP from the unpacker function..
offset = Pe32Helpers.FindPattern(bind, "61 B8 ?? ?? ?? ?? FF E0");
if (offset == -1)
return false;
// Read and store the real OEP..
this.OriginalEntryPoint = BitConverter.ToUInt32(bind, (int)offset + 2) - this.File.NtHeaders.OptionalHeader.ImageBase;
return true;
}
/// <summary>
/// Step #2
///
/// Remove the bind section if requested.
/// Find the code section.
/// </summary>
/// <returns></returns>
private bool Step2()
{
// Remove the bind section if its not requested to be saved..
if (!this.Options.KeepBindSection)
{
// Obtain the .bind section..
var bindSection = this.File.GetSection(".bind");
if (!bindSection.IsValid)
return false;
// Remove the section..
this.File.RemoveSection(bindSection);
// Decrease the header section count..
var ntHeaders = this.File.NtHeaders;
ntHeaders.FileHeader.NumberOfSections--;
this.File.NtHeaders = ntHeaders;
this.Log(" --> .bind section was removed from the file.", LogMessageType.Debug);
}
else
this.Log(" --> .bind section was kept in the file.", LogMessageType.Debug);
return true;
}
/// <summary>
/// Step #3
///
/// Rebuild and save the unpacked file.
/// </summary>
/// <returns></returns>
private bool Step3()
{
FileStream fStream = null;
try
{
// Zero the DosStubData if desired..
if (this.Options.ZeroDosStubData && this.File.DosStubSize > 0)
this.File.DosStubData = Enumerable.Repeat((byte)0, (int)this.File.DosStubSize).ToArray();
// Rebuild the file sections..
this.File.RebuildSections(this.Options.DontRealignSections == false);
// Open the unpacked file for writing..
var unpackedPath = this.File.FilePath + ".unpacked.exe";
fStream = new FileStream(unpackedPath, FileMode.Create, FileAccess.ReadWrite);
// Write the DOS header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(this.File.DosHeader));
// Write the DOS stub to the file..
if (this.File.DosStubSize > 0)
fStream.WriteBytes(this.File.DosStubData);
// Update the entry point of the file..
var ntHeaders = this.File.NtHeaders;
ntHeaders.OptionalHeader.AddressOfEntryPoint = this.OriginalEntryPoint;
this.File.NtHeaders = ntHeaders;
// Write the NT headers to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(ntHeaders));
// Write the sections to the file..
for (var x = 0; x < this.File.Sections.Count; x++)
{
var section = this.File.Sections[x];
var sectionData = this.File.SectionData[x];
// Write the section header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(section));
// Set the file pointer to the sections raw data..
var sectionOffset = fStream.Position;
fStream.Position = section.PointerToRawData;
// Write the sections raw data..
fStream.WriteBytes(sectionData);
// Reset the file offset..
fStream.Position = sectionOffset;
}
// Set the stream to the end of the file..
fStream.Position = fStream.Length;
// Write the overlay data if it exists..
if (this.File.OverlayData != null)
fStream.WriteBytes(this.File.OverlayData);
this.Log(" --> Unpacked file saved to disk!", LogMessageType.Success);
this.Log($" --> File Saved As: {unpackedPath}", LogMessageType.Success);
return true;
}
catch
{
this.Log(" --> Error trying to save unpacked file!", LogMessageType.Error);
return false;
}
finally
{
fStream?.Dispose();
}
}
/// <summary>
/// Gets or sets the Steamless options this file was requested to process with.
/// </summary>
private SteamlessOptions Options { get; set; }
/// <summary>
/// Gets or sets the file being processed.
/// </summary>
private Pe32File File { get; set; }
/// <summary>
/// Gets or sets the DRM stub header.
/// </summary>
private SteamStub32Var10Header StubHeader { get; set; }
/// <summary>
/// Gets or sets the true entry point take from the bind unpacker function.
/// </summary>
private uint OriginalEntryPoint { get; set; }
}
}