diff --git a/Steamless.Unpacker.Variant10.x86/Classes/SteamStubHeader.cs b/Steamless.Unpacker.Variant10.x86/Classes/SteamStubHeader.cs
new file mode 100644
index 0000000..f3db21d
--- /dev/null
+++ b/Steamless.Unpacker.Variant10.x86/Classes/SteamStubHeader.cs
@@ -0,0 +1,109 @@
+/**
+ * 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.Classes
+{
+ using System.Runtime.InteropServices;
+
+ ///
+ /// SteamStub DRM Variant 1.0 Header
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SteamStub32Var10Header
+ {
+ public uint GetModuleHandleA_idata; // The address of GetModuleHandleA inside of the .idata section.
+ public uint GetProcAddress_idata; // The address of GetProcAddress inside of the .idata section.
+ public uint BindFunction; // The .bind unpacker function address.
+ public uint BindCodeSize; // The .bind unpacker function size.
+ public uint Checksum; // The checksum of the header data after its initialized. (This is done via addition chunking.)
+ public uint AppId;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x08)]
+ public byte[] SteamAppIDString; // The SteamAppID of the packed file, in string format.
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_kernel32dll; // String: kernel32.dll
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_user32dll; // String: user32.dll
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_shell32dll; // String: shell32.dll
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_loadlibraryexa; // String: LoadLibraryExA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_freelibrary; // String: FreeLibrary
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_messageboxa; // String: MessageBoxA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x18)]
+ public byte[] str_getmodulefilenamea; // String: GetModuleFileNameA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_lstrlena; // String: lstrlenA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_lstrcata; // String lstrcatA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_exitprocess; // String: ExitProcess
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_shellexecutea; // String: ShellExecuteA
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_steamerror; // String: Steam Error
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_steamdll; // String: Steam.dll
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x18)]
+ public byte[] str_steamisappsubscribed; // String: SteamIsAppSubscribed
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_steamstartup; // String: SteamStartup
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
+ public byte[] str_steamcleanup; // String: SteamCleanup
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
+ public byte[] str_failedtofindsteam; // String: Failed to find Steam
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
+ public byte[] str_failedtoloadsteam; // String: Failed to load Steam
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x18)]
+ public byte[] str_steamstoreurl; // String: steam://store/
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x18)]
+ public byte[] str_steamrunurl; // String: steam://run/
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x08)]
+ public byte[] str_open; // String: open
+ }
+}
\ No newline at end of file
diff --git a/Steamless.Unpacker.Variant10.x86/Main.cs b/Steamless.Unpacker.Variant10.x86/Main.cs
index a793abe..4874226 100644
--- a/Steamless.Unpacker.Variant10.x86/Main.cs
+++ b/Steamless.Unpacker.Variant10.x86/Main.cs
@@ -27,10 +27,13 @@ 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.Reflection;
[SteamlessApiVersion(1, 0)]
@@ -121,17 +124,213 @@ namespace Steamless.Unpacker.Variant10.x86
///
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;
- 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;
}
+ ///
+ /// Step #1
+ ///
+ /// Read, decode and validate the SteamStub DRM header.
+ ///
+ ///
+ 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(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;
+ }
+
+ ///
+ /// Step #2
+ ///
+ /// Remove the bind section if requested.
+ /// Find the code section.
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Step #3
+ ///
+ /// Rebuild and save the unpacked file.
+ ///
+ ///
+ private bool Step3()
+ {
+ FileStream fStream = null;
+
+ try
+ {
+ // 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();
+ }
+ }
+
+ ///
+ /// Gets or sets the Steamless options this file was requested to process with.
+ ///
+ private SteamlessOptions Options { get; set; }
+
///
/// Gets or sets the file being processed.
///
private Pe32File File { get; set; }
+
+ ///
+ /// Gets or sets the DRM stub header.
+ ///
+ private SteamStub32Var10Header StubHeader { get; set; }
+
+ ///
+ /// Gets or sets the true entry point take from the bind unpacker function.
+ ///
+ private uint OriginalEntryPoint { get; set; }
}
}
\ No newline at end of file
diff --git a/Steamless.Unpacker.Variant10.x86/Steamless.Unpacker.Variant10.x86.csproj b/Steamless.Unpacker.Variant10.x86/Steamless.Unpacker.Variant10.x86.csproj
index 0a4f339..6834d2b 100644
--- a/Steamless.Unpacker.Variant10.x86/Steamless.Unpacker.Variant10.x86.csproj
+++ b/Steamless.Unpacker.Variant10.x86/Steamless.Unpacker.Variant10.x86.csproj
@@ -42,6 +42,7 @@
+