diff --git a/Steamless.API/Model/SteamlessOptions.cs b/Steamless.API/Model/SteamlessOptions.cs index 6c98c56..653bbb7 100644 --- a/Steamless.API/Model/SteamlessOptions.cs +++ b/Steamless.API/Model/SteamlessOptions.cs @@ -39,6 +39,7 @@ namespace Steamless.API.Model this.UseExperimentalFeatures = false; this.DontRealignSections = true; this.ZeroDosStubData = true; + this.RecalculateFileChecksum = false; } /// @@ -103,5 +104,14 @@ namespace Steamless.API.Model get => this.Get("ZeroDosStubData"); set => this.Set("ZeroDosStubData", value); } + + /// + /// Gets or sets if the file checksum should be recalculated. + /// + public bool RecalculateFileChecksum + { + get => this.Get("RecalculateFileChecksum"); + set => this.Set("RecalculateFileChecksum", value); + } } } \ No newline at end of file diff --git a/Steamless.API/PE32/Pe32Helpers.cs b/Steamless.API/PE32/Pe32Helpers.cs index 81ecd7d..8d33b5a 100644 --- a/Steamless.API/PE32/Pe32Helpers.cs +++ b/Steamless.API/PE32/Pe32Helpers.cs @@ -27,6 +27,7 @@ namespace Steamless.API.PE32 { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -89,6 +90,97 @@ namespace Steamless.API.PE32 return GetStructure(rawData, 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(NativeApi32.ImageDosHeader32), "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(NativeApi32.ImageFileHeader32)) + (uint)Marshal.OffsetOf(typeof(NativeApi32.ImageOptionalHeader32), "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.Variant10.x86/Main.cs b/Steamless.Unpacker.Variant10.x86/Main.cs index bbc84fe..f8a96bd 100644 --- a/Steamless.Unpacker.Variant10.x86/Main.cs +++ b/Steamless.Unpacker.Variant10.x86/Main.cs @@ -36,6 +36,7 @@ namespace Steamless.Unpacker.Variant10.x86 using System.IO; using System.Linq; using System.Reflection; + using System.Runtime.InteropServices; [SteamlessApiVersion(1, 0)] public class Main : SteamlessPlugin @@ -149,6 +150,13 @@ namespace Steamless.Unpacker.Variant10.x86 if (!this.Step3()) return false; + if (this.Options.RecalculateFileChecksum) + { + this.Log("Step 4 - Rebuild unpacked file checksum.", LogMessageType.Information); + if (!this.Step4()) + return false; + } + return true; } @@ -267,9 +275,10 @@ namespace Steamless.Unpacker.Variant10.x86 if (this.File.DosStubSize > 0) fStream.WriteBytes(this.File.DosStubData); - // Update the entry point of the file.. + // Update the entry point and checksum of the file.. var ntHeaders = this.File.NtHeaders; ntHeaders.OptionalHeader.AddressOfEntryPoint = this.OriginalEntryPoint; + ntHeaders.OptionalHeader.CheckSum = 0; this.File.NtHeaders = ntHeaders; // Write the NT headers to the file.. @@ -318,6 +327,26 @@ namespace Steamless.Unpacker.Variant10.x86 } } + /// + /// Step #4 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step4() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe32Helpers.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 the Steamless options this file was requested to process with. /// diff --git a/Steamless.Unpacker.Variant20.x86/Main.cs b/Steamless.Unpacker.Variant20.x86/Main.cs index 1abc507..382c24e 100644 --- a/Steamless.Unpacker.Variant20.x86/Main.cs +++ b/Steamless.Unpacker.Variant20.x86/Main.cs @@ -159,6 +159,13 @@ namespace Steamless.Unpacker.Variant20.x86 if (!this.Step4()) return false; + if (this.Options.RecalculateFileChecksum) + { + this.Log("Step 5 - Rebuild unpacked file checksum.", LogMessageType.Information); + if (!this.Step5()) + return false; + } + return true; } @@ -381,6 +388,26 @@ namespace Steamless.Unpacker.Variant20.x86 } } + /// + /// Step #5 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step5() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe32Helpers.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; + + } + /// /// Disassembles the file to locate the needed DRM header information. /// diff --git a/Steamless.Unpacker.Variant21.x86/Main.cs b/Steamless.Unpacker.Variant21.x86/Main.cs index 172568c..4f5e58d 100644 --- a/Steamless.Unpacker.Variant21.x86/Main.cs +++ b/Steamless.Unpacker.Variant21.x86/Main.cs @@ -169,6 +169,13 @@ namespace Steamless.Unpacker.Variant21.x86 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; } @@ -508,6 +515,26 @@ namespace Steamless.Unpacker.Variant21.x86 } } + /// + /// Step #7 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step7() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe32Helpers.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; + + } + /// /// Disassembles the file to locate the needed DRM header information. /// diff --git a/Steamless.Unpacker.Variant30.x86/Main.cs b/Steamless.Unpacker.Variant30.x86/Main.cs index 643184f..c93e26d 100644 --- a/Steamless.Unpacker.Variant30.x86/Main.cs +++ b/Steamless.Unpacker.Variant30.x86/Main.cs @@ -193,6 +193,13 @@ namespace Steamless.Unpacker.Variant30.x86 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; } @@ -511,6 +518,26 @@ namespace Steamless.Unpacker.Variant30.x86 } } + /// + /// Step #7 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step7() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe32Helpers.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.x86/Main.cs b/Steamless.Unpacker.Variant31.x86/Main.cs index 9fbf23d..980cc7f 100644 --- a/Steamless.Unpacker.Variant31.x86/Main.cs +++ b/Steamless.Unpacker.Variant31.x86/Main.cs @@ -192,6 +192,13 @@ namespace Steamless.Unpacker.Variant31.x86 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; } @@ -507,6 +514,26 @@ namespace Steamless.Unpacker.Variant31.x86 } } + /// + /// Step #7 + /// + /// Recalculate the file checksum. + /// + /// + private bool Step7() + { + var unpackedPath = this.File.FilePath + ".unpacked.exe"; + if (!Pe32Helpers.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/View/MainView.xaml b/Steamless/View/MainView.xaml index efcb3c9..e609215 100644 --- a/Steamless/View/MainView.xaml +++ b/Steamless/View/MainView.xaml @@ -86,6 +86,7 @@ + @@ -93,7 +94,8 @@ - + +