diff --git a/Steamless.API/PE64/Pe64Helpers.cs b/Steamless.API/PE64/Pe64Helpers.cs index ee98c7e..d3e8289 100644 --- a/Steamless.API/PE64/Pe64Helpers.cs +++ b/Steamless.API/PE64/Pe64Helpers.cs @@ -27,6 +27,7 @@ namespace Steamless.API.PE64 { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -89,6 +90,97 @@ namespace Steamless.API.PE64 return GetStructure(rawData, (int)dataOffset + (index * sectionSize)); } + /// + /// Calculates the PE checksum of the opened file. (OptionalHeader.Checksum should be 0 before calling this!) + /// + /// + /// + private static byte[] CalculateChecksum(FileStream fStream) + { + fStream.Position = 0; + + var dataSize = fStream.Length; + var totalWords = dataSize / 2; + var sumTotal = 0; + + // Process the file data in uint16_t chunks.. + while (totalWords > 0) + { + var sumChunk = 0; + var chunkWords = totalWords; + + // Prepare next chunk size.. + if (chunkWords > UInt16.MaxValue) + chunkWords = UInt16.MaxValue; + + totalWords -= chunkWords; + + do + { + var data = new byte[2]; + fStream.Read(data, 0, 2); + sumChunk += BitConverter.ToUInt16(data, 0); + } while (--chunkWords != 0); + + sumTotal += (sumChunk >> 16) + (sumChunk & 0xFFFF); + } + + if ((dataSize % 2) != 0) + sumTotal += fStream.ReadByte(); + + var checksum = (uint)(((sumTotal >> 16) + (sumTotal & 0xFFFF)) + dataSize); + + return BitConverter.GetBytes(checksum); + } + + /// + /// Updates the given files PE checksum value. (Path is assumed to be a 32bit PE file.) + /// + /// + /// + public static bool UpdateFileChecksum(string path) + { + FileStream fStream = null; + var data = new byte[4]; + + try + { + // Open the file for reading/writing.. + fStream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); + + // Read the starting offset to the files NT headers.. + fStream.Position = (int)Marshal.OffsetOf(typeof(NativeApi64.ImageDosHeader64), "e_lfanew"); + fStream.Read(data, 0, 4); + + var offset = BitConverter.ToUInt32(data, 0); + + // Move to the files CheckSum position.. + offset += 4 + (uint)Marshal.SizeOf(typeof(NativeApi64.ImageFileHeader64)) + (uint)Marshal.OffsetOf(typeof(NativeApi64.ImageOptionalHeader64), "CheckSum").ToInt32(); + fStream.Position = offset; + + // Ensure the checksum is 0 to start.. + data = new byte[4] { 0, 0, 0, 0 }; + fStream.Write(data, 0, 4); + + // Calculate the new checksum.. + var checksum = CalculateChecksum(fStream); + + // Update the checksum value.. + fStream.Position = offset; + fStream.Write(checksum, 0, 4); + + return true; + } + catch + { + return false; + } + finally + { + fStream?.Dispose(); + } + } + /// /// Scans the given data for the given pattern. /// diff --git a/Steamless.Unpacker.Variant30.x64/Main.cs b/Steamless.Unpacker.Variant30.x64/Main.cs index 6d96bd5..e52c858 100644 --- a/Steamless.Unpacker.Variant30.x64/Main.cs +++ b/Steamless.Unpacker.Variant30.x64/Main.cs @@ -189,6 +189,13 @@ namespace Steamless.Unpacker.Variant30.x64 if (!this.Step6()) return false; + if (this.Options.RecalculateFileChecksum) + { + this.Log("Step 7 - Rebuild unpacked file checksum.", LogMessageType.Information); + if (!this.Step7()) + return false; + } + return true; } @@ -500,12 +507,13 @@ namespace Steamless.Unpacker.Variant30.x64 if (this.File.DosStubSize > 0) fStream.WriteBytes(this.File.DosStubData); - // Update the entry point of the file.. + // Update the NT headers.. var ntHeaders = this.File.NtHeaders; if (this.StubHeader.HasTlsCallback != 1) ntHeaders.OptionalHeader.AddressOfEntryPoint = this.StubHeader.OriginalEntryPoint; else ntHeaders.OptionalHeader.AddressOfEntryPoint = this.TlsOepOverride; + ntHeaders.OptionalHeader.CheckSum = 0; this.File.NtHeaders = ntHeaders; // Write the NT headers to the file.. @@ -558,6 +566,26 @@ namespace Steamless.Unpacker.Variant30.x64 } } + /// + /// Step #7 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step7() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe64Helpers.UpdateFileChecksum(unpackedPath)) + { + this.Log(" --> Error trying to recalculate unpacked file checksum!", LogMessageType.Error); + return false; + } + + this.Log(" --> Unpacked file updated with new checksum!", LogMessageType.Success); + return true; + + } + /// /// Gets or sets if the Tls callback is being used as the Oep. /// diff --git a/Steamless.Unpacker.Variant31.x64/Main.cs b/Steamless.Unpacker.Variant31.x64/Main.cs index bd08f96..da09cb9 100644 --- a/Steamless.Unpacker.Variant31.x64/Main.cs +++ b/Steamless.Unpacker.Variant31.x64/Main.cs @@ -186,6 +186,13 @@ namespace Steamless.Unpacker.Variant31.x64 if (!this.Step6()) return false; + if (this.Options.RecalculateFileChecksum) + { + this.Log("Step 7 - Rebuild unpacked file checksum.", LogMessageType.Information); + if (!this.Step7()) + return false; + } + return true; } @@ -452,9 +459,10 @@ namespace Steamless.Unpacker.Variant31.x64 if (this.File.DosStubSize > 0) fStream.WriteBytes(this.File.DosStubData); - // Update the entry point of the file.. + // Update the NT headers.. var ntHeaders = this.File.NtHeaders; ntHeaders.OptionalHeader.AddressOfEntryPoint = (uint)this.StubHeader.OriginalEntryPoint; + ntHeaders.OptionalHeader.CheckSum = 0; this.File.NtHeaders = ntHeaders; // Write the NT headers to the file.. @@ -507,6 +515,26 @@ namespace Steamless.Unpacker.Variant31.x64 } } + /// + /// Step #7 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step7() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe64Helpers.UpdateFileChecksum(unpackedPath)) + { + this.Log(" --> Error trying to recalculate unpacked file checksum!", LogMessageType.Error); + return false; + } + + this.Log(" --> Unpacked file updated with new checksum!", LogMessageType.Success); + return true; + + } + /// /// Gets or sets if the Tls callback is being used as the Oep. ///