mirror of
https://github.com/atom0s/Steamless.git
synced 2024-12-19 23:07:41 +01:00
Unpacker: v10.x86 - Finish implementing SteamStub variant v1.0 (x86) unpacker. (Fixes #22)
This unpacker is currently considered beta. Since I only have 1 sample that uses this variant at the moment, it is not possible to confirm the data and signatures being used are consistent across the board. This variant is also very basic and does not seem to include any means of real protection. (ie. encrypting the code section.) I'd assume this version also did not support x64 files or TLS callbacks so most of the work the other variants can do is left out until other samples prove this needs to support those kinds of features.
This commit is contained in:
parent
39ad5a2631
commit
18c389ce3c
3 changed files with 310 additions and 1 deletions
109
Steamless.Unpacker.Variant10.x86/Classes/SteamStubHeader.cs
Normal file
109
Steamless.Unpacker.Variant10.x86/Classes/SteamStubHeader.cs
Normal file
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// SteamStub DRM Variant 1.0 Header
|
||||
/// </summary>
|
||||
[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
|
||||
}
|
||||
}
|
|
@ -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
|
|||
/// <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;
|
||||
|
||||
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
|
||||
{
|
||||
// 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; }
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Classes\SteamStubHeader.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
Loading…
Reference in a new issue