From b97f14894509b9194b46656f95af82188e9dc3c3 Mon Sep 17 00:00:00 2001 From: atom0s Date: Fri, 25 Mar 2022 16:46:38 -0700 Subject: [PATCH] Unpacker: v30.x64 - Renamed header field `Unknown0003` to `HasTlsCallback`. Unpacker: v30.x64 - Add support for handling files packed with TlsCallback overrides. This feature is currently only supported in this variant for the time being. (Until other samples are provided that have a TlsCallback override for the other variants.) Notes on how this file type works can be found here: https://github.com/atom0s/Steamless/issues/20#issuecomment-1078821463 This fixes: #20 --- .../Classes/SteamStubHeader.cs | 2 +- Steamless.Unpacker.Variant30.x64/Main.cs | 61 ++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Steamless.Unpacker.Variant30.x64/Classes/SteamStubHeader.cs b/Steamless.Unpacker.Variant30.x64/Classes/SteamStubHeader.cs index 0a3751d..201933b 100644 --- a/Steamless.Unpacker.Variant30.x64/Classes/SteamStubHeader.cs +++ b/Steamless.Unpacker.Variant30.x64/Classes/SteamStubHeader.cs @@ -63,7 +63,7 @@ namespace Steamless.Unpacker.Variant30.x64.Classes [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x04)] public uint[] EncryptionKeys; // Encryption keys used for decrypting SteamDRMP.dll file. - public uint Unknown0003; // [Cyanic: This field is most likely used to flag if the file has Tls data or not.] + public uint HasTlsCallback; // Flag that states if the file was protected with a TlsCallback present. public uint Unknown0004; public uint Unknown0005; public uint Unknown0006; diff --git a/Steamless.Unpacker.Variant30.x64/Main.cs b/Steamless.Unpacker.Variant30.x64/Main.cs index 79a440a..d544050 100644 --- a/Steamless.Unpacker.Variant30.x64/Main.cs +++ b/Steamless.Unpacker.Variant30.x64/Main.cs @@ -35,6 +35,7 @@ namespace Steamless.Unpacker.Variant30.x64 using Classes; using System; using System.IO; + using System.Linq; using System.Reflection; using System.Security.Cryptography; @@ -189,6 +190,48 @@ namespace Steamless.Unpacker.Variant30.x64 return true; } + /// + /// Rebuilds the file TlsCallback information and repairs the proper OEP. + /// + /// + private bool RebuildTlsCallbackInformation() + { + // Ensure the modified main TlsCallback is within the .bind section.. + var section = this.File.GetOwnerSection(this.File.GetRvaFromVa(this.File.TlsCallbacks[0])); + if (!section.IsValid || string.Compare(section.SectionName, ".bind", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.CompareOptions.IgnoreCase) != 0) + return false; + + // Obtain the section that holds the Tls directory information.. + var addr = this.File.GetFileOffsetFromRva(this.File.GetRvaFromVa(this.File.TlsDirectory.AddressOfCallBacks)); + var tlsd = this.File.GetOwnerSection(addr); + + if (!tlsd.IsValid) + return false; + + addr -= tlsd.PointerToRawData; + + // Restore the true original TlsCallback address.. + var callback = BitConverter.GetBytes(this.File.NtHeaders.OptionalHeader.ImageBase + this.StubHeader.OriginalEntryPoint); + Array.Copy(callback, 0, this.File.GetSectionData(this.File.GetSectionIndex(tlsd)), (int)addr, callback.Length); + + // Find the original entry point function.. + var entry = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint); + var data = this.File.FileData.Skip((int)entry).Take(0x100).ToArray(); + + // Find the XOR key from within the function.. + var res = Pe64Helpers.FindPattern(data, "48 81 EA ?? ?? ?? ?? 8B 12 81 F2"); + if (res == 0) + return false; + + // Decrypt and recalculate the true OEP address.. + var key = (ulong)(this.StubHeader.XorKey ^ BitConverter.ToInt32(data, (int)res + 0x0B)); + var off = (ulong)((this.File.NtHeaders.OptionalHeader.ImageBase + this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint) + key); + + // Store the proper OEP.. + this.TlsOepOverride = (uint)(off - this.File.NtHeaders.OptionalHeader.ImageBase); + return true; + } + /// /// Step #1 /// @@ -234,7 +277,13 @@ namespace Steamless.Unpacker.Variant30.x64 // Tls was valid for the real oep.. this.TlsAsOep = true; this.TlsOepRva = this.File.GetRvaFromVa(this.File.TlsCallbacks[0]); - return true; + + // Is the TlsCallback replacing the OEP.. + if (this.StubHeader.HasTlsCallback != 1 || this.File.TlsCallbacks[0] == 0) + return true; + + // Rebuild the file Tls callback information.. + return this.RebuildTlsCallbackInformation(); } /// @@ -447,7 +496,10 @@ namespace Steamless.Unpacker.Variant30.x64 // Update the entry point of the file.. var ntHeaders = this.File.NtHeaders; - ntHeaders.OptionalHeader.AddressOfEntryPoint = this.StubHeader.OriginalEntryPoint; + if (this.StubHeader.HasTlsCallback != 1) + ntHeaders.OptionalHeader.AddressOfEntryPoint = this.StubHeader.OriginalEntryPoint; + else + ntHeaders.OptionalHeader.AddressOfEntryPoint = this.TlsOepOverride; this.File.NtHeaders = ntHeaders; // Write the NT headers to the file.. @@ -510,6 +562,11 @@ namespace Steamless.Unpacker.Variant30.x64 /// private ulong TlsOepRva { get; set; } + /// + /// Gets or sets the Tls Oep override value to use when the stub has set the HasTlsCallback flag. + /// + private uint TlsOepOverride { get; set; } + /// /// Gets or sets the Steamless options this file was requested to process with. ///