public static void PatchFunction(string path) { if (IsFileLocked(path)) { Form1.SetStatus("Error: Executable is locked. Can't patch.", System.Drawing.Color.Red); Form1.SetProgress(100, System.Drawing.Color.Red); return; } CheckBackup(path); byte[] exe = File.ReadAllBytes(path); var patterns = new Pattern.Byte[][] { Pattern.Transform("9A 99 05 42"), Pattern.Transform("9A 99 85 41") }; bool isPatched = true; foreach (Pattern.Byte[] pb in patterns) { if (!Pattern.Find(exe, pb, out long offsetFound)) { Form1.SetStatus("Error: FPS Limit could not be found. Already patched or unsupported version.", System.Drawing.Color.Red); Form1.SetProgress(100, System.Drawing.Color.Red); return; } if (!PatchFile(exe, offsetFound, path)) { Form1.SetStatus("Error: Executable file could not be patched.", System.Drawing.Color.Red); isPatched = false; break; } } if (isPatched) { Form1.SetStatus("Game patched!", System.Drawing.Color.Green); } }
private bool InitInlinedKeys(CryptoKey[] keys, MemoryStream ms) { // Key1 find routine var patKey1 = new Pattern.Byte[][] { Pattern.Transform("B8 ?? ?? 00 00 7E"), Pattern.Transform("BA ?? ?? 00 00 85"), }; long key1Offset = -1; for (int keyIndex = 0; keyIndex < patKey1.Length; keyIndex++) { if (Pattern.Find(_data, patKey1[keyIndex], out key1Offset, ms.Position, ms.Position + 500)) { ms.Position = key1Offset + 1; break; } } if (key1Offset == -1) { Console.WriteLine($"[TargetPE::InitInlinedKeys]: Unable to find key1."); return(false); } DWORD dword = ms.ReadStructure <DWORD>(); keys[0] = new CryptoKey((ms.Position - 4), (ushort)dword.sValue1, (long)Calculator.OffsetToVA((ulong)(ms.Position - 4))); // Key2 find routine // TODO(Gilad): Key got inlined by multiple asm instructions...Will need to implement algorithm to // calculate from instructions region of what's the key value (TryGettingInlinedKey2Value()). keys[1] = new CryptoKey(-1, 0xFFFF, -1); // Key3 find routine var patKey2 = new Pattern.Byte[][] { Pattern.Transform("8D 94 02 ?? ?? 00 00 7C"), Pattern.Transform("B8 ?? ?? 00 00 8D"), Pattern.Transform("B8 ?? ?? 00 00 2B"), }; long key2Offset = -1; for (int keyIndex = 0; keyIndex < patKey2.Length; keyIndex++) { if (Pattern.Find(_data, patKey2[keyIndex], out key2Offset, ms.Position, ms.Position + 700)) { ms.Position = key2Offset + (keyIndex == 0 ? 3 : 1); break; } } if (key2Offset == -1) { Console.WriteLine($"[TargetPE::InitInlinedKeys]: Unable to find key2."); return(false); } dword = ms.ReadStructure <DWORD>(); keys[2] = new CryptoKey((ms.Position - 4), (ushort)dword.sValue1, (long)Calculator.OffsetToVA((ulong)(ms.Position - 4))); return(true); }
private bool InitKeysOffsets() { CryptoPatches.Clear(); // "N3TableBase - Can't open file(read) File Handle..." var pattern = Pattern.Transform("4E 33 54 61 62 6C 65 42 61 73 65 20 2D 20 43"); // "N3TableBase - C" long offsetLogWrite; if (!Pattern.Find(_data, pattern, out offsetLogWrite)) { Console.WriteLine("[TargetPE::InitKeysOffsets]: Unable to find offset."); return(false); } // Generate pattern for finding all references to that string string logWriteVA = Calculator.OffsetToVA((ulong)offsetLogWrite).ToString("X8"); string logWriteGenPattern = "68"; // push for (int i = logWriteVA.Length - 1; i >= 0; --i) { logWriteGenPattern += i % 2 == 0 ? " " + logWriteVA.Substring(i, 2) : ""; } logWriteGenPattern += " E8"; // call Console.WriteLine($"[TargetPE::InitKeysOffsets]: Generated pattern result: {logWriteGenPattern}"); var patternStrRefRegion = Pattern.Transform(logWriteGenPattern); List <long> regionStrRefOffsets; if (!Pattern.FindAll(_data, patternStrRefRegion, out regionStrRefOffsets)) { Console.WriteLine("[TargetPE::InitKeysOffsets]: No offsets were found."); return(false); } Console.WriteLine($"[TargetPE::InitKeysOffsets]: Found {regionStrRefOffsets.Count} region offsets."); var patDecryptRegions = new Pattern.Byte[][] { // push ?? <- DWORD as short // push ?? <- DWORD as short // push ?? <- DWORD as short Pattern.Transform("68 ?? ?? 00 00 68 ?? ?? 00 00 68 ?? ?? 00 00 ?? ?? E8"), //Pattern.Transform("FF ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 55 FF"), //Pattern.Transform("FF ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 56 FF"), //Pattern.Transform("FF ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 57 FF"), // call ?? <- ReadFile // push ebp|esi|edi // call ?? <- CloseHandle // xor reg,reg Pattern.Transform("FF ?? ?? ?? ?? ?? 55 FF"), Pattern.Transform("FF ?? ?? ?? ?? ?? 56 FF"), Pattern.Transform("FF ?? ?? ?? ?? ?? 57 FF"), }; const int ENCRYPT_REGION_MIN = 50; const int ENCRYPT_REGION_MAX = 700; const int ESTIMATED_DES_RANGE_BYTES = 280; bool detectedDesEncryption = false; foreach (long regionStrRefOffset in regionStrRefOffsets) { long decryptRegionOffset = -1; bool isInlinedFunction = false; for (int i = 0; i < patDecryptRegions.Length; i++) { if (!Pattern.Find(_data, patDecryptRegions[i], out decryptRegionOffset, regionStrRefOffset + ENCRYPT_REGION_MIN, regionStrRefOffset + ENCRYPT_REGION_MAX)) { continue; } // Estimate average distance by bytes, to know whether there is more code for DES encryption before XOR. if (!detectedDesEncryption && (decryptRegionOffset - regionStrRefOffset) > ESTIMATED_DES_RANGE_BYTES) { detectedDesEncryption = true; } // First pattern: we know it's a function that got inlined by the compiler. // +5 for ptr & +2 for xor reg,reg isInlinedFunction = i != 0; if (isInlinedFunction) { CanUpdateKey2 = false; decryptRegionOffset += patDecryptRegions[i].Length + 5 + 2; } break; } if (decryptRegionOffset == -1) { Console.WriteLine($"[TargetPE::InitKeysOffsets]: Unable to find decryption region offset."); continue; } CryptoKey[] keys = new CryptoKey[CryptoPatch.KEYS_COUNT]; MemoryStream ms = (MemoryStream)PE.GetStream(); ms.Seek(decryptRegionOffset, SeekOrigin.Begin); if (isInlinedFunction) { if (!InitInlinedKeys(keys, ms)) { return(false); } } else { for (int i = CryptoPatch.KEYS_COUNT - 1; i >= 0; i--) { DWORD dword; ms.Position++; dword = ms.ReadStructure <DWORD>(); keys[i] = new CryptoKey((ms.Position - 4), (ushort)dword.sValue1, (long)Calculator.OffsetToVA((ulong)(ms.Position - 4))); } } long keyVA = (long)Calculator.OffsetToVA((ulong)decryptRegionOffset); var patch = new CryptoPatch(decryptRegionOffset, keyVA, keys, isInlinedFunction); CryptoPatches.Add(patch); } // Update the container cryption type, so we know what we deal with CryptoPatches.CryptoType = detectedDesEncryption ? CryptoType.DES_XOR : CryptoType.XOR; if (CryptoPatches.Count != regionStrRefOffsets.Count) { return(false); } if (!VerifyAllMatchedKeys(CryptoPatches)) { return(false); } return(true); }