From db4a052285a1250085332e1202df2a837429cfae Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 18 Nov 2024 01:47:01 +0500 Subject: [PATCH 01/87] Add files via upload --- Directory.Packages.props | 11 +++++ UEAESKeyFinder.sln | 62 ++++++++++++++-------------- UEAESKeyFinder/UEAESKeyFinder.csproj | 26 ++++++++---- 3 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 Directory.Packages.props diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..f5b3650 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,11 @@ + + + true + true + $(NoWarn);NU1507 + + + + + + \ No newline at end of file diff --git a/UEAESKeyFinder.sln b/UEAESKeyFinder.sln index d8365f4..5a222f1 100644 --- a/UEAESKeyFinder.sln +++ b/UEAESKeyFinder.sln @@ -1,31 +1,31 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31105.61 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UEAESKeyFinder", "UEAESKeyFinder\UEAESKeyFinder.csproj", "{A184C3B9-EDEF-4BA5-96E6-207908AC9A46}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.ActiveCfg = Debug|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.Build.0 = Debug|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.ActiveCfg = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.Build.0 = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.ActiveCfg = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B55F5E37-882F-4695-B88E-49404B10CC4E} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UEAESKeyFinder", "UEAESKeyFinder\UEAESKeyFinder.csproj", "{A184C3B9-EDEF-4BA5-96E6-207908AC9A46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.ActiveCfg = Debug|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.Build.0 = Debug|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.ActiveCfg = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.Build.0 = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.ActiveCfg = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B55F5E37-882F-4695-B88E-49404B10CC4E} + EndGlobalSection +EndGlobal diff --git a/UEAESKeyFinder/UEAESKeyFinder.csproj b/UEAESKeyFinder/UEAESKeyFinder.csproj index d3ac3ac..3d58563 100644 --- a/UEAESKeyFinder/UEAESKeyFinder.csproj +++ b/UEAESKeyFinder/UEAESKeyFinder.csproj @@ -1,9 +1,17 @@ - - - - Exe - netcoreapp3.1 - AnyCPU;x64 - - - + + + Exe + netcoreapp8.0 + AnyCPU;x64 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file From e693b0677367539f7c0bc55eff534c248ceacb74 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 18 Nov 2024 02:21:35 +0500 Subject: [PATCH 02/87] Create SpecialForcesGroup3.md --- AESKeys/SpecialForcesGroup3.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 AESKeys/SpecialForcesGroup3.md diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md new file mode 100644 index 0000000..72596e3 --- /dev/null +++ b/AESKeys/SpecialForcesGroup3.md @@ -0,0 +1,6 @@ +# AES Keys for SpecialForcesGroup3 + +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| 1.7 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | From 29315dc08409329cadcdddb02ea1e01d27214736 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 18 Nov 2024 02:31:47 +0500 Subject: [PATCH 03/87] Create SpecialForcesGroup2.md --- AESKeys/SpecialForcesGroup2.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 AESKeys/SpecialForcesGroup2.md diff --git a/AESKeys/SpecialForcesGroup2.md b/AESKeys/SpecialForcesGroup2.md new file mode 100644 index 0000000..4a12286 --- /dev/null +++ b/AESKeys/SpecialForcesGroup2.md @@ -0,0 +1,6 @@ +# AES Keys for SpecialForcesGroup2 + +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| 4.21 | 0x78E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A00000 | From 354266ff8e27713477bdf8cb79aa9a159f370774 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 7 Dec 2024 14:54:04 +0500 Subject: [PATCH 04/87] Add files via upload --- Directory.Packages.props | 4 +- UEAESKeyFinder/Program.cs | 390 ++++++++++---------- UEAESKeyFinder/Searcher.cs | 710 ++++++++++++++++++------------------- 3 files changed, 552 insertions(+), 552 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f5b3650..a51e1cc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ $(NoWarn);NU1507 - - + + \ No newline at end of file diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 5081b7b..1b7d1af 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,196 +1,196 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using static Searcher; - -namespace UEAesKeyFinder -{ - class Program - { - [DllImport("ntdll.dll", PreserveSig = false)] - public static extern void NtSuspendProcess(IntPtr processHandle); - public static byte[] GetHex(string hex) - { - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - return r; - } - static void Main(string[] args) - { - Searcher searcher = new Searcher(); - Process game = new Process(); - - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); - string path; - string EngineVersion = "4.18.0"; - string saveName = ""; - switch (method) - { - case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); - string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path)); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } - - searcher = new Searcher(File.ReadAllBytes(path), true); - break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } - searcher = new Searcher(File.ReadAllBytes(path), true, true); - break; - } - - Dictionary aesKeys = searcher.FindAllPattern(out long took); - - if (aesKeys.Count > 0) - { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); - } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; - - Console.ReadLine(); - } - } +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using static Searcher; + +namespace UEAesKeyFinder +{ + class Program + { + [DllImport("ntdll.dll", PreserveSig = false)] + public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) + { + var r = new byte[hex.Length / 2]; + for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return r; + } + static void Main(string[] args) + { + Searcher searcher = new Searcher(); + Process game = new Process(); + + Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); + + char method = (char)Console.Read(); + string path; + string EngineVersion = "4.18.0"; + string saveName = ""; + switch (method) + { + case '0': + Console.Write("Enter the name or id of the process: "); + Console.Read(); + Console.Read(); + string ProcessName = Console.ReadLine(); + + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) + { + Console.WriteLine($"\nFound {p.ProcessName}"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the process."); + Console.ReadLine(); + return; + } + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '1': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1000); + // Not required to fully load + NtSuspendProcess(game.Handle); + + searcher = new Searcher(game); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '2': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length-1]; + + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '3': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the lib."); + return; + } + + searcher = new Searcher(File.ReadAllBytes(path), true); + break; + case '4': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the apk."); + return; + } + searcher = new Searcher(File.ReadAllBytes(path), true, true); + break; + } + + Dictionary aesKeys = searcher.FindAllPattern(out long took); + + if (aesKeys.Count > 0) + { + string WriteToFile = ""; + string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + + WriteToFile += txt; + + Console.ForegroundColor = ConsoleColor.Green; + Console.Write("\n" + txt); + Console.ForegroundColor = ConsoleColor.White; + int EngineVersionI = 17; + if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); + if (EngineVersionI < 18) + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + else + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + + File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("\nFailed to find any AES Keys."); + } + + if (method == '1') try { game.Kill(); } catch { }; + + Console.ReadLine(); + } + } } \ No newline at end of file diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 8282c69..dbb2b95 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,356 +1,356 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; - -public class Searcher -{ - private const int PAGE_SIZE = 4000; - - private bool useUE4Lib = false; - - private IntPtr hProcess; - private Process Process; - private ulong AllocationBase; - private byte[] ProcessMemory; - private string FilePath; - - public Searcher() { } - - public Searcher(Process p) - { - Process = p; - hProcess = p.Handle; - AllocationBase = (ulong)p.MainModule.BaseAddress; - ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - - // To best honest idk why some regions are 0 if we read all at once ¯\_(ツ)_/¯ - for (int i = 0; i < ProcessMemory.Length; i += 2048) - { - byte[] bytes = new byte[2048]; - Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, 2048); - for (int ii = 0; ii < bytes.Length; ii++) - { - if (!(i + ii >= ProcessMemory.Length)) ProcessMemory[i + ii] = bytes[ii]; - else break; - }; - } - } - public Searcher(byte[] bytes) - { - AllocationBase = 0; - ProcessMemory = bytes; - } - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) - { - if (isAPK) - { - // Find "APK Sig Block" (Opening the whole file is bad...) - int libUE4Offset = 0; - for (int i = bytes.Length - 1; i > 0; i--) - { - if ( - bytes[i] != 0x41 || - bytes[i + 1] != 0x50 || - bytes[i + 1] != 0x50 || - bytes[i + 2] != 0x4B || - bytes[i + 3] != 0x20 || - bytes[i + 4] != 0x53 || - bytes[i + 5] != 0x69 || - bytes[i + 6] != 0x67 || - bytes[i + 7] != 0x20 || - bytes[i + 8] != 0x42 || - bytes[i + 9] != 0x6C || - bytes[i + 10] != 0x6F || - bytes[i + 11] != 0x63 || - bytes[i + 12] != 0x6B - ) continue; - - libUE4Offset = i; - break; - } - - byte[] libUE4 = new byte[] { 0x6C, 0x69, 0x62, 0x2F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x2D, 0x76, 0x38, 0x61, 0x2F, 0x6C, 0x69, 0x62, 0x55, 0x45, 0x34, 0x2E, 0x73, 0x6F }; - for (int i = libUE4Offset; i < bytes.Length - 1 - libUE4.Length - 1; i++) - { - if (bytes[i] != libUE4[0]) continue; - bool c = false; - for (int ii = 0; ii < libUE4.Length - 1; ii++) - { - if (bytes[ii + i] != libUE4[ii]) - { - c = true; - break; - }; - }; - if (c) continue; - - libUE4Offset = BitConverter.ToInt32(bytes[(i - 4)..(i)]); - } - if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, patterns were not found!"); - - // Read compressed/uncompressed size from the header and then skip it - int compressed = BitConverter.ToInt32(bytes[(libUE4Offset + 18)..(libUE4Offset + 22)]); - int uncompressed = BitConverter.ToInt32(bytes[(libUE4Offset + 22)..(libUE4Offset + 26)]); - libUE4Offset = libUE4Offset + 53; // Header size is hardcoded, but why would it ever change? - - MemoryStream uncompressedLibUE4 = new MemoryStream(); - DeflateStream deflated = new DeflateStream(new MemoryStream(bytes[(libUE4Offset)..(libUE4Offset + compressed)]), CompressionMode.Decompress); - deflated.CopyTo(uncompressedLibUE4); - if (uncompressedLibUE4.Length != uncompressed) throw new Exception("Failed to read LibUE4.so, decompressed size does not match the decompressed size from the header!"); - ProcessMemory = uncompressedLibUE4.ToArray(); - } - else - { - ProcessMemory = bytes; - } - - useUE4Lib = useAndroid; - } - public void SetFilePath(string path) { FilePath = path; } - public string SearchEngineVersion() - { - if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - // We search backwards because its mostly at the end - byte[] ProductVersion = new byte[] { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; - for (int i = ProcessMemory.Length - 1; i > 0; i--) - { - if (this.ProcessMemory[i] != ProductVersion[0]) continue; - bool c = false; - for (int ii = 0; ii < ProductVersion.Length - 1; ii++) - { - if (this.ProcessMemory[ii + i] != ProductVersion[ii]) - { - c = true; - break; - }; - }; - if (c) continue; - - UnicodeEncoding unicodeEncoding = new UnicodeEncoding(); - return new string(unicodeEncoding.GetChars(this.ProcessMemory[(i + ProductVersion.Length - 2)..(i + ProductVersion.Length - 2 + 14)], 2, 12)); - } - - return ""; - } - public int FollowJMP(int addr) - { - addr = (BitConverter.ToInt32(this.ProcessMemory[(addr + 1)..(addr + 1 + 4)].ToArray()) + 5) + addr; - if ((this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)) return FollowJMP(addr + 4); - - return addr; - } - public UInt64 DecodeADRP(int adrp) // https://chromium.googlesource.com/chromiumos/third_party/binutils/+/refs/heads/stabilize-7374.B/gold/aarch64.cc#150 - { - const int mask19 = (1 << 19) - 1; - const int mask2 = 3; - - // 21-bit imm encoded in adrp. - int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); - // Retrieve msb of 21-bit-signed imm for sign extension. - int msbt = (imm >> 20) & 1; - // Real value is imm multipled by 4k. Value now has 33-bit information. - int value = imm << 12; - // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it - // with value. - return (UInt64)(((((int)(1) << 32) - msbt) << 33) | value); - } - public UInt64 DecodeADD(int add) - { - var imm12 = (add & 0x3ffc00) >> 10; - if ((imm12 & 0xc00000) != 0) imm12 <<= 12; - return (UInt64)imm12; - } - public int GetADRLAddress(int ADRPLoc) - { - UInt64 ADRP = DecodeADRP(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc)); - UInt64 ADD = DecodeADD(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc + 4)); - - return (int)((((UInt64)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); - } - public Dictionary FindAllPattern(out long t) - { - Stopwatch timer = Stopwatch.StartNew(); - Dictionary offsets = new Dictionary(); - - // Android - if (useUE4Lib) - { - string aesKey = ""; - // We could (should) use a function to match the pattern but idk (lazy)... - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - // if this gets no results (or too many) for some reason we could also get the addr that calls this... - - // 01 01 40 AD 01 00 00 AD C0 03 5F D6 - - // First instruction is the adrp, then add... - - // Second instruction - if (this.ProcessMemory[i] != 0x01) continue; - if (this.ProcessMemory[i + 1] != 0x01) continue; - if (this.ProcessMemory[i + 2] != 0x40) continue; - if (this.ProcessMemory[i + 3] != 0xAD) continue; - - // Third instruction - if (this.ProcessMemory[i + 4] != 0x01) continue; - if (this.ProcessMemory[i + 5] != 0x00) continue; - if (this.ProcessMemory[i + 6] != 0x00) continue; - if (this.ProcessMemory[i + 7] != 0xAD) continue; - - // Fourth instruction - if (this.ProcessMemory[i + 8] != 0xC0) continue; - if (this.ProcessMemory[i + 9] != 0x03) continue; - if (this.ProcessMemory[i + 10] != 0x5F) continue; - if (this.ProcessMemory[i + 11] != 0xD6) continue; - - aesKey = ""; - int aesKeyAddr = GetADRLAddress(i - 8); - - aesKey += BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - - aesKeyAddr += 0x1000; // Please fix this, idk when its + 0x1000 and when not.... - aesKey = BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - } - } - else - { - string EngineVersionStr = SearchEngineVersion(); - int EngineVersion = 17; - if (EngineVersionStr != "") EngineVersion = Convert.ToInt32(EngineVersionStr.Split(".")[1]); - if (EngineVersion < 18) - { - // Let's just try something, not sure if that works for all games - string aesKey = ""; - - for (int i = 0; i < ProcessMemory.Length-10; i++) - { - if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; - - // Now we need to find where the first 00 starts and go back to that location - int start = i; - while (ProcessMemory[start-1] == 0x00) start -= 1; - - // Key is 64 letters long, lets make sure the first byte before the key is 0x00 - if (ProcessMemory[start - 65] != 0x00) continue; - - aesKey = Encoding.Default.GetString(ProcessMemory[(start - 64)..start]); - - // Lets make sure the key is as valid string - if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) - { - offsets.Add(AllocationBase + (ulong)start-64, aesKey); - break; - } - } - } - - { - // Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z" - // https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841 - - // Should work for all newer Fortnite versions - // C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ? - - string aesKey = ""; - int verify_1 = 0xC7; - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - try - { - // Should start with smth like 48 8D 64 24 08 and end with it - if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue; - if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue; - int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; - int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; - if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue; - - // It should be the first keypart - if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue; - - verify_3 += 0x04; - // Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps - bool c = false; - int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1); - aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck - - while (aesKey.Length != 64) - // 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4 - // older versions have a simple mov rcx, but never have mov rcx+4 - { - if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games - { - // Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start - // JMP Right after it is possible too - if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9) - { - addr += 4; // Skip the useless bytes - // jump to the address and check if the bytes are valid - addr = FollowJMP(addr); - if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true; - } - else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true; - else addr += 4; - }; - - if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); - else - { - if (this.ProcessMemory[addr + 1] != verify_2) c = true; - if ((this.ProcessMemory[addr + 2] != verify_3)) c = true; - aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", ""); - addr += 4 + 3; // C7 4x xx - verify_3 += 0x04; - }; - - if (aesKey.Length == 64) - { - // This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys. - // if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true; - if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); - // && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08 - if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48) - { - // There might be movups so lets check 50 bytes if we still get 48 8D - if (this.ProcessMemory[addr] != 0x0F) c = true; - for (int xx = 0; xx < 30; xx++) - { - addr = addr + xx; - if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break; - } - // We should probably delete this... - if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true; - } - } - if (c) break; - } - if (c) continue; - - offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); - } - catch { } - } - } - } - - t = timer.ElapsedMilliseconds; - return offsets; - } - - public static class Win32 - { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); - - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); - } +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; + +public class Searcher +{ + private const int PAGE_SIZE = 4000; + + private bool useUE4Lib = false; + + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } + + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + + // To best honest idk why some regions are 0 if we read all at once ¯\_(ツ)_/¯ + for (int i = 0; i < ProcessMemory.Length; i += 2048) + { + byte[] bytes = new byte[2048]; + Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, 2048); + for (int ii = 0; ii < bytes.Length; ii++) + { + if (!(i + ii >= ProcessMemory.Length)) ProcessMemory[i + ii] = bytes[ii]; + else break; + }; + } + } + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + { + if (isAPK) + { + // Find "APK Sig Block" (Opening the whole file is bad...) + int libUE4Offset = 0; + for (int i = bytes.Length - 1; i > 0; i--) + { + if ( + bytes[i] != 0x41 || + bytes[i + 1] != 0x50 || + bytes[i + 1] != 0x50 || + bytes[i + 2] != 0x4B || + bytes[i + 3] != 0x20 || + bytes[i + 4] != 0x53 || + bytes[i + 5] != 0x69 || + bytes[i + 6] != 0x67 || + bytes[i + 7] != 0x20 || + bytes[i + 8] != 0x42 || + bytes[i + 9] != 0x6C || + bytes[i + 10] != 0x6F || + bytes[i + 11] != 0x63 || + bytes[i + 12] != 0x6B + ) continue; + + libUE4Offset = i; + break; + } + + byte[] libUE4 = new byte[] { 0x6C, 0x69, 0x62, 0x2F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x2D, 0x76, 0x38, 0x61, 0x2F, 0x6C, 0x69, 0x62, 0x55, 0x45, 0x34, 0x2E, 0x73, 0x6F }; + for (int i = libUE4Offset; i < bytes.Length - 1 - libUE4.Length - 1; i++) + { + if (bytes[i] != libUE4[0]) continue; + bool c = false; + for (int ii = 0; ii < libUE4.Length - 1; ii++) + { + if (bytes[ii + i] != libUE4[ii]) + { + c = true; + break; + }; + }; + if (c) continue; + + libUE4Offset = BitConverter.ToInt32(bytes[(i - 4)..(i)]); + } + if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, patterns were not found!"); + + // Read compressed/uncompressed size from the header and then skip it + int compressed = BitConverter.ToInt32(bytes[(libUE4Offset + 18)..(libUE4Offset + 22)]); + int uncompressed = BitConverter.ToInt32(bytes[(libUE4Offset + 22)..(libUE4Offset + 26)]); + libUE4Offset = libUE4Offset + 53; // Header size is hardcoded, but why would it ever change? + + MemoryStream uncompressedLibUE4 = new MemoryStream(); + DeflateStream deflated = new DeflateStream(new MemoryStream(bytes[(libUE4Offset)..(libUE4Offset + compressed)]), CompressionMode.Decompress); + deflated.CopyTo(uncompressedLibUE4); + if (uncompressedLibUE4.Length != uncompressed) throw new Exception("Failed to read LibUE4.so, decompressed size does not match the decompressed size from the header!"); + ProcessMemory = uncompressedLibUE4.ToArray(); + } + else + { + ProcessMemory = bytes; + } + + useUE4Lib = useAndroid; + } + public void SetFilePath(string path) { FilePath = path; } + public string SearchEngineVersion() + { + if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // We search backwards because its mostly at the end + byte[] ProductVersion = new byte[] { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; + for (int i = ProcessMemory.Length - 1; i > 0; i--) + { + if (this.ProcessMemory[i] != ProductVersion[0]) continue; + bool c = false; + for (int ii = 0; ii < ProductVersion.Length - 1; ii++) + { + if (this.ProcessMemory[ii + i] != ProductVersion[ii]) + { + c = true; + break; + }; + }; + if (c) continue; + + UnicodeEncoding unicodeEncoding = new UnicodeEncoding(); + return new string(unicodeEncoding.GetChars(this.ProcessMemory[(i + ProductVersion.Length - 2)..(i + ProductVersion.Length - 2 + 14)], 2, 12)); + } + + return ""; + } + public int FollowJMP(int addr) + { + addr = (BitConverter.ToInt32(this.ProcessMemory[(addr + 1)..(addr + 1 + 4)].ToArray()) + 5) + addr; + if ((this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)) return FollowJMP(addr + 4); + + return addr; + } + public UInt64 DecodeADRP(int adrp) // https://chromium.googlesource.com/chromiumos/third_party/binutils/+/refs/heads/stabilize-7374.B/gold/aarch64.cc#150 + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + + // 21-bit imm encoded in adrp. + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + // Retrieve msb of 21-bit-signed imm for sign extension. + int msbt = (imm >> 20) & 1; + // Real value is imm multipled by 4k. Value now has 33-bit information. + int value = imm << 12; + // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it + // with value. + return (UInt64)(((((int)(1) << 32) - msbt) << 33) | value); + } + public UInt64 DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) imm12 <<= 12; + return (UInt64)imm12; + } + public int GetADRLAddress(int ADRPLoc) + { + UInt64 ADRP = DecodeADRP(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc)); + UInt64 ADD = DecodeADD(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc + 4)); + + return (int)((((UInt64)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + public Dictionary FindAllPattern(out long t) + { + Stopwatch timer = Stopwatch.StartNew(); + Dictionary offsets = new Dictionary(); + + // Android + if (useUE4Lib) + { + string aesKey = ""; + // We could (should) use a function to match the pattern but idk (lazy)... + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + // if this gets no results (or too many) for some reason we could also get the addr that calls this... + + // 01 01 40 AD 01 00 00 AD C0 03 5F D6 + + // First instruction is the adrp, then add... + + // Second instruction + if (this.ProcessMemory[i] != 0x01) continue; + if (this.ProcessMemory[i + 1] != 0x01) continue; + if (this.ProcessMemory[i + 2] != 0x40) continue; + if (this.ProcessMemory[i + 3] != 0xAD) continue; + + // Third instruction + if (this.ProcessMemory[i + 4] != 0x01) continue; + if (this.ProcessMemory[i + 5] != 0x00) continue; + if (this.ProcessMemory[i + 6] != 0x00) continue; + if (this.ProcessMemory[i + 7] != 0xAD) continue; + + // Fourth instruction + if (this.ProcessMemory[i + 8] != 0xC0) continue; + if (this.ProcessMemory[i + 9] != 0x03) continue; + if (this.ProcessMemory[i + 10] != 0x5F) continue; + if (this.ProcessMemory[i + 11] != 0xD6) continue; + + aesKey = ""; + int aesKeyAddr = GetADRLAddress(i - 8); + + aesKey += BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // Please fix this, idk when its + 0x1000 and when not.... + aesKey = BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (EngineVersionStr != "") EngineVersion = Convert.ToInt32(EngineVersionStr.Split(".")[1]); + if (EngineVersion < 18) + { + // Let's just try something, not sure if that works for all games + string aesKey = ""; + + for (int i = 0; i < ProcessMemory.Length-10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + // Now we need to find where the first 00 starts and go back to that location + int start = i; + while (ProcessMemory[start-1] == 0x00) start -= 1; + + // Key is 64 letters long, lets make sure the first byte before the key is 0x00 + if (ProcessMemory[start - 65] != 0x00) continue; + + aesKey = Encoding.Default.GetString(ProcessMemory[(start - 64)..start]); + + // Lets make sure the key is as valid string + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)start-64, aesKey); + break; + } + } + } + + { + // Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z" + // https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841 + + // Should work for all newer Fortnite versions + // C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ? + + string aesKey = ""; + int verify_1 = 0xC7; + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + try + { + // Should start with smth like 48 8D 64 24 08 and end with it + if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue; + if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue; + int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue; + + // It should be the first keypart + if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue; + + verify_3 += 0x04; + // Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps + bool c = false; + int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1); + aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck + + while (aesKey.Length != 64) + // 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4 + // older versions have a simple mov rcx, but never have mov rcx+4 + { + if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games + { + // Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start + // JMP Right after it is possible too + if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; // Skip the useless bytes + // jump to the address and check if the bytes are valid + addr = FollowJMP(addr); + if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true; + } + else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true; + else addr += 4; + }; + + if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); + else + { + if (this.ProcessMemory[addr + 1] != verify_2) c = true; + if ((this.ProcessMemory[addr + 2] != verify_3)) c = true; + aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", ""); + addr += 4 + 3; // C7 4x xx + verify_3 += 0x04; + }; + + if (aesKey.Length == 64) + { + // This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys. + // if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true; + if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); + // && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08 + if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48) + { + // There might be movups so lets check 50 bytes if we still get 48 8D + if (this.ProcessMemory[addr] != 0x0F) c = true; + for (int xx = 0; xx < 30; xx++) + { + addr = addr + xx; + if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break; + } + // We should probably delete this... + if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true; + } + } + if (c) break; + } + if (c) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch { } + } + } + } + + t = timer.ElapsedMilliseconds; + return offsets; + } + + public static class Win32 + { + [DllImport("kernel32.dll")] + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + } } \ No newline at end of file From 543cc19c1173e618ca678eb24f18da47e89d1ae4 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Thu, 19 Dec 2024 20:33:00 +0500 Subject: [PATCH 05/87] Update SpecialForcesGroup3.md --- AESKeys/SpecialForcesGroup3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md index 72596e3..516cd51 100644 --- a/AESKeys/SpecialForcesGroup3.md +++ b/AESKeys/SpecialForcesGroup3.md @@ -3,4 +3,4 @@ ## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | -| 1.7 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 1.7 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | From cb168127d53af9a5ee34115146976cc78ca46e5d Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Fri, 23 May 2025 17:46:17 +0500 Subject: [PATCH 06/87] Update Splitgate.md --- AESKeys/Splitgate.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AESKeys/Splitgate.md b/AESKeys/Splitgate.md index b9a7a4b..d1008ec 100644 --- a/AESKeys/Splitgate.md +++ b/AESKeys/Splitgate.md @@ -1,4 +1,7 @@ # AES Keys for Splitgate Never changed afaik -Key: 0xD73A797940208F2FB29256BE81A7CBC7B74CBF899441BB277F357F7F4577DBBB \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0xD73A797940208F2FB29256BE81A7CBC7B74CBF899441BB277F357F7F4577DBBB | From 58a878af50df7287a7f796ed8775b010a2b7d727 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Fri, 23 May 2025 17:47:52 +0500 Subject: [PATCH 07/87] Update Sword Art Online - Fatal Bullet.md --- AESKeys/Sword Art Online - Fatal Bullet.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AESKeys/Sword Art Online - Fatal Bullet.md b/AESKeys/Sword Art Online - Fatal Bullet.md index 2599c4b..4b7c619 100644 --- a/AESKeys/Sword Art Online - Fatal Bullet.md +++ b/AESKeys/Sword Art Online - Fatal Bullet.md @@ -1,4 +1,7 @@ # AES Keys for Sword Art Online : Fatal Bullet Never changed afaik -Key: h67GrjX2aGMgrAQeNwf9VmCYbt50ylJFeP3rIhbxh4e9bZXnqm8sbvEjWGOi6rgs \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | h67GrjX2aGMgrAQeNwf9VmCYbt50ylJFeP3rIhbxh4e9bZXnqm8sbvEjWGOi6rgs | From e2a814a298b2b140844d264dcc7c52b71aa2b253 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Fri, 23 May 2025 17:49:22 +0500 Subject: [PATCH 08/87] Update Valorant.md --- AESKeys/Valorant.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AESKeys/Valorant.md b/AESKeys/Valorant.md index 778eef8..698dc2a 100644 --- a/AESKeys/Valorant.md +++ b/AESKeys/Valorant.md @@ -1,4 +1,7 @@ # AES Keys for Valorant Never changed afaik -Key: 0x4BE71AF2459CF83899EC9DC2CB60E22AC4B3047E0211034BBABE9D174C069DD6 \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0x4BE71AF2459CF83899EC9DC2CB60E22AC4B3047E0211034BBABE9D174C069DD6 | From fa0058ca313f77155bd838c7a5cc35f5cdcdc6fa Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Fri, 23 May 2025 17:51:24 +0500 Subject: [PATCH 09/87] Update Blade & Soul.md --- AESKeys/Blade & Soul.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AESKeys/Blade & Soul.md b/AESKeys/Blade & Soul.md index 59c7a64..cb19b05 100644 --- a/AESKeys/Blade & Soul.md +++ b/AESKeys/Blade & Soul.md @@ -1,4 +1,7 @@ # AES Keys for Blade & Soul Never changed afaik -Key: 0xD2E5F7F94E625EFE2726B5360C1039CE7CB9ABB760A94F37BB15A6DC08741656 \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0xD2E5F7F94E625EFE2726B5360C1039CE7CB9ABB760A94F37BB15A6DC08741656 | From 6ae5a32e24fc66dc3149362289b764765a977f88 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Fri, 23 May 2025 19:07:36 +0500 Subject: [PATCH 10/87] Update SpecialForcesGroup3.md --- AESKeys/SpecialForcesGroup3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md index 516cd51..6711e30 100644 --- a/AESKeys/SpecialForcesGroup3.md +++ b/AESKeys/SpecialForcesGroup3.md @@ -3,4 +3,4 @@ ## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | -| 1.7 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | From 44687ab149c1c94ed488db17ed56ad5f282b9419 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 06:01:41 +0500 Subject: [PATCH 11/87] Add files via upload --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a51e1cc..2b63181 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,6 @@ - + \ No newline at end of file From eb98e893be9647b11b1fd7dd83ff21fa5db2333b Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 06:02:05 +0500 Subject: [PATCH 12/87] Add files via upload From 0373ba1de5713046fc529abfae1fa26f9195dff2 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 06:53:40 +0500 Subject: [PATCH 13/87] Create .github/workflows/build.yml --- .github/workflows/build.yml | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..73e44c2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: Build UEAESKeyFinder + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + + - name: Restore dependencies + run: dotnet restore UEAESKeyFinder.sln + + - name: Build + run: dotnet build UEAESKeyFinder.sln --configuration Release --no-restore + + - name: Publish (Generate EXE) + run: dotnet publish UEAESKeyFinder.sln -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o publish + + - name: Upload EXE Artifact + uses: actions/upload-artifact@v4 + with: + name: UEAESKeyFinder + path: publish/** \ No newline at end of file From 55d1239d7d912e228729a58e6c47c424fed9c2f8 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 07:18:35 +0500 Subject: [PATCH 14/87] Update build.yml --- .github/workflows/build.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 73e44c2..bdeaaff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,11 +2,7 @@ name: Build UEAESKeyFinder on: push: - branches: - - main - pull_request: - branches: - - main + branches: [ main ] workflow_dispatch: jobs: @@ -30,7 +26,7 @@ jobs: run: dotnet build UEAESKeyFinder.sln --configuration Release --no-restore - name: Publish (Generate EXE) - run: dotnet publish UEAESKeyFinder.sln -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o publish + run: dotnet publish UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o publish - name: Upload EXE Artifact uses: actions/upload-artifact@v4 From 5843614bf2fdbf2ffedffd6044ca8c91d020abab Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 08:54:09 +0500 Subject: [PATCH 15/87] Update build.yml --- .github/workflows/build.yml | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdeaaff..78f35f1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,8 @@ name: Build UEAESKeyFinder on: push: - branches: [ main ] + branches: + - main workflow_dispatch: jobs: @@ -10,26 +11,27 @@ jobs: runs-on: windows-latest steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 8.0.x - - - name: Restore dependencies - run: dotnet restore UEAESKeyFinder.sln - - - name: Build - run: dotnet build UEAESKeyFinder.sln --configuration Release --no-restore - - - name: Publish (Generate EXE) - run: dotnet publish UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o publish - - - name: Upload EXE Artifact - uses: actions/upload-artifact@v4 - with: - name: UEAESKeyFinder - path: publish/** \ No newline at end of file + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + + - name: Restore dependencies + run: dotnet restore UEAESKeyFinder.sln + + - name: Build Publish Binary + run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + tag_name: auto-${{ github.run_number }} + name: Авто-релиз ${{ github.run_number }} + body: "Автоматически сгенерированная сборка при пуше в main" + files: ./publish/** + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 85d40dcd54fc086edb5b95da1f313bf6a1f53709 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 08:58:19 +0500 Subject: [PATCH 16/87] Update build.yml --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78f35f1..d2ceebe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,12 +26,12 @@ jobs: - name: Build Publish Binary run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish - - name: Create Release + - name: Create or Update Release uses: softprops/action-gh-release@v2 with: - tag_name: auto-${{ github.run_number }} - name: Авто-релиз ${{ github.run_number }} - body: "Автоматически сгенерированная сборка при пуше в main" + tag_name: latest + name: UEAESKeyFinder Build + body: "Automatically generated build on push to main" files: ./publish/** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 12977e81e027a9a881895ff5d1efba0795a6346e Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:01:32 +0500 Subject: [PATCH 17/87] Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2ceebe..74a1224 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,3 +1,5 @@ +permissions: + contents: write name: Build UEAESKeyFinder on: From 63e53db89359acba438b3d97ad0535773f7db27a Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:09:31 +0500 Subject: [PATCH 18/87] Update build.yml --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74a1224..6594045 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,6 @@ jobs: with: tag_name: latest name: UEAESKeyFinder Build - body: "Automatically generated build on push to main" files: ./publish/** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 522cc688c33a49d37185f572b0cdbfeabe947f2f Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:13:28 +0500 Subject: [PATCH 19/87] Update build.yml --- .github/workflows/build.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6594045..5e9ea20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,11 +28,16 @@ jobs: - name: Build Publish Binary run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish + - name: Get DateTime + id: datetime + run: echo "NOW=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + - name: Create or Update Release uses: softprops/action-gh-release@v2 with: tag_name: latest name: UEAESKeyFinder Build + body: "Build time: ${{ env.NOW }}" files: ./publish/** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 87bf9d048dca0761cccd3f475b2fe04dc2210bae Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:18:16 +0500 Subject: [PATCH 20/87] Update build.yml --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e9ea20..6919096 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,14 +30,14 @@ jobs: - name: Get DateTime id: datetime - run: echo "NOW=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + run: echo "now=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT - name: Create or Update Release uses: softprops/action-gh-release@v2 with: tag_name: latest name: UEAESKeyFinder Build - body: "Build time: ${{ env.NOW }}" + body: "Build time: ${{ steps.datetime.outputs.now }}" files: ./publish/** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 14a8b226ddc4e50e356c9dffd33b972e9999b53a Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:23:34 +0500 Subject: [PATCH 21/87] Update build.yml --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6919096..95f064d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,14 +30,15 @@ jobs: - name: Get DateTime id: datetime - run: echo "now=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT + shell: bash + run: echo "now=$(date '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV - name: Create or Update Release uses: softprops/action-gh-release@v2 with: tag_name: latest name: UEAESKeyFinder Build - body: "Build time: ${{ steps.datetime.outputs.now }}" + body: "Build time: ${{ env.now }}" files: ./publish/** env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From cb8f2a90bcfda4179310e0bbb616f3cb233fe3bb Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:27:36 +0500 Subject: [PATCH 22/87] Update build.yml --- .github/workflows/build.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95f064d..1a1b01a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,8 @@ on: jobs: build: runs-on: windows-latest + env: + TZ: Asia/Tashkent # Устанавливаем часовой пояс Узбекистана steps: - name: Checkout @@ -28,16 +30,16 @@ jobs: - name: Build Publish Binary run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish - - name: Get DateTime + - name: Get DateTime Tashkent id: datetime shell: bash - run: echo "now=$(date '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + run: echo "now=$(TZ='Asia/Tashkent' date '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV - name: Create or Update Release uses: softprops/action-gh-release@v2 with: tag_name: latest - name: UEAESKeyFinder Build + name: "UEAESKeyFinder Build (${{ env.now }})" body: "Build time: ${{ env.now }}" files: ./publish/** env: From e31f92142480de388e286ce8f91a5868570f4bb4 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 09:32:05 +0500 Subject: [PATCH 23/87] Update build.yml --- .github/workflows/build.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a1b01a..03e977d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,8 +11,6 @@ on: jobs: build: runs-on: windows-latest - env: - TZ: Asia/Tashkent # Устанавливаем часовой пояс Узбекистана steps: - name: Checkout @@ -30,10 +28,10 @@ jobs: - name: Build Publish Binary run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish - - name: Get DateTime Tashkent + - name: Get DateTime Tashkent/Uzbekistan id: datetime shell: bash - run: echo "now=$(TZ='Asia/Tashkent' date '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + run: echo "now=$(date -u -d '+5 hour' '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV - name: Create or Update Release uses: softprops/action-gh-release@v2 From ff7ae179576b4e3fa4c6be0b5b7b2a071c81bae6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 23 Aug 2025 20:25:18 +0500 Subject: [PATCH 24/87] Update ReadMe.md --- ReadMe.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 288bfcf..f52aa06 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -25,4 +25,16 @@ Found MyProject Found 1 AES Keys in 720ms 0xD0DE16965D23CC8A46178FFFAB18130651D2C99F7B3D63ECC8FD04D691609572 (0N4Wll0jzIpGF4//qxgTBlHSyZ97PWPsyP0E1pFglXI=) at 140695177160459 -``` \ No newline at end of file + +``` + + From a682cc156994d26f20f8572a28c535d397e54a01 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 25 Aug 2025 15:08:02 +0500 Subject: [PATCH 25/87] Create linux.yml --- .github/workflows/linux.yml | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml new file mode 100644 index 0000000..b490095 --- /dev/null +++ b/.github/workflows/linux.yml @@ -0,0 +1,69 @@ +permissions: + contents: write +name: Build UEAESKeyFinder .deb + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + build-deb: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + + - name: Restore dependencies + run: dotnet restore UEAESKeyFinder.sln + + - name: Build Linux Binary + run: | + dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj \ + -c Release \ + -r linux-x64 \ + --self-contained true \ + -p:PublishSingleFile=true \ + -p:IncludeAllContentForSelfExtract=true \ + -p:PublishTrimmed=false \ + -o ./publish/linux + + - name: Prepare .deb package structure + run: | + mkdir -p ueaeskeyfinder_1.0.0/DEBIAN + mkdir -p ueaeskeyfinder_1.0.0/usr/local/bin + cp publish/linux/UEAESKeyFinder ueaeskeyfinder_1.0.0/usr/local/bin/ueaeskeyfinder + + echo "Package: ueaeskeyfinder + Version: 1.0.0 + Section: utils + Priority: optional + Architecture: amd64 + Maintainer: Your Name + Description: UEAESKeyFinder Linux version + A tool to find UE AES Keys." > ueaeskeyfinder_1.0.0/DEBIAN/control + + - name: Build .deb package + run: dpkg-deb --build ueaeskeyfinder_1.0.0 + + - name: Get DateTime Tashkent/Uzbekistan + id: datetime + shell: bash + run: echo "now=$(date -u -d '+5 hour' '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + + - name: Create or Update Release + uses: softprops/action-gh-release@v2 + with: + tag_name: latest + name: "UEAESKeyFinder Linux .deb Build (${{ env.now }})" + body: "Build time: ${{ env.now }}" + files: ueaeskeyfinder_1.0.0.deb + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From ed8c2b9b9250f3ee8adb6d28a3792e910fe1ccc5 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 25 Aug 2025 15:12:37 +0500 Subject: [PATCH 26/87] Update linux.yml --- .github/workflows/linux.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b490095..e15b527 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -37,21 +37,21 @@ jobs: - name: Prepare .deb package structure run: | - mkdir -p ueaeskeyfinder_1.0.0/DEBIAN - mkdir -p ueaeskeyfinder_1.0.0/usr/local/bin - cp publish/linux/UEAESKeyFinder ueaeskeyfinder_1.0.0/usr/local/bin/ueaeskeyfinder + mkdir -p ueaeskeyfinder/DEBIAN + mkdir -p ueaeskeyfinder/usr/local/bin + cp publish/linux/UEAESKeyFinder ueaeskeyfinder/usr/local/bin/ueaeskeyfinder echo "Package: ueaeskeyfinder - Version: 1.0.0 + Version: 1.0 Section: utils Priority: optional Architecture: amd64 Maintainer: Your Name Description: UEAESKeyFinder Linux version - A tool to find UE AES Keys." > ueaeskeyfinder_1.0.0/DEBIAN/control + A tool to find UE AES Keys." > ueaeskeyfinder/DEBIAN/control - name: Build .deb package - run: dpkg-deb --build ueaeskeyfinder_1.0.0 + run: dpkg-deb --build ueaeskeyfinder ueaeskeyfinder.deb - name: Get DateTime Tashkent/Uzbekistan id: datetime @@ -64,6 +64,6 @@ jobs: tag_name: latest name: "UEAESKeyFinder Linux .deb Build (${{ env.now }})" body: "Build time: ${{ env.now }}" - files: ueaeskeyfinder_1.0.0.deb + files: ueaeskeyfinder.deb env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From b5b46d9649ba94252e3c0978ab78675ed0822411 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 25 Aug 2025 16:09:03 +0500 Subject: [PATCH 27/87] Delete .github/workflows/linux.yml --- .github/workflows/linux.yml | 69 ------------------------------------- 1 file changed, 69 deletions(-) delete mode 100644 .github/workflows/linux.yml diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml deleted file mode 100644 index e15b527..0000000 --- a/.github/workflows/linux.yml +++ /dev/null @@ -1,69 +0,0 @@ -permissions: - contents: write -name: Build UEAESKeyFinder .deb - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - build-deb: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v5 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Restore dependencies - run: dotnet restore UEAESKeyFinder.sln - - - name: Build Linux Binary - run: | - dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj \ - -c Release \ - -r linux-x64 \ - --self-contained true \ - -p:PublishSingleFile=true \ - -p:IncludeAllContentForSelfExtract=true \ - -p:PublishTrimmed=false \ - -o ./publish/linux - - - name: Prepare .deb package structure - run: | - mkdir -p ueaeskeyfinder/DEBIAN - mkdir -p ueaeskeyfinder/usr/local/bin - cp publish/linux/UEAESKeyFinder ueaeskeyfinder/usr/local/bin/ueaeskeyfinder - - echo "Package: ueaeskeyfinder - Version: 1.0 - Section: utils - Priority: optional - Architecture: amd64 - Maintainer: Your Name - Description: UEAESKeyFinder Linux version - A tool to find UE AES Keys." > ueaeskeyfinder/DEBIAN/control - - - name: Build .deb package - run: dpkg-deb --build ueaeskeyfinder ueaeskeyfinder.deb - - - name: Get DateTime Tashkent/Uzbekistan - id: datetime - shell: bash - run: echo "now=$(date -u -d '+5 hour' '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV - - - name: Create or Update Release - uses: softprops/action-gh-release@v2 - with: - tag_name: latest - name: "UEAESKeyFinder Linux .deb Build (${{ env.now }})" - body: "Build time: ${{ env.now }}" - files: ueaeskeyfinder.deb - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From fc78857f8140451a5ca2cd5e72639d32fb76f3d6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:01:37 +0500 Subject: [PATCH 28/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index dbb2b95..299491e 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,3 +1,4 @@ +/** using System; using System.Collections.Generic; using System.Diagnostics; @@ -353,4 +354,4 @@ public static class Win32 [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } -} \ No newline at end of file +}*/ \ No newline at end of file From 15ffaafa4626fd02270c8bf4a8caf2d57d5bbd90 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:02:18 +0500 Subject: [PATCH 29/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 398 ++++++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 299491e..dc5d976 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -354,4 +354,400 @@ public static class Win32 [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } -}*/ \ No newline at end of file +}*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.RegularExpressions; +using System.Runtime.InteropServices; + +public class Searcher +{ + private const int PAGE_SIZE = 4000; + + private bool useUE4Lib = false; + + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } + + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + + // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) + for (int i = 0; i < ProcessMemory.Length; i += 2048) + { + int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + byte[] bytes = new byte[bytesToRead]; + if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) + { + Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); + } + else + { + // Если чтение не удалось — просто продолжаем, можно логировать при необходимости + } + } + } + + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + { + if (isAPK) + { + // Поиск "APK Sig Block" (ищем подпись APK) + int libUE4Offset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) + { + if (bytes[i + j] != apkSigBlock[j]) + { + matched = false; + break; + } + } + if (matched) + { + libUE4Offset = i; + break; + } + } + + if (libUE4Offset == 0) + throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + + // Поиск оффсета libUE4.so в блоке + byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + + int foundOffset = 0; + for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) + { + if (bytes[i] != libUE4[0]) continue; + bool c = false; + for (int ii = 0; ii < libUE4.Length; ii++) + { + if (bytes[i + ii] != libUE4[ii]) + { + c = true; + break; + } + } + if (c) continue; + + // Считаем offset 4 байта перед этим местом (int32 little-endian) + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } + + if (foundOffset == 0) + throw new Exception("Failed to read LibUE4.so, pattern not found!"); + + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + int headerSize = 53; + int dataStart = foundOffset + headerSize; + + using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLibUE4 = new MemoryStream()) + { + deflateStream.CopyTo(uncompressedLibUE4); + if (uncompressedLibUE4.Length != uncompressed) + throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); + + ProcessMemory = uncompressedLibUE4.ToArray(); + } + } + else + { + ProcessMemory = bytes; + } + + useUE4Lib = useAndroid; + } + + public void SetFilePath(string path) => FilePath = path; + + public string SearchEngineVersion() + { + if (FilePath != null) + return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // Паттерн ProductVersion в юникоде (бэкап) + byte[] ProductVersion = new byte[] + { + 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, + 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, + 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 + }; + + for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < ProductVersion.Length; j++) + { + if (ProcessMemory[i + j] != ProductVersion[j]) + { + matched = false; + break; + } + } + if (!matched) continue; + + var unicodeEncoding = new UnicodeEncoding(); + return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) + } + + return ""; + } + + public int FollowJMP(int addr) + { + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int newAddr = addr + offset + 5; + if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + return FollowJMP(newAddr + 4); + return newAddr; + } + + public ulong DecodeADRP(int adrp) + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + int msbt = (imm >> 20) & 1; + int value = imm << 12; + + // Поддержка знакового расширения для 64-битного значения + long signedValue = (long)value; + if (msbt == 1) + signedValue |= -1L << 33; + + return (ulong)signedValue; + } + + public ulong DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) + imm12 <<= 12; + return (ulong)imm12; + } + + public int GetADRLAddress(int ADRPLoc) + { + ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); + ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); + + return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + + public Dictionary FindAllPattern(out long elapsedMilliseconds) + { + Stopwatch timer = Stopwatch.StartNew(); + var offsets = new Dictionary(); + + if (useUE4Lib) + { + // Android UE4 key search pattern + for (int i = 0; i < ProcessMemory.Length - 12; i++) + { + if (ProcessMemory[i] != 0x01) continue; + if (ProcessMemory[i + 1] != 0x01) continue; + if (ProcessMemory[i + 2] != 0x40) continue; + if (ProcessMemory[i + 3] != 0xAD) continue; + if (ProcessMemory[i + 4] != 0x01) continue; + if (ProcessMemory[i + 5] != 0x00) continue; + if (ProcessMemory[i + 6] != 0x00) continue; + if (ProcessMemory[i + 7] != 0xAD) continue; + if (ProcessMemory[i + 8] != 0xC0) continue; + if (ProcessMemory[i + 9] != 0x03) continue; + if (ProcessMemory[i + 10] != 0x5F) continue; + if (ProcessMemory[i + 11] != 0xD6) continue; + + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; + + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр + if (aesKeyAddr + 32 > ProcessMemory.Length) continue; + + aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (!string.IsNullOrEmpty(EngineVersionStr)) + { + string[] parts = EngineVersionStr.Split('.'); + if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) + EngineVersion = ver; + } + + if (EngineVersion < 18) + { + // Старый способ поиска ключа + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + int start = i; + while (start > 0 && ProcessMemory[start - 1] == 0x00) + start--; + + if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + + string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); + break; + } + } + } + + // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) + int verify_1 = 0xC7; + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + try + { + if (i < 3) continue; + + if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) + continue; + + if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) + continue; + + int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + + if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) + continue; + + if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) + continue; + + verify_3 += 0x04; + bool invalid = false; + int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); + string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + + while (aesKey.Length != 64) + { + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + { + if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; + addr = FollowJMP(addr); + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) + invalid = true; + } + else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) + { + invalid = true; + } + else + { + addr += 4; + } + } + + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + else + { + if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) + invalid = true; + + aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); + addr += 7; // 4 + 3 + verify_3 += 0x04; + } + + if (aesKey.Length == 64) + { + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + + if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) + { + if (ProcessMemory[addr] != 0x0F) + invalid = true; + + bool found = false; + for (int xx = 0; xx < 30; xx++) + { + int checkAddr = addr + xx; + if (checkAddr >= ProcessMemory.Length) break; + if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) + { + found = true; + break; + } + } + if (!found) + invalid = true; + } + } + + if (invalid) + break; + } + + if (invalid) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch + { + // Игнорируем ошибки парсинга + } + } + } + + timer.Stop(); + elapsedMilliseconds = timer.ElapsedMilliseconds; + return offsets; + } + + public static class Win32 + { + [DllImport("kernel32.dll")] + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + } +} \ No newline at end of file From 90b7ad7a1ac93d1d68600bccb47f8c50629f24c3 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:14:22 +0500 Subject: [PATCH 30/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index dc5d976..d918e4c 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -744,8 +744,8 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) public static class Win32 { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + [DllImport("kernel32.dll", SetLastError = true)] +public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); From b37d84e85f12d89f56a29c8bb446eb58ad5468de Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:19:29 +0500 Subject: [PATCH 31/87] Refactor Searcher class and update ReadProcessMemory --- UEAESKeyFinder/Searcher.cs | 797 +++++++++++++++++++------------------ 1 file changed, 399 insertions(+), 398 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index d918e4c..eee5de0 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,4 +1,4 @@ -/** +/** using System; using System.Collections.Generic; using System.Diagnostics; @@ -354,400 +354,401 @@ public static class Win32 [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } -}*/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Text; -using System.Text.RegularExpressions; -using System.Runtime.InteropServices; - -public class Searcher -{ - private const int PAGE_SIZE = 4000; - - private bool useUE4Lib = false; - - private IntPtr hProcess; - private Process Process; - private ulong AllocationBase; - private byte[] ProcessMemory; - private string FilePath; - - public Searcher() { } - - public Searcher(Process p) - { - Process = p; - hProcess = p.Handle; - AllocationBase = (ulong)p.MainModule.BaseAddress; - ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - - // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) - for (int i = 0; i < ProcessMemory.Length; i += 2048) - { - int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); - byte[] bytes = new byte[bytesToRead]; - if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) - { - Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); - } - else - { - // Если чтение не удалось — просто продолжаем, можно логировать при необходимости - } - } - } - - public Searcher(byte[] bytes) - { - AllocationBase = 0; - ProcessMemory = bytes; - } - - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) - { - if (isAPK) - { - // Поиск "APK Sig Block" (ищем подпись APK) - int libUE4Offset = 0; - byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); - - for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) - { - bool matched = true; - for (int j = 0; j < apkSigBlock.Length; j++) - { - if (bytes[i + j] != apkSigBlock[j]) - { - matched = false; - break; - } - } - if (matched) - { - libUE4Offset = i; - break; - } - } - - if (libUE4Offset == 0) - throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); - - // Поиск оффсета libUE4.so в блоке - byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - - int foundOffset = 0; - for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) - { - if (bytes[i] != libUE4[0]) continue; - bool c = false; - for (int ii = 0; ii < libUE4.Length; ii++) - { - if (bytes[i + ii] != libUE4[ii]) - { - c = true; - break; - } - } - if (c) continue; - - // Считаем offset 4 байта перед этим местом (int32 little-endian) - foundOffset = BitConverter.ToInt32(bytes, i - 4); - break; - } - - if (foundOffset == 0) - throw new Exception("Failed to read LibUE4.so, pattern not found!"); - - int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); - int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); - int headerSize = 53; - int dataStart = foundOffset + headerSize; - - using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) - using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) - using (var uncompressedLibUE4 = new MemoryStream()) - { - deflateStream.CopyTo(uncompressedLibUE4); - if (uncompressedLibUE4.Length != uncompressed) - throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); - - ProcessMemory = uncompressedLibUE4.ToArray(); - } - } - else - { - ProcessMemory = bytes; - } - - useUE4Lib = useAndroid; - } - - public void SetFilePath(string path) => FilePath = path; - - public string SearchEngineVersion() - { - if (FilePath != null) - return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - // Паттерн ProductVersion в юникоде (бэкап) - byte[] ProductVersion = new byte[] - { - 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, - 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, - 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, - 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 - }; - - for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) - { - bool matched = true; - for (int j = 0; j < ProductVersion.Length; j++) - { - if (ProcessMemory[i + j] != ProductVersion[j]) - { - matched = false; - break; - } - } - if (!matched) continue; - - var unicodeEncoding = new UnicodeEncoding(); - return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) - } - - return ""; - } - - public int FollowJMP(int addr) - { - int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); - int newAddr = addr + offset + 5; - if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) - return FollowJMP(newAddr + 4); - return newAddr; - } - - public ulong DecodeADRP(int adrp) - { - const int mask19 = (1 << 19) - 1; - const int mask2 = 3; - - int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); - int msbt = (imm >> 20) & 1; - int value = imm << 12; - - // Поддержка знакового расширения для 64-битного значения - long signedValue = (long)value; - if (msbt == 1) - signedValue |= -1L << 33; - - return (ulong)signedValue; - } - - public ulong DecodeADD(int add) - { - var imm12 = (add & 0x3ffc00) >> 10; - if ((imm12 & 0xc00000) != 0) - imm12 <<= 12; - return (ulong)imm12; - } - - public int GetADRLAddress(int ADRPLoc) - { - ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); - ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); - - return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); - } - - public Dictionary FindAllPattern(out long elapsedMilliseconds) - { - Stopwatch timer = Stopwatch.StartNew(); - var offsets = new Dictionary(); - - if (useUE4Lib) - { - // Android UE4 key search pattern - for (int i = 0; i < ProcessMemory.Length - 12; i++) - { - if (ProcessMemory[i] != 0x01) continue; - if (ProcessMemory[i + 1] != 0x01) continue; - if (ProcessMemory[i + 2] != 0x40) continue; - if (ProcessMemory[i + 3] != 0xAD) continue; - if (ProcessMemory[i + 4] != 0x01) continue; - if (ProcessMemory[i + 5] != 0x00) continue; - if (ProcessMemory[i + 6] != 0x00) continue; - if (ProcessMemory[i + 7] != 0xAD) continue; - if (ProcessMemory[i + 8] != 0xC0) continue; - if (ProcessMemory[i + 9] != 0x03) continue; - if (ProcessMemory[i + 10] != 0x5F) continue; - if (ProcessMemory[i + 11] != 0xD6) continue; - - int aesKeyAddr = GetADRLAddress(i - 8); - if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; - - string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - - aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр - if (aesKeyAddr + 32 > ProcessMemory.Length) continue; - - aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - } - } - else - { - string EngineVersionStr = SearchEngineVersion(); - int EngineVersion = 17; - if (!string.IsNullOrEmpty(EngineVersionStr)) - { - string[] parts = EngineVersionStr.Split('.'); - if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) - EngineVersion = ver; - } - - if (EngineVersion < 18) - { - // Старый способ поиска ключа - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; - - int start = i; - while (start > 0 && ProcessMemory[start - 1] == 0x00) - start--; - - if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; - - string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); - - if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) - { - offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); - break; - } - } - } - - // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) - int verify_1 = 0xC7; - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - try - { - if (i < 3) continue; - - if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) - continue; - - if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) - continue; - - int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; - int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; - - if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) - continue; - - if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) - continue; - - verify_3 += 0x04; - bool invalid = false; - int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); - string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); - - while (aesKey.Length != 64) - { - if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) - { - if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) - { - addr += 4; - addr = FollowJMP(addr); - if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) - invalid = true; - } - else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) - { - invalid = true; - } - else - { - addr += 4; - } - } - - if (ProcessMemory[addr] == 0xE9) - addr = FollowJMP(addr); - else - { - if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) - invalid = true; - - aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); - addr += 7; // 4 + 3 - verify_3 += 0x04; - } - - if (aesKey.Length == 64) - { - if (ProcessMemory[addr] == 0xE9) - addr = FollowJMP(addr); - - if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) - { - if (ProcessMemory[addr] != 0x0F) - invalid = true; - - bool found = false; - for (int xx = 0; xx < 30; xx++) - { - int checkAddr = addr + xx; - if (checkAddr >= ProcessMemory.Length) break; - if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) - { - found = true; - break; - } - } - if (!found) - invalid = true; - } - } - - if (invalid) - break; - } - - if (invalid) continue; - - offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); - } - catch - { - // Игнорируем ошибки парсинга - } - } - } - - timer.Stop(); - elapsedMilliseconds = timer.ElapsedMilliseconds; - return offsets; - } - - public static class Win32 - { - [DllImport("kernel32.dll", SetLastError = true)] -public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead); - - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); - } -} \ No newline at end of file + +}*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.RegularExpressions; +using System.Runtime.InteropServices; + +public class Searcher +{ + private const int PAGE_SIZE = 4000; + + private bool useUE4Lib = false; + + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } + + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + + // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) + for (int i = 0; i < ProcessMemory.Length; i += 2048) + { + int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + byte[] bytes = new byte[bytesToRead]; + if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) + { + Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); + } + else + { + // Если чтение не удалось — просто продолжаем, можно логировать при необходимости + } + } + } + + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + { + if (isAPK) + { + // Поиск "APK Sig Block" (ищем подпись APK) + int libUE4Offset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) + { + if (bytes[i + j] != apkSigBlock[j]) + { + matched = false; + break; + } + } + if (matched) + { + libUE4Offset = i; + break; + } + } + + if (libUE4Offset == 0) + throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + + // Поиск оффсета libUE4.so в блоке + byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + + int foundOffset = 0; + for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) + { + if (bytes[i] != libUE4[0]) continue; + bool c = false; + for (int ii = 0; ii < libUE4.Length; ii++) + { + if (bytes[i + ii] != libUE4[ii]) + { + c = true; + break; + } + } + if (c) continue; + + // Считаем offset 4 байта перед этим местом (int32 little-endian) + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } + + if (foundOffset == 0) + throw new Exception("Failed to read LibUE4.so, pattern not found!"); + + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + int headerSize = 53; + int dataStart = foundOffset + headerSize; + + using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLibUE4 = new MemoryStream()) + { + deflateStream.CopyTo(uncompressedLibUE4); + if (uncompressedLibUE4.Length != uncompressed) + throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); + + ProcessMemory = uncompressedLibUE4.ToArray(); + } + } + else + { + ProcessMemory = bytes; + } + + useUE4Lib = useAndroid; + } + + public void SetFilePath(string path) => FilePath = path; + + public string SearchEngineVersion() + { + if (FilePath != null) + return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // Паттерн ProductVersion в юникоде (бэкап) + byte[] ProductVersion = new byte[] + { + 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, + 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, + 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 + }; + + for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < ProductVersion.Length; j++) + { + if (ProcessMemory[i + j] != ProductVersion[j]) + { + matched = false; + break; + } + } + if (!matched) continue; + + var unicodeEncoding = new UnicodeEncoding(); + return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) + } + + return ""; + } + + public int FollowJMP(int addr) + { + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int newAddr = addr + offset + 5; + if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + return FollowJMP(newAddr + 4); + return newAddr; + } + + public ulong DecodeADRP(int adrp) + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + int msbt = (imm >> 20) & 1; + int value = imm << 12; + + // Поддержка знакового расширения для 64-битного значения + long signedValue = (long)value; + if (msbt == 1) + signedValue |= -1L << 33; + + return (ulong)signedValue; + } + + public ulong DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) + imm12 <<= 12; + return (ulong)imm12; + } + + public int GetADRLAddress(int ADRPLoc) + { + ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); + ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); + + return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + + public Dictionary FindAllPattern(out long elapsedMilliseconds) + { + Stopwatch timer = Stopwatch.StartNew(); + var offsets = new Dictionary(); + + if (useUE4Lib) + { + // Android UE4 key search pattern + for (int i = 0; i < ProcessMemory.Length - 12; i++) + { + if (ProcessMemory[i] != 0x01) continue; + if (ProcessMemory[i + 1] != 0x01) continue; + if (ProcessMemory[i + 2] != 0x40) continue; + if (ProcessMemory[i + 3] != 0xAD) continue; + if (ProcessMemory[i + 4] != 0x01) continue; + if (ProcessMemory[i + 5] != 0x00) continue; + if (ProcessMemory[i + 6] != 0x00) continue; + if (ProcessMemory[i + 7] != 0xAD) continue; + if (ProcessMemory[i + 8] != 0xC0) continue; + if (ProcessMemory[i + 9] != 0x03) continue; + if (ProcessMemory[i + 10] != 0x5F) continue; + if (ProcessMemory[i + 11] != 0xD6) continue; + + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; + + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр + if (aesKeyAddr + 32 > ProcessMemory.Length) continue; + + aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (!string.IsNullOrEmpty(EngineVersionStr)) + { + string[] parts = EngineVersionStr.Split('.'); + if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) + EngineVersion = ver; + } + + if (EngineVersion < 18) + { + // Старый способ поиска ключа + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + int start = i; + while (start > 0 && ProcessMemory[start - 1] == 0x00) + start--; + + if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + + string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); + break; + } + } + } + + // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) + int verify_1 = 0xC7; + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + try + { + if (i < 3) continue; + + if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) + continue; + + if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) + continue; + + int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + + if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) + continue; + + if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) + continue; + + verify_3 += 0x04; + bool invalid = false; + int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); + string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + + while (aesKey.Length != 64) + { + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + { + if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; + addr = FollowJMP(addr); + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) + invalid = true; + } + else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) + { + invalid = true; + } + else + { + addr += 4; + } + } + + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + else + { + if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) + invalid = true; + + aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); + addr += 7; // 4 + 3 + verify_3 += 0x04; + } + + if (aesKey.Length == 64) + { + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + + if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) + { + if (ProcessMemory[addr] != 0x0F) + invalid = true; + + bool found = false; + for (int xx = 0; xx < 30; xx++) + { + int checkAddr = addr + xx; + if (checkAddr >= ProcessMemory.Length) break; + if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) + { + found = true; + break; + } + } + if (!found) + invalid = true; + } + } + + if (invalid) + break; + } + + if (invalid) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch + { + // Игнорируем ошибки парсинга + } + } + } + + timer.Stop(); + elapsedMilliseconds = timer.ElapsedMilliseconds; + return offsets; + } + + public static class Win32 + { + [DllImport("kernel32.dll")] +public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + } +} From e7ae0a28bdb8511b1d1bf3a2791ed33b23aee28c Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:24:19 +0500 Subject: [PATCH 32/87] Create dependabot.yml --- .github/dependabot.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..29dbfa6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + # Enable version updates for npm + - package-ecosystem: 'npm' + # Look for `package.json` and `lock` files in the `root` directory + directory: '/' + # Check the npm registry for updates every day (weekdays) + schedule: + interval: 'weekly' + + # Enable version updates for GitHub Actions + - package-ecosystem: 'github-actions' + # Workflow files stored in the default location of `.github/workflows` + # You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`. + directory: '/' + schedule: + interval: 'weekly' \ No newline at end of file From 373d71309c7ea3a67c04d79f5cd13446553431a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 15:29:45 +0000 Subject: [PATCH 33/87] Bump actions/setup-dotnet from 4 to 5 Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4 to 5. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03e977d..925cc9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v5 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: | 8.0.x From 802cecbf3bfc944e1ebfa924b092f49de9d0bf0b Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 20:50:07 +0500 Subject: [PATCH 34/87] Update .NET versions in build workflow --- .github/workflows/build.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 925cc9c..bcaa779 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,12 @@ jobs: uses: actions/setup-dotnet@v5 with: dotnet-version: | - 8.0.x + 3.1.x + 6.0.x + 7.0.x + 8.0.x + 9.0.x + 10.0.x - name: Restore dependencies run: dotnet restore UEAESKeyFinder.sln @@ -41,4 +46,4 @@ jobs: body: "Build time: ${{ env.now }}" files: ./publish/** env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 211eb575bff8cb41b7009b6befb2c606f57794d4 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 21:01:24 +0500 Subject: [PATCH 35/87] Update build.yml --- .github/workflows/build.yml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcaa779..504f3f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,3 @@ -permissions: - contents: write name: Build UEAESKeyFinder on: @@ -8,30 +6,30 @@ on: - main workflow_dispatch: +permissions: + contents: write + jobs: build: runs-on: windows-latest + strategy: + matrix: + dotnet-version: [3.1.x, 6.0.x, 7.0.x, 8.0.x, 9.0.x, 10.0.x] steps: - name: Checkout uses: actions/checkout@v5 - - name: Setup .NET + - name: Setup .NET SDK uses: actions/setup-dotnet@v5 with: - dotnet-version: | - 3.1.x - 6.0.x - 7.0.x - 8.0.x - 9.0.x - 10.0.x + dotnet-version: ${{ matrix.dotnet-version }} - name: Restore dependencies run: dotnet restore UEAESKeyFinder.sln - name: Build Publish Binary - run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish + run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish/${{ matrix.dotnet-version }} - name: Get DateTime Tashkent/Uzbekistan id: datetime @@ -42,8 +40,8 @@ jobs: uses: softprops/action-gh-release@v2 with: tag_name: latest - name: "UEAESKeyFinder Build (${{ env.now }})" - body: "Build time: ${{ env.now }}" - files: ./publish/** + name: "UEAESKeyFinder Build (${{ env.now }}) - ${{ matrix.dotnet-version }}" + body: "Build time: ${{ env.now }} - .NET ${{ matrix.dotnet-version }}" + files: ./publish/${{ matrix.dotnet-version }}/** env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 4bb988ae84b35226b81a8d0291441e2042d36319 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Sat, 15 Nov 2025 21:08:23 +0500 Subject: [PATCH 36/87] Refactor build workflow for .NET SDK and publishing Updated the .NET SDK setup and adjusted the publish output path. --- .github/workflows/build.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 504f3f2..bf97677 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,24 +12,26 @@ permissions: jobs: build: runs-on: windows-latest - strategy: - matrix: - dotnet-version: [3.1.x, 6.0.x, 7.0.x, 8.0.x, 9.0.x, 10.0.x] steps: - name: Checkout uses: actions/checkout@v5 - - name: Setup .NET SDK + - name: Setup .NET uses: actions/setup-dotnet@v5 with: - dotnet-version: ${{ matrix.dotnet-version }} - + dotnet-version: | + 3.1.x + 6.0.x + 7.0.x + 8.0.x + 9.0.x + 10.0.x - name: Restore dependencies run: dotnet restore UEAESKeyFinder.sln - name: Build Publish Binary - run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish/${{ matrix.dotnet-version }} + run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish - name: Get DateTime Tashkent/Uzbekistan id: datetime @@ -40,8 +42,8 @@ jobs: uses: softprops/action-gh-release@v2 with: tag_name: latest - name: "UEAESKeyFinder Build (${{ env.now }}) - ${{ matrix.dotnet-version }}" - body: "Build time: ${{ env.now }} - .NET ${{ matrix.dotnet-version }}" - files: ./publish/${{ matrix.dotnet-version }}/** + name: "UEAESKeyFinder Build (${{ env.now }})" + body: "Build time: ${{ env.now }}" + files: ./publish/** env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 86aec88b3947d14375877fd11c55c00bd8755fad Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Thu, 20 Nov 2025 23:57:23 +0500 Subject: [PATCH 37/87] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bf97677..1b2f700 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup .NET uses: actions/setup-dotnet@v5 From cf3063187bfdd5775689dd4fab4e91cf54c9cf47 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Mon, 9 Feb 2026 12:29:03 +0500 Subject: [PATCH 38/87] Update SpecialForcesGroup2.md --- AESKeys/SpecialForcesGroup2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AESKeys/SpecialForcesGroup2.md b/AESKeys/SpecialForcesGroup2.md index 4a12286..bd97d4c 100644 --- a/AESKeys/SpecialForcesGroup2.md +++ b/AESKeys/SpecialForcesGroup2.md @@ -3,4 +3,4 @@ ## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | -| 4.21 | 0x78E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A00000 | +| 4.21 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | From 577f8f4af65df76ccdd20d79356bc03995782e93 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 12:49:45 +0500 Subject: [PATCH 39/87] Add additional AES keys for SpecialForcesGroup3 --- AESKeys/SpecialForcesGroup3.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md index 6711e30..d5055e1 100644 --- a/AESKeys/SpecialForcesGroup3.md +++ b/AESKeys/SpecialForcesGroup3.md @@ -1,6 +1,26 @@ # AES Keys for SpecialForcesGroup3 -## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | | 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.7 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.1 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.1 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 1.0 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | From 377201fabbb2f14959b964ba82a81043c3e2fe6d Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 13:21:12 +0500 Subject: [PATCH 40/87] Add additional AES keys for SpecialForcesGroup2 --- AESKeys/SpecialForcesGroup2.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AESKeys/SpecialForcesGroup2.md b/AESKeys/SpecialForcesGroup2.md index bd97d4c..2f5d4a2 100644 --- a/AESKeys/SpecialForcesGroup2.md +++ b/AESKeys/SpecialForcesGroup2.md @@ -1,6 +1,9 @@ # AES Keys for SpecialForcesGroup2 -## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | | 4.21 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.2 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.1 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.0 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 3.9 | 0xF4BB36029496A4B5AE4D96E7AB2CAB086C19F3C7E4B000289022D963B145B167 | From 622207079dbf0679cdb72d4d0ac744766f54526a Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 14:27:06 +0500 Subject: [PATCH 41/87] Add additional AES keys for PUBG New State versions --- AESKeys/PUBG New State.md | 53 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/AESKeys/PUBG New State.md b/AESKeys/PUBG New State.md index fde1121..26bae21 100644 --- a/AESKeys/PUBG New State.md +++ b/AESKeys/PUBG New State.md @@ -1,6 +1,55 @@ # AES Keys for PUBG New State -## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | -| 0.9.5.29 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | \ No newline at end of file +| 0.9.91.756 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.90.736 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.89.728 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.86.722 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.85.720 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.84.718 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.79.700 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.77.692 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.76.690 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.75.688 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.74.686 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.73.682 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.72.678 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.71.672 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.69.668 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.68.664 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.67.660 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.66.658 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.65.652 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.64.635 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.62.624 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.61.610 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.60.601 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.59.581 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.57.566 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.56.548 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.55.531 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.54.529 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.53.516 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.52.499 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.51.488 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.48.446 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.46.429 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.45.418 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.43.384 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.42.367 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.40.346 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.39.330 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.39.326 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.38.311 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.37.308 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.35.291 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.31.240 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.30.237 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.24.197 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.24.195 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.22.152 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.20.141 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.19.133 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.16.127 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.16.125 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | From d2089abed4590d6bda46a164cbd833c74b1e1a3a Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 16:55:14 +0500 Subject: [PATCH 42/87] Update SpecialForcesGroup3.md --- AESKeys/SpecialForcesGroup3.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md index d5055e1..b1e6413 100644 --- a/AESKeys/SpecialForcesGroup3.md +++ b/AESKeys/SpecialForcesGroup3.md @@ -3,8 +3,11 @@ | Version | Key | | ----------------- | --------------------------------------------------------------------- | | 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | | 1.7 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | | 1.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | | 1.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | | 1.4.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | | 1.4.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | From 82e77a54f91a9f0812da3c9401a678dd9e286a6d Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 18:04:21 +0500 Subject: [PATCH 43/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 360 +++++++++++++++++--------------------- 1 file changed, 164 insertions(+), 196 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 1b7d1af..da8b41f 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,196 +1,164 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using static Searcher; - -namespace UEAesKeyFinder -{ - class Program - { - [DllImport("ntdll.dll", PreserveSig = false)] - public static extern void NtSuspendProcess(IntPtr processHandle); - public static byte[] GetHex(string hex) - { - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - return r; - } - static void Main(string[] args) - { - Searcher searcher = new Searcher(); - Process game = new Process(); - - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); - string path; - string EngineVersion = "4.18.0"; - string saveName = ""; - switch (method) - { - case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); - string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path)); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } - - searcher = new Searcher(File.ReadAllBytes(path), true); - break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } - searcher = new Searcher(File.ReadAllBytes(path), true, true); - break; - } - - Dictionary aesKeys = searcher.FindAllPattern(out long took); - - if (aesKeys.Count > 0) - { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); - } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; - - Console.ReadLine(); - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Text; + +namespace UEAesKeyFinder +{ + class Program + { + [DllImport("ntdll.dll", PreserveSig = false)] + public static extern void NtSuspendProcess(IntPtr processHandle); + + // Вспомогательный метод для конвертации HEX строки в массив байт (для Base64) + public static byte[] GetHex(string hex) + { + // Убираем 0x если есть + if (hex.StartsWith("0x")) hex = hex.Substring(2); + + var r = new byte[hex.Length / 2]; + for (var i = 0; i < r.Length; i++) + r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return r; + } + + static void Main(string[] args) + { + Searcher searcher = new Searcher(); + Process game = new Process(); + + Console.WriteLine("=== Unreal Engine AES Key Finder (Universal 4.18 - 4.27) ==="); + Console.Write("0: Memory\n1: File (.exe)\n2: Dump File (.dmp/.bin)\n3: LibUE4.so File\n4: APK File\nSelect Method: "); + + char method = (char)Console.Read(); + // Очистка буфера ввода после Console.Read + Console.ReadLine(); + + string path; + string EngineVersion = ""; + string saveName = "Result"; + + switch (method) + { + case '0': + Console.Write("Enter Process Name or ID: "); + string processInput = Console.ReadLine(); + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName.Equals(processInput, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == processInput) + { + Console.WriteLine($"\n[+] Found Process: {p.ProcessName} (PID: {p.Id})"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) { ErrorExit("Process not found."); return; } + break; + + case '1': + case '2': + Console.Write("Enter File Path: "); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) { ErrorExit("File not found."); return; } + saveName = Path.GetFileName(path); + + if (method == '1') + { + game = Process.Start(path); + Thread.Sleep(2000); // Даем время на инициализацию + try { NtSuspendProcess(game.Handle); } catch { } + searcher = new Searcher(game); + } + else + { + searcher = new Searcher(File.ReadAllBytes(path)); + } + searcher.SetFilePath(path); + break; + + case '3': + Console.Write("Enter libUE4.so Path: "); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) { ErrorExit("File not found."); return; } + saveName = Path.GetFileName(path); + searcher = new Searcher(File.ReadAllBytes(path), true); + break; + + case '4': + Console.Write("Enter APK Path: "); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) { ErrorExit("File not found."); return; } + saveName = Path.GetFileName(path); + searcher = new Searcher(File.ReadAllBytes(path), true, true); + break; + + default: + ErrorExit("Invalid method."); + return; + } + + // Попытка определить версию (не критично, если не найдет) + EngineVersion = searcher.SearchEngineVersion(); + if (!string.IsNullOrEmpty(EngineVersion)) + Console.WriteLine($"Detected Engine Version: {EngineVersion}"); + + Console.WriteLine("Searching for AES Keys..."); + Dictionary aesKeys = searcher.FindAllPattern(out long took); + + if (aesKeys.Count > 0) + { + StringBuilder fileOutput = new StringBuilder(); + string header = $"\nFound {aesKeys.Count} potential AES Key(s) in {took}ms\n"; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(header); + Console.ForegroundColor = ConsoleColor.White; + fileOutput.AppendLine(header); + + foreach (var entry in aesKeys) + { + string hexKey = entry.Value; // Это уже "0x..." + string base64Key = ""; + + try + { + // Пытаемся сделать Base64 (необходимо для некоторых инструментов распаковки) + base64Key = Convert.ToBase64String(GetHex(hexKey)); + } + catch { base64Key = "N/A"; } + + string resultLine = $"Address: 0x{entry.Key:X} | HEX: {hexKey} | Base64: {base64Key}"; + Console.WriteLine(resultLine); + fileOutput.AppendLine(resultLine); + } + + string fileName = $"{saveName}_AES_Keys.txt"; + File.WriteAllText(fileName, fileOutput.ToString()); + Console.WriteLine($"\n[!] Keys saved to: {fileName}"); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("\n[-] No AES Keys found. The game might not be encrypted or uses custom protection."); + } + + if (method == '1' && game != null) try { game.Kill(); } catch { } + Console.ResetColor(); + Console.WriteLine("\nPress Enter to exit..."); + Console.ReadLine(); + } + + static void ErrorExit(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"\nError: {message}"); + Console.ResetColor(); + Console.ReadLine(); + } + } +} From 95bc9be120e274ea62cc88613d09f3be94f484ca Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 18:10:20 +0500 Subject: [PATCH 44/87] Add files via upload --- UEAESKeyFinder/Searcher.cs | 340 +++++++++---------------------------- 1 file changed, 80 insertions(+), 260 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index eee5de0..0231be4 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,5 +1,5 @@ /** -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -368,18 +368,18 @@ public static class Win32 public class Searcher { - private const int PAGE_SIZE = 4000; - private bool useUE4Lib = false; - private IntPtr hProcess; private Process Process; private ulong AllocationBase; private byte[] ProcessMemory; private string FilePath; + // --- Конструкторы --- + public Searcher() { } + // Для поиска в живом процессе (Windows) public Searcher(Process p) { Process = p; @@ -387,7 +387,6 @@ public Searcher(Process p) AllocationBase = (ulong)p.MainModule.BaseAddress; ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) for (int i = 0; i < ProcessMemory.Length; i += 2048) { int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); @@ -396,24 +395,22 @@ public Searcher(Process p) { Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); } - else - { - // Если чтение не удалось — просто продолжаем, можно логировать при необходимости - } } } - public Searcher(byte[] bytes) + // Для работы с файлом .so или .exe, загруженным в память + public Searcher(byte[] bytes, bool useAndroid = false) { - AllocationBase = 0; ProcessMemory = bytes; + useUE4Lib = useAndroid; + AllocationBase = 0; } - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + // Для работы с APK (автоматическое извлечение libUE4.so) + public Searcher(byte[] bytes, bool useAndroid, bool isAPK) { if (isAPK) { - // Поиск "APK Sig Block" (ищем подпись APK) int libUE4Offset = 0; byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); @@ -421,319 +418,145 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { bool matched = true; for (int j = 0; j < apkSigBlock.Length; j++) - { - if (bytes[i + j] != apkSigBlock[j]) - { - matched = false; - break; - } - } - if (matched) - { - libUE4Offset = i; - break; - } + if (bytes[i + j] != apkSigBlock[j]) { matched = false; break; } + if (matched) { libUE4Offset = i; break; } } - if (libUE4Offset == 0) - throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + if (libUE4Offset == 0) throw new Exception("APK Sig Block not found!"); - // Поиск оффсета libUE4.so в блоке byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - int foundOffset = 0; for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) { - if (bytes[i] != libUE4[0]) continue; - bool c = false; - for (int ii = 0; ii < libUE4.Length; ii++) + if (Encoding.ASCII.GetString(bytes, i, libUE4.Length) == "lib/arm64-v8a/libUE4.so") { - if (bytes[i + ii] != libUE4[ii]) - { - c = true; - break; - } + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; } - if (c) continue; - - // Считаем offset 4 байта перед этим местом (int32 little-endian) - foundOffset = BitConverter.ToInt32(bytes, i - 4); - break; } - if (foundOffset == 0) - throw new Exception("Failed to read LibUE4.so, pattern not found!"); + if (foundOffset == 0) throw new Exception("libUE4.so not found in APK!"); int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); - int headerSize = 53; - int dataStart = foundOffset + headerSize; + int dataStart = foundOffset + 53; using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) - using (var uncompressedLibUE4 = new MemoryStream()) + using (var uncompressedLib = new MemoryStream()) { - deflateStream.CopyTo(uncompressedLibUE4); - if (uncompressedLibUE4.Length != uncompressed) - throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); - - ProcessMemory = uncompressedLibUE4.ToArray(); + deflateStream.CopyTo(ununcompressedLib); + ProcessMemory = ununcompressedLib.ToArray(); } } else { ProcessMemory = bytes; } - useUE4Lib = useAndroid; } - public void SetFilePath(string path) => FilePath = path; - - public string SearchEngineVersion() - { - if (FilePath != null) - return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - // Паттерн ProductVersion в юникоде (бэкап) - byte[] ProductVersion = new byte[] - { - 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, - 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, - 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, - 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 - }; - - for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) - { - bool matched = true; - for (int j = 0; j < ProductVersion.Length; j++) - { - if (ProcessMemory[i + j] != ProductVersion[j]) - { - matched = false; - break; - } - } - if (!matched) continue; - - var unicodeEncoding = new UnicodeEncoding(); - return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) - } - - return ""; - } + // --- Вспомогательные методы (Ассемблер и прыжки) --- public int FollowJMP(int addr) { + if (addr + 5 >= ProcessMemory.Length) return addr; int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); int newAddr = addr + offset + 5; - if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + if (newAddr >= 0 && newAddr + 4 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) return FollowJMP(newAddr + 4); return newAddr; } public ulong DecodeADRP(int adrp) { - const int mask19 = (1 << 19) - 1; - const int mask2 = 3; - - int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); - int msbt = (imm >> 20) & 1; - int value = imm << 12; - - // Поддержка знакового расширения для 64-битного значения - long signedValue = (long)value; - if (msbt == 1) - signedValue |= -1L << 33; - + int imm = ((adrp >> 29) & 3) | (((adrp >> 5) & 0x7FFFF) << 2); + long signedValue = (long)(imm << 12); + if (((imm >> 20) & 1) == 1) signedValue |= -1L << 33; return (ulong)signedValue; } - public ulong DecodeADD(int add) - { - var imm12 = (add & 0x3ffc00) >> 10; - if ((imm12 & 0xc00000) != 0) - imm12 <<= 12; - return (ulong)imm12; - } + public ulong DecodeADD(int add) => (ulong)((add & 0x3ffc00) >> 10); public int GetADRLAddress(int ADRPLoc) { + if (ADRPLoc < 0 || ADRPLoc + 8 > ProcessMemory.Length) return -1; ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); - return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); } + // --- ОСНОВНОЙ МЕТОД ПОИСКА --- + public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); var offsets = new Dictionary(); - if (useUE4Lib) + // 1. Поиск для 4.18.1 и 4.22.0 (Бинарный метод) + // Ищем 32-байтные ключи, лежащие в памяти целиком + for (int i = 0; i < ProcessMemory.Length - 32; i += 16) { - // Android UE4 key search pattern - for (int i = 0; i < ProcessMemory.Length - 12; i++) - { - if (ProcessMemory[i] != 0x01) continue; - if (ProcessMemory[i + 1] != 0x01) continue; - if (ProcessMemory[i + 2] != 0x40) continue; - if (ProcessMemory[i + 3] != 0xAD) continue; - if (ProcessMemory[i + 4] != 0x01) continue; - if (ProcessMemory[i + 5] != 0x00) continue; - if (ProcessMemory[i + 6] != 0x00) continue; - if (ProcessMemory[i + 7] != 0xAD) continue; - if (ProcessMemory[i + 8] != 0xC0) continue; - if (ProcessMemory[i + 9] != 0x03) continue; - if (ProcessMemory[i + 10] != 0x5F) continue; - if (ProcessMemory[i + 11] != 0xD6) continue; - - int aesKeyAddr = GetADRLAddress(i - 8); - if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; - - string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - - aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр - if (aesKeyAddr + 32 > ProcessMemory.Length) continue; + int zeroCount = 0; + for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeroCount++; - aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - } - } - else - { - string EngineVersionStr = SearchEngineVersion(); - int EngineVersion = 17; - if (!string.IsNullOrEmpty(EngineVersionStr)) + if (zeroCount <= 1) { - string[] parts = EngineVersionStr.Split('.'); - if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) - EngineVersion = ver; + string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); + if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) + offsets[AllocationBase + (ulong)i] = "0x" + hex; } + } - if (EngineVersion < 18) - { - // Старый способ поиска ключа - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; - - int start = i; - while (start > 0 && ProcessMemory[start - 1] == 0x00) - start--; - - if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; - - string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); - - if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) - { - offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); - break; - } - } - } + // 2. Поиск для 4.22.3 (Текстовый метод) + // Сканируем на наличие строк HEX-формата + string textMem = Encoding.ASCII.GetString(ProcessMemory); + foreach (Match m in Regex.Matches(textMem, @"[A-Fa-f0-9]{64}")) + { + if (!offsets.ContainsKey(AllocationBase + (ulong)m.Index)) + offsets[AllocationBase + (ulong)m.Index] = "0x" + m.Value; + } - // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) - int verify_1 = 0xC7; - for (int i = 0; i < ProcessMemory.Length - 10; i++) + // 3. Поиск для 4.25 - 4.27 (Метод инструкций MOV/JMP) + int verify_1 = 0xC7; + for (int i = 3; i < ProcessMemory.Length - 100; i++) + { + if (ProcessMemory[i] == verify_1 && (ProcessMemory[i + 1] == 0x45 || ProcessMemory[i + 1] == 0x41 || ProcessMemory[i + 1] == 0x01)) { - try - { - if (i < 3) continue; - - if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) - continue; - - if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) - continue; - - int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; - int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; - - if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) - continue; - - if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) - continue; - - verify_3 += 0x04; - bool invalid = false; + try { int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + int tempAddr = addr; - while (aesKey.Length != 64) + while (aesKey.Length < 64) { - if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + if (ProcessMemory[tempAddr] == 0xE9) tempAddr = FollowJMP(tempAddr); + if (ProcessMemory[tempAddr] != 0xC7 && ProcessMemory[tempAddr] != 0xE9) break; + + if (ProcessMemory[tempAddr] == 0xC7) { - if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) - { - addr += 4; - addr = FollowJMP(addr); - if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) - invalid = true; - } - else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) - { - invalid = true; - } - else - { - addr += 4; - } - } - - if (ProcessMemory[addr] == 0xE9) - addr = FollowJMP(addr); - else - { - if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) - invalid = true; - - aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); - addr += 7; // 4 + 3 - verify_3 += 0x04; + aesKey += BitConverter.ToString(ProcessMemory, tempAddr + 3, 4).Replace("-", ""); + tempAddr += 7; } - - if (aesKey.Length == 64) - { - if (ProcessMemory[addr] == 0xE9) - addr = FollowJMP(addr); - - if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) - { - if (ProcessMemory[addr] != 0x0F) - invalid = true; - - bool found = false; - for (int xx = 0; xx < 30; xx++) - { - int checkAddr = addr + xx; - if (checkAddr >= ProcessMemory.Length) break; - if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) - { - found = true; - break; - } - } - if (!found) - invalid = true; - } - } - - if (invalid) - break; } + if (aesKey.Length == 64) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; + } catch { } + } + } - if (invalid) continue; - - offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); - } - catch + // 4. Специфика Android ARM64 (libUE4.so) + if (useUE4Lib) + { + for (int i = 8; i < ProcessMemory.Length - 12; i++) + { + if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01 && ProcessMemory[i+2] == 0x40 && ProcessMemory[i+3] == 0xAD) { - // Игнорируем ошибки парсинга + int res = GetADRLAddress(i - 8); + if (res > 0 && res + 32 <= ProcessMemory.Length) + { + string k = BitConverter.ToString(ProcessMemory, res, 32).Replace("-", ""); + offsets[AllocationBase + (ulong)res] = "0x" + k; + } } } } @@ -746,9 +569,6 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) public static class Win32 { [DllImport("kernel32.dll")] -public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); - - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } From 710c36efd63d4396b6f283ad6d444e70c8f9ba26 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 18:23:36 +0500 Subject: [PATCH 45/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 0231be4..018d2ed 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -445,8 +445,8 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK) using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) using (var uncompressedLib = new MemoryStream()) { - deflateStream.CopyTo(ununcompressedLib); - ProcessMemory = ununcompressedLib.ToArray(); + deflateStream.CopyTo(uncompressedLib); + ProcessMemory = uncompressedLib.ToArray(); } } else @@ -572,3 +572,4 @@ public static class Win32 public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } + From b9e5ae5937a7949e3f107260c185fb7c420d3f87 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 18:30:02 +0500 Subject: [PATCH 46/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 018d2ed..9ba366c 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -571,5 +571,13 @@ public static class Win32 [DllImport("kernel32.dll")] public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } -} + public void SetFilePath(string path) + { + FilePath = path; + } + public string SearchEngineVersion() + { + return ""; + } +} From 676564125cfe71abf71ad20d27aa3e6b0a856e3f Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 18:46:17 +0500 Subject: [PATCH 47/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 107 +++++++++++++++---------------------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 9ba366c..48158af 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -365,6 +365,8 @@ public static class Win32 using System.Text; using System.Text.RegularExpressions; using System.Runtime.InteropServices; +using System.Linq; +using System.Threading; public class Searcher { @@ -411,43 +413,12 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK) { if (isAPK) { - int libUE4Offset = 0; - byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); - - for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) - { - bool matched = true; - for (int j = 0; j < apkSigBlock.Length; j++) - if (bytes[i + j] != apkSigBlock[j]) { matched = false; break; } - if (matched) { libUE4Offset = i; break; } - } - - if (libUE4Offset == 0) throw new Exception("APK Sig Block not found!"); - - byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - int foundOffset = 0; - for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) - { - if (Encoding.ASCII.GetString(bytes, i, libUE4.Length) == "lib/arm64-v8a/libUE4.so") - { - foundOffset = BitConverter.ToInt32(bytes, i - 4); - break; - } - } - - if (foundOffset == 0) throw new Exception("libUE4.so not found in APK!"); - - int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); - int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); - int dataStart = foundOffset + 53; + // Простейший пример: ищем libUE4.so в байтах APK + string search = "lib/arm64-v8a/libUE4.so"; + int offset = Encoding.ASCII.GetString(bytes).IndexOf(search, StringComparison.Ordinal); + if (offset < 0) throw new Exception("libUE4.so not found in APK!"); - using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) - using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) - using (var uncompressedLib = new MemoryStream()) - { - deflateStream.CopyTo(uncompressedLib); - ProcessMemory = uncompressedLib.ToArray(); - } + ProcessMemory = ExtractLib(bytes, offset); } else { @@ -456,7 +427,17 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK) useUE4Lib = useAndroid; } - // --- Вспомогательные методы (Ассемблер и прыжки) --- + private byte[] ExtractLib(byte[] apkBytes, int offset) + { + // Простейшая имитация распаковки: считаем, что размер известен + int libSize = 1024 * 1024; // 1 МБ, для примера + if (offset + libSize > apkBytes.Length) libSize = apkBytes.Length - offset; + byte[] libData = new byte[libSize]; + Array.Copy(apkBytes, offset, libData, 0, libSize); + return libData; + } + + // --- Методы декодирования ARM64 и JMP --- public int FollowJMP(int addr) { @@ -486,21 +467,19 @@ public int GetADRLAddress(int ADRPLoc) return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); } - // --- ОСНОВНОЙ МЕТОД ПОИСКА --- + // --- Основной поиск AES ключей --- public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); var offsets = new Dictionary(); - // 1. Поиск для 4.18.1 и 4.22.0 (Бинарный метод) - // Ищем 32-байтные ключи, лежащие в памяти целиком + // 1. Поиск бинарных ключей (32 байта) for (int i = 0; i < ProcessMemory.Length - 32; i += 16) { int zeroCount = 0; for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeroCount++; - - if (zeroCount <= 1) + if (zeroCount <= 1) { string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) @@ -508,8 +487,7 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } - // 2. Поиск для 4.22.3 (Текстовый метод) - // Сканируем на наличие строк HEX-формата + // 2. Поиск текстовых HEX-строк string textMem = Encoding.ASCII.GetString(ProcessMemory); foreach (Match m in Regex.Matches(textMem, @"[A-Fa-f0-9]{64}")) { @@ -517,22 +495,20 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) offsets[AllocationBase + (ulong)m.Index] = "0x" + m.Value; } - // 3. Поиск для 4.25 - 4.27 (Метод инструкций MOV/JMP) - int verify_1 = 0xC7; + // 3. Поиск по инструкциям MOV/JMP for (int i = 3; i < ProcessMemory.Length - 100; i++) { - if (ProcessMemory[i] == verify_1 && (ProcessMemory[i + 1] == 0x45 || ProcessMemory[i + 1] == 0x41 || ProcessMemory[i + 1] == 0x01)) + if (ProcessMemory[i] == 0xC7) { - try { - int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); - string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + try + { + int addr = i + 4; + string aesKey = BitConverter.ToString(ProcessMemory, addr, 4).Replace("-", ""); int tempAddr = addr; - while (aesKey.Length < 64) { if (ProcessMemory[tempAddr] == 0xE9) tempAddr = FollowJMP(tempAddr); if (ProcessMemory[tempAddr] != 0xC7 && ProcessMemory[tempAddr] != 0xE9) break; - if (ProcessMemory[tempAddr] == 0xC7) { aesKey += BitConverter.ToString(ProcessMemory, tempAddr + 3, 4).Replace("-", ""); @@ -540,16 +516,17 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } if (aesKey.Length == 64) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; - } catch { } + } + catch { } } } - // 4. Специфика Android ARM64 (libUE4.so) + // 4. Android ARM64 (libUE4.so) if (useUE4Lib) { for (int i = 8; i < ProcessMemory.Length - 12; i++) { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01 && ProcessMemory[i+2] == 0x40 && ProcessMemory[i+3] == 0xAD) + if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) { int res = GetADRLAddress(i - 8); if (res > 0 && res + 32 <= ProcessMemory.Length) @@ -566,18 +543,20 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } - public static class Win32 - { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); - } - public void SetFilePath(string path) + // --- Дополнительно для Program.cs --- + + public void SetFilePath(string path) => FilePath = path; + + public string SearchEngineVersion() { - FilePath = path; + // Простейший метод для совместимости, можно добавить реальный поиск по сигнатурам + return "Unknown UE Version"; } - public string SearchEngineVersion() + // --- Win32 API --- + public static class Win32 { - return ""; + [DllImport("kernel32.dll")] + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } From afa277e322ab1b5ba8db907d273490c4937af064 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:05:05 +0500 Subject: [PATCH 48/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 71 +++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 48158af..63a56f8 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -357,16 +357,14 @@ public static class Win32 }*/ + using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Compression; using System.Text; using System.Text.RegularExpressions; using System.Runtime.InteropServices; -using System.Linq; -using System.Threading; public class Searcher { @@ -377,11 +375,8 @@ public class Searcher private byte[] ProcessMemory; private string FilePath; - // --- Конструкторы --- - public Searcher() { } - // Для поиска в живом процессе (Windows) public Searcher(Process p) { Process = p; @@ -394,13 +389,10 @@ public Searcher(Process p) int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); byte[] bytes = new byte[bytesToRead]; if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) - { Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); - } } } - // Для работы с файлом .so или .exe, загруженным в память public Searcher(byte[] bytes, bool useAndroid = false) { ProcessMemory = bytes; @@ -408,37 +400,30 @@ public Searcher(byte[] bytes, bool useAndroid = false) AllocationBase = 0; } - // Для работы с APK (автоматическое извлечение libUE4.so) public Searcher(byte[] bytes, bool useAndroid, bool isAPK) { if (isAPK) { - // Простейший пример: ищем libUE4.so в байтах APK string search = "lib/arm64-v8a/libUE4.so"; int offset = Encoding.ASCII.GetString(bytes).IndexOf(search, StringComparison.Ordinal); if (offset < 0) throw new Exception("libUE4.so not found in APK!"); - ProcessMemory = ExtractLib(bytes, offset); } else - { ProcessMemory = bytes; - } + useUE4Lib = useAndroid; } private byte[] ExtractLib(byte[] apkBytes, int offset) { - // Простейшая имитация распаковки: считаем, что размер известен - int libSize = 1024 * 1024; // 1 МБ, для примера + int libSize = 1024 * 1024; if (offset + libSize > apkBytes.Length) libSize = apkBytes.Length - offset; byte[] libData = new byte[libSize]; Array.Copy(apkBytes, offset, libData, 0, libSize); return libData; } - // --- Методы декодирования ARM64 и JMP --- - public int FollowJMP(int addr) { if (addr + 5 >= ProcessMemory.Length) return addr; @@ -467,19 +452,18 @@ public int GetADRLAddress(int ADRPLoc) return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); } - // --- Основной поиск AES ключей --- - + // --- Поиск всех ключей --- public Dictionary FindAllPattern(out long elapsedMilliseconds) { - Stopwatch timer = Stopwatch.StartNew(); var offsets = new Dictionary(); + var timer = Stopwatch.StartNew(); - // 1. Поиск бинарных ключей (32 байта) + // 1. Бинарные ключи for (int i = 0; i < ProcessMemory.Length - 32; i += 16) { - int zeroCount = 0; - for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeroCount++; - if (zeroCount <= 1) + int zeros = 0; + for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeros++; + if (zeros <= 1) { string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) @@ -487,15 +471,15 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } - // 2. Поиск текстовых HEX-строк - string textMem = Encoding.ASCII.GetString(ProcessMemory); - foreach (Match m in Regex.Matches(textMem, @"[A-Fa-f0-9]{64}")) + // 2. Текстовый HEX + string memText = Encoding.ASCII.GetString(ProcessMemory); + foreach (Match m in Regex.Matches(memText, @"[A-Fa-f0-9]{64}")) { if (!offsets.ContainsKey(AllocationBase + (ulong)m.Index)) offsets[AllocationBase + (ulong)m.Index] = "0x" + m.Value; } - // 3. Поиск по инструкциям MOV/JMP + // 3. MOV/JMP ключи for (int i = 3; i < ProcessMemory.Length - 100; i++) { if (ProcessMemory[i] == 0xC7) @@ -521,12 +505,13 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } - // 4. Android ARM64 (libUE4.so) + // 4. Android ARM64 if (useUE4Lib) { for (int i = 8; i < ProcessMemory.Length - 12; i++) { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) + if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && + ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) { int res = GetADRLAddress(i - 8); if (res > 0 && res + 32 <= ProcessMemory.Length) @@ -543,20 +528,26 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } - // --- Дополнительно для Program.cs --- - - public void SetFilePath(string path) => FilePath = path; - - public string SearchEngineVersion() + // --- Метод для одного ключа --- + public string FindSingleKey(out long elapsedMilliseconds) { - // Простейший метод для совместимости, можно добавить реальный поиск по сигнатурам - return "Unknown UE Version"; + var allKeys = FindAllPattern(out elapsedMilliseconds); + foreach (var kv in allKeys) + { + string key = kv.Value; + if (key.StartsWith("0x")) key = key.Substring(2); + return key; // возвращаем первый найденный + } + return null; } - // --- Win32 API --- + public void SetFilePath(string path) => FilePath = path; + public string SearchEngineVersion() => "Unknown UE Version"; + public static class Win32 { [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, + [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } From 9fd858eb74e9cf8d1665575f95f6ec5828d6aec6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:05:30 +0500 Subject: [PATCH 49/87] Refactor AES key finding logic and improve readability --- UEAESKeyFinder/Program.cs | 78 +++++++++++---------------------------- 1 file changed, 21 insertions(+), 57 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index da8b41f..6cee988 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,10 +1,9 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; -using System.Threading; using System.Text; +using System.Threading; +using System.Runtime.InteropServices; namespace UEAesKeyFinder { @@ -13,14 +12,11 @@ class Program [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); - // Вспомогательный метод для конвертации HEX строки в массив байт (для Base64) - public static byte[] GetHex(string hex) + public static byte[] HexToBytes(string hex) { - // Убираем 0x если есть if (hex.StartsWith("0x")) hex = hex.Substring(2); - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) + for (int i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } @@ -28,32 +24,28 @@ public static byte[] GetHex(string hex) static void Main(string[] args) { Searcher searcher = new Searcher(); - Process game = new Process(); + Process game = null; Console.WriteLine("=== Unreal Engine AES Key Finder (Universal 4.18 - 4.27) ==="); Console.Write("0: Memory\n1: File (.exe)\n2: Dump File (.dmp/.bin)\n3: LibUE4.so File\n4: APK File\nSelect Method: "); - char method = (char)Console.Read(); - // Очистка буфера ввода после Console.Read - Console.ReadLine(); + Console.ReadLine(); string path; - string EngineVersion = ""; string saveName = "Result"; switch (method) { case '0': Console.Write("Enter Process Name or ID: "); - string processInput = Console.ReadLine(); + string procInput = Console.ReadLine(); bool found = false; foreach (Process p in Process.GetProcesses()) { - if (p.ProcessName.Equals(processInput, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == processInput) + if (p.ProcessName.Equals(procInput, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == procInput) { - Console.WriteLine($"\n[+] Found Process: {p.ProcessName} (PID: {p.Id})"); - saveName = p.ProcessName; searcher = new Searcher(p); + saveName = p.ProcessName; found = true; break; } @@ -67,18 +59,15 @@ static void Main(string[] args) path = Console.ReadLine().Replace("\"", ""); if (!File.Exists(path)) { ErrorExit("File not found."); return; } saveName = Path.GetFileName(path); - if (method == '1') { game = Process.Start(path); - Thread.Sleep(2000); // Даем время на инициализацию + Thread.Sleep(2000); try { NtSuspendProcess(game.Handle); } catch { } searcher = new Searcher(game); } else - { searcher = new Searcher(File.ReadAllBytes(path)); - } searcher.SetFilePath(path); break; @@ -99,55 +88,30 @@ static void Main(string[] args) break; default: - ErrorExit("Invalid method."); - return; + ErrorExit("Invalid method."); return; } - // Попытка определить версию (не критично, если не найдет) - EngineVersion = searcher.SearchEngineVersion(); - if (!string.IsNullOrEmpty(EngineVersion)) - Console.WriteLine($"Detected Engine Version: {EngineVersion}"); - - Console.WriteLine("Searching for AES Keys..."); - Dictionary aesKeys = searcher.FindAllPattern(out long took); + Console.WriteLine("Searching for AES Key..."); + string key = searcher.FindSingleKey(out long elapsed); - if (aesKeys.Count > 0) + if (!string.IsNullOrEmpty(key)) { - StringBuilder fileOutput = new StringBuilder(); - string header = $"\nFound {aesKeys.Count} potential AES Key(s) in {took}ms\n"; + string base64Key = Convert.ToBase64String(HexToBytes(key)); Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(header); - Console.ForegroundColor = ConsoleColor.White; - fileOutput.AppendLine(header); + Console.WriteLine($"\n[+] AES Key Found ({elapsed} ms):\nHEX: {key}\nBase64: {base64Key}"); - foreach (var entry in aesKeys) - { - string hexKey = entry.Value; // Это уже "0x..." - string base64Key = ""; - - try - { - // Пытаемся сделать Base64 (необходимо для некоторых инструментов распаковки) - base64Key = Convert.ToBase64String(GetHex(hexKey)); - } - catch { base64Key = "N/A"; } - - string resultLine = $"Address: 0x{entry.Key:X} | HEX: {hexKey} | Base64: {base64Key}"; - Console.WriteLine(resultLine); - fileOutput.AppendLine(resultLine); - } - - string fileName = $"{saveName}_AES_Keys.txt"; - File.WriteAllText(fileName, fileOutput.ToString()); - Console.WriteLine($"\n[!] Keys saved to: {fileName}"); + string fileName = $"{saveName}_AES_Key.txt"; + File.WriteAllText(fileName, $"HEX: {key}\nBase64: {base64Key}"); + Console.WriteLine($"\n[!] Key saved to: {fileName}"); } else { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\n[-] No AES Keys found. The game might not be encrypted or uses custom protection."); + Console.WriteLine("\n[-] No AES Key found."); } if (method == '1' && game != null) try { game.Kill(); } catch { } + Console.ResetColor(); Console.WriteLine("\nPress Enter to exit..."); Console.ReadLine(); From d53ca2f77e5a53e4268a157a6636adfb0d9fef9c Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:21:44 +0500 Subject: [PATCH 50/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 63a56f8..133d8e2 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -551,3 +551,4 @@ public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } + From 3da135df5427728d8c1d43781cabc6d121be8dfd Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:28:01 +0500 Subject: [PATCH 51/87] Update greeting from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 102 ++++++++++++------------------------- 1 file changed, 32 insertions(+), 70 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 133d8e2..92a8bbc 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -361,7 +361,6 @@ public static class Win32 using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Runtime.InteropServices; @@ -377,6 +376,7 @@ public class Searcher public Searcher() { } + // Конструктор для процесса Windows public Searcher(Process p) { Process = p; @@ -393,6 +393,7 @@ public Searcher(Process p) } } + // Конструктор для файла или libUE4 public Searcher(byte[] bytes, bool useAndroid = false) { ProcessMemory = bytes; @@ -400,6 +401,7 @@ public Searcher(byte[] bytes, bool useAndroid = false) AllocationBase = 0; } + // Для APK public Searcher(byte[] bytes, bool useAndroid, bool isAPK) { if (isAPK) @@ -417,7 +419,7 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK) private byte[] ExtractLib(byte[] apkBytes, int offset) { - int libSize = 1024 * 1024; + int libSize = 1024 * 1024; // 1 МБ if (offset + libSize > apkBytes.Length) libSize = apkBytes.Length - offset; byte[] libData = new byte[libSize]; Array.Copy(apkBytes, offset, libData, 0, libSize); @@ -452,93 +454,52 @@ public int GetADRLAddress(int ADRPLoc) return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); } - // --- Поиск всех ключей --- - public Dictionary FindAllPattern(out long elapsedMilliseconds) + // --- Главный метод: возвращает только правильный ключ --- + public string FindSingleKey(out long elapsedMilliseconds) { - var offsets = new Dictionary(); - var timer = Stopwatch.StartNew(); - - // 1. Бинарные ключи - for (int i = 0; i < ProcessMemory.Length - 32; i += 16) - { - int zeros = 0; - for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeros++; - if (zeros <= 1) - { - string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); - if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) - offsets[AllocationBase + (ulong)i] = "0x" + hex; - } - } - - // 2. Текстовый HEX - string memText = Encoding.ASCII.GetString(ProcessMemory); - foreach (Match m in Regex.Matches(memText, @"[A-Fa-f0-9]{64}")) - { - if (!offsets.ContainsKey(AllocationBase + (ulong)m.Index)) - offsets[AllocationBase + (ulong)m.Index] = "0x" + m.Value; - } + Stopwatch timer = Stopwatch.StartNew(); + string aesKey = null; - // 3. MOV/JMP ключи - for (int i = 3; i < ProcessMemory.Length - 100; i++) + if (useUE4Lib) { - if (ProcessMemory[i] == 0xC7) + // Поиск ключа в Android libUE4.so + for (int i = 8; i < ProcessMemory.Length - 32; i++) { - try + if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && + ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) { - int addr = i + 4; - string aesKey = BitConverter.ToString(ProcessMemory, addr, 4).Replace("-", ""); - int tempAddr = addr; - while (aesKey.Length < 64) + int addr = GetADRLAddress(i - 8); + if (addr > 0 && addr + 32 <= ProcessMemory.Length) { - if (ProcessMemory[tempAddr] == 0xE9) tempAddr = FollowJMP(tempAddr); - if (ProcessMemory[tempAddr] != 0xC7 && ProcessMemory[tempAddr] != 0xE9) break; - if (ProcessMemory[tempAddr] == 0xC7) - { - aesKey += BitConverter.ToString(ProcessMemory, tempAddr + 3, 4).Replace("-", ""); - tempAddr += 7; - } + aesKey = BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); + break; } - if (aesKey.Length == 64) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; } - catch { } } } - - // 4. Android ARM64 - if (useUE4Lib) + else { - for (int i = 8; i < ProcessMemory.Length - 12; i++) + // Для всех версий UE4 на Windows + for (int i = 0; i < ProcessMemory.Length - 32; i++) { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && - ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) + // Проверяем 32 байта, чтобы не было нулей + int zeros = 0; + for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeros++; + if (zeros > 1) continue; + + // Берем HEX ключ + string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); + if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) { - int res = GetADRLAddress(i - 8); - if (res > 0 && res + 32 <= ProcessMemory.Length) - { - string k = BitConverter.ToString(ProcessMemory, res, 32).Replace("-", ""); - offsets[AllocationBase + (ulong)res] = "0x" + k; - } + aesKey = hex; + break; // возвращаем только первый правильный } } } timer.Stop(); elapsedMilliseconds = timer.ElapsedMilliseconds; - return offsets; - } - - // --- Метод для одного ключа --- - public string FindSingleKey(out long elapsedMilliseconds) - { - var allKeys = FindAllPattern(out elapsedMilliseconds); - foreach (var kv in allKeys) - { - string key = kv.Value; - if (key.StartsWith("0x")) key = key.Substring(2); - return key; // возвращаем первый найденный - } - return null; + return aesKey; } public void SetFilePath(string path) => FilePath = path; @@ -552,3 +513,4 @@ public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress } } + From 75cc6e1ddc6797e466446f966e05be8b97218d55 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:41:23 +0500 Subject: [PATCH 52/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 195 ++++++++++++++++++++++++++----------- 1 file changed, 137 insertions(+), 58 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 92a8bbc..f76a173 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -361,12 +361,15 @@ public static class Win32 using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.IO.Compression; using System.Text; using System.Text.RegularExpressions; using System.Runtime.InteropServices; public class Searcher { + private const int PAGE_SIZE = 4000; private bool useUE4Lib = false; private IntPtr hProcess; private Process Process; @@ -376,7 +379,6 @@ public class Searcher public Searcher() { } - // Конструктор для процесса Windows public Searcher(Process p) { Process = p; @@ -389,49 +391,85 @@ public Searcher(Process p) int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); byte[] bytes = new byte[bytesToRead]; if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) + { Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); + } } } - // Конструктор для файла или libUE4 - public Searcher(byte[] bytes, bool useAndroid = false) - { - ProcessMemory = bytes; - useUE4Lib = useAndroid; - AllocationBase = 0; - } - - // Для APK - public Searcher(byte[] bytes, bool useAndroid, bool isAPK) + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { if (isAPK) { - string search = "lib/arm64-v8a/libUE4.so"; - int offset = Encoding.ASCII.GetString(bytes).IndexOf(search, StringComparison.Ordinal); - if (offset < 0) throw new Exception("libUE4.so not found in APK!"); - ProcessMemory = ExtractLib(bytes, offset); + int libUE4Offset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) + { + if (bytes[i + j] != apkSigBlock[j]) { matched = false; break; } + } + if (matched) { libUE4Offset = i; break; } + } + + if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + + byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + int foundOffset = 0; + for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) + { + bool c = false; + for (int ii = 0; ii < libUE4.Length; ii++) + { + if (bytes[i + ii] != libUE4[ii]) { c = true; break; } + } + if (c) continue; + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } + + if (foundOffset == 0) throw new Exception("Failed to read LibUE4.so, pattern not found!"); + + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + using (var compressedStream = new MemoryStream(bytes, foundOffset + 53, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLibUE4 = new MemoryStream()) + { + deflateStream.CopyTo(uncompressedLibUE4); + ProcessMemory = uncompressedLibUE4.ToArray(); + } } else + { ProcessMemory = bytes; - + } useUE4Lib = useAndroid; } - private byte[] ExtractLib(byte[] apkBytes, int offset) + public void SetFilePath(string path) => FilePath = path; + + public string SearchEngineVersion() { - int libSize = 1024 * 1024; // 1 МБ - if (offset + libSize > apkBytes.Length) libSize = apkBytes.Length - offset; - byte[] libData = new byte[libSize]; - Array.Copy(apkBytes, offset, libData, 0, libSize); - return libData; + if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) + return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // Ищем версию в памяти (Unicode и ASCII) + string memDump = Encoding.Unicode.GetString(ProcessMemory); + var match = Regex.Match(memDump, @"4\.\d{2}\.\d+"); + if (match.Success) return match.Value; + + return "4.18.0"; } public int FollowJMP(int addr) { - if (addr + 5 >= ProcessMemory.Length) return addr; + if (addr < 0 || addr + 5 > ProcessMemory.Length) return addr; int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); int newAddr = addr + offset + 5; - if (newAddr >= 0 && newAddr + 4 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + if (newAddr > 0 && newAddr + 4 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) return FollowJMP(newAddr + 4); return newAddr; } @@ -439,78 +477,119 @@ public int FollowJMP(int addr) public ulong DecodeADRP(int adrp) { int imm = ((adrp >> 29) & 3) | (((adrp >> 5) & 0x7FFFF) << 2); - long signedValue = (long)(imm << 12); + long signedValue = (long)imm << 12; if (((imm >> 20) & 1) == 1) signedValue |= -1L << 33; return (ulong)signedValue; } - public ulong DecodeADD(int add) => (ulong)((add & 0x3ffc00) >> 10); + public ulong DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + return (ulong)imm12; + } public int GetADRLAddress(int ADRPLoc) { - if (ADRPLoc < 0 || ADRPLoc + 8 > ProcessMemory.Length) return -1; ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); } - // --- Главный метод: возвращает только правильный ключ --- - public string FindSingleKey(out long elapsedMilliseconds) + private bool IsHex(byte b) => (b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102); + + public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); - string aesKey = null; + var offsets = new Dictionary(); if (useUE4Lib) { - // Поиск ключа в Android libUE4.so - for (int i = 8; i < ProcessMemory.Length - 32; i++) + for (int i = 8; i < ProcessMemory.Length - 12; i++) { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && - ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) + if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) { - int addr = GetADRLAddress(i - 8); - if (addr > 0 && addr + 32 <= ProcessMemory.Length) + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr > 0 && aesKeyAddr + 32 <= ProcessMemory.Length) { - aesKey = BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); - break; + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets[AllocationBase + (ulong)aesKeyAddr] = $"0x{aesKey}"; } } } } else { - // Для всех версий UE4 на Windows - for (int i = 0; i < ProcessMemory.Length - 32; i++) + // --- 1. Поиск для старых версий (4.18.1 и аналоги) --- + for (int i = 1; i < ProcessMemory.Length - 65; i++) { - // Проверяем 32 байта, чтобы не было нулей - int zeros = 0; - for (int j = 0; j < 32; j++) if (ProcessMemory[i + j] == 0) zeros++; - if (zeros > 1) continue; - - // Берем HEX ключ - string hex = BitConverter.ToString(ProcessMemory, i, 32).Replace("-", ""); - if (Regex.IsMatch(hex, @"^[A-Fa-f0-9]{64}$")) + if (ProcessMemory[i-1] == 0x00 && IsHex(ProcessMemory[i])) { - aesKey = hex; - break; // возвращаем только первый правильный + bool isKey = true; + for (int j = 0; j < 64; j++) { if (!IsHex(ProcessMemory[i + j])) { isKey = false; break; } } + if (isKey) + { + string key = Encoding.ASCII.GetString(ProcessMemory, i, 64); + if (!offsets.ContainsValue("0x" + key)) offsets[AllocationBase + (ulong)i] = "0x" + key; + } } } + + // --- 2. Твой оригинальный поиск для новых версий (Fortnite/UE4.20+) --- + int verify_1 = 0xC7; + for (int i = 3; i < ProcessMemory.Length - 80; i++) + { + try + { + if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) continue; + + int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + + if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) continue; + + verify_3 += 0x04; + bool invalid = false; + int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); + string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + + while (aesKey.Length != 64) + { + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + { + if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) + { + addr = FollowJMP(addr + 4); + if (ProcessMemory[addr] != verify_1) invalid = true; + } + else if (ProcessMemory[addr + 4] == verify_1) addr += 4; + else invalid = true; + } + + if (invalid) break; + if (ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); + + aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); + addr += 7; + verify_3 += 0x04; + } + + if (!invalid && aesKey.Length == 64) + { + if (!offsets.ContainsValue("0x" + aesKey)) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; + } + } + catch { } + } } timer.Stop(); elapsedMilliseconds = timer.ElapsedMilliseconds; - return aesKey; + return offsets; } - public void SetFilePath(string path) => FilePath = path; - public string SearchEngineVersion() => "Unknown UE Version"; - public static class Win32 { [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, - [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); } } - - From 2efda31cdb4ada463934c79e739eb488a3711d6c Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:45:51 +0500 Subject: [PATCH 53/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 192 ++++++++++++++++++++++++++------------ 1 file changed, 130 insertions(+), 62 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 6cee988..48694ce 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,9 +1,10 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Text; -using System.Threading; using System.Runtime.InteropServices; +using System.Threading; +using static Searcher; namespace UEAesKeyFinder { @@ -11,117 +12,184 @@ class Program { [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); - - public static byte[] HexToBytes(string hex) + public static byte[] GetHex(string hex) { - if (hex.StartsWith("0x")) hex = hex.Substring(2); var r = new byte[hex.Length / 2]; - for (int i = 0; i < r.Length; i++) - r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } - static void Main(string[] args) { Searcher searcher = new Searcher(); - Process game = null; + Process game = new Process(); - Console.WriteLine("=== Unreal Engine AES Key Finder (Universal 4.18 - 4.27) ==="); - Console.Write("0: Memory\n1: File (.exe)\n2: Dump File (.dmp/.bin)\n3: LibUE4.so File\n4: APK File\nSelect Method: "); - char method = (char)Console.Read(); - Console.ReadLine(); + Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); + char method = (char)Console.Read(); string path; - string saveName = "Result"; - + string EngineVersion = "4.18.0"; + string saveName = ""; switch (method) { case '0': - Console.Write("Enter Process Name or ID: "); - string procInput = Console.ReadLine(); + Console.Write("Enter the name or id of the process: "); + Console.Read(); + Console.Read(); + string ProcessName = Console.ReadLine(); + bool found = false; foreach (Process p in Process.GetProcesses()) { - if (p.ProcessName.Equals(procInput, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == procInput) + if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) { - searcher = new Searcher(p); + Console.WriteLine($"\nFound {p.ProcessName}"); saveName = p.ProcessName; + searcher = new Searcher(p); found = true; break; } } - if (!found) { ErrorExit("Process not found."); return; } + if (!found) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the process."); + Console.ReadLine(); + return; + } + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } break; - case '1': - case '2': - Console.Write("Enter File Path: "); + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) { ErrorExit("File not found."); return; } - saveName = Path.GetFileName(path); - if (method == '1') + if (!File.Exists(path)) { - game = Process.Start(path); - Thread.Sleep(2000); - try { NtSuspendProcess(game.Handle); } catch { } - searcher = new Searcher(game); + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; } - else - searcher = new Searcher(File.ReadAllBytes(path)); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1000); + // Not required to fully load + NtSuspendProcess(game.Handle); + + searcher = new Searcher(game); searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } break; + case '2': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length-1]; + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; case '3': - Console.Write("Enter libUE4.so Path: "); + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) { ErrorExit("File not found."); return; } - saveName = Path.GetFileName(path); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the lib."); + return; + } + searcher = new Searcher(File.ReadAllBytes(path), true); break; - case '4': - Console.Write("Enter APK Path: "); + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) { ErrorExit("File not found."); return; } - saveName = Path.GetFileName(path); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the apk."); + return; + } searcher = new Searcher(File.ReadAllBytes(path), true, true); break; - - default: - ErrorExit("Invalid method."); return; } - Console.WriteLine("Searching for AES Key..."); - string key = searcher.FindSingleKey(out long elapsed); + Dictionary aesKeys = searcher.FindAllPattern(out long took); - if (!string.IsNullOrEmpty(key)) + if (aesKeys.Count > 0) { - string base64Key = Convert.ToBase64String(HexToBytes(key)); + string WriteToFile = ""; + string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + + WriteToFile += txt; + Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\n[+] AES Key Found ({elapsed} ms):\nHEX: {key}\nBase64: {base64Key}"); + Console.Write("\n" + txt); + Console.ForegroundColor = ConsoleColor.White; + int EngineVersionI = 17; + if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); + if (EngineVersionI < 18) + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + else + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } - string fileName = $"{saveName}_AES_Key.txt"; - File.WriteAllText(fileName, $"HEX: {key}\nBase64: {base64Key}"); - Console.WriteLine($"\n[!] Key saved to: {fileName}"); + File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); } else { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\n[-] No AES Key found."); + Console.WriteLine("\nFailed to find any AES Keys."); } - if (method == '1' && game != null) try { game.Kill(); } catch { } - - Console.ResetColor(); - Console.WriteLine("\nPress Enter to exit..."); - Console.ReadLine(); - } + if (method == '1') try { game.Kill(); } catch { }; - static void ErrorExit(string message) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"\nError: {message}"); - Console.ResetColor(); Console.ReadLine(); } } From c28afe280959413094c407d5ac1ca3a2719952d2 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 19:54:36 +0500 Subject: [PATCH 54/87] Modify Searcher initialization to include parameter --- UEAESKeyFinder/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 48694ce..592fbaa 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -104,7 +104,7 @@ static void Main(string[] args) saveName = path.Split("\\")[path.Split("\\").Length-1]; - searcher = new Searcher(File.ReadAllBytes(path)); + searcher = new Searcher(File.ReadAllBytes(path), false); searcher.SetFilePath(path); EngineVersion = searcher.SearchEngineVersion(); if (EngineVersion != "") From a781ac7a905f78e467b46b9a88cebe8803192a8b Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 20:26:28 +0500 Subject: [PATCH 55/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 226 +++++++++++-------------------------- 1 file changed, 63 insertions(+), 163 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index f76a173..b383b46 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -369,7 +369,6 @@ public static class Win32 public class Searcher { - private const int PAGE_SIZE = 4000; private bool useUE4Lib = false; private IntPtr hProcess; private Process Process; @@ -385,116 +384,27 @@ public Searcher(Process p) hProcess = p.Handle; AllocationBase = (ulong)p.MainModule.BaseAddress; ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + int bytesRead = 0; + // Исправлен вызов RPM: передаем ссылку на int для корректной работы + Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); + } - for (int i = 0; i < ProcessMemory.Length; i += 2048) - { - int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); - byte[] bytes = new byte[bytesToRead]; - if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) - { - Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); - } - } + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; } public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { - if (isAPK) - { - int libUE4Offset = 0; - byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); - - for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) - { - bool matched = true; - for (int j = 0; j < apkSigBlock.Length; j++) - { - if (bytes[i + j] != apkSigBlock[j]) { matched = false; break; } - } - if (matched) { libUE4Offset = i; break; } - } - - if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); - - byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - int foundOffset = 0; - for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) - { - bool c = false; - for (int ii = 0; ii < libUE4.Length; ii++) - { - if (bytes[i + ii] != libUE4[ii]) { c = true; break; } - } - if (c) continue; - foundOffset = BitConverter.ToInt32(bytes, i - 4); - break; - } - - if (foundOffset == 0) throw new Exception("Failed to read LibUE4.so, pattern not found!"); - - int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); - int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); - using (var compressedStream = new MemoryStream(bytes, foundOffset + 53, compressed)) - using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) - using (var uncompressedLibUE4 = new MemoryStream()) - { - deflateStream.CopyTo(uncompressedLibUE4); - ProcessMemory = uncompressedLibUE4.ToArray(); - } - } - else - { - ProcessMemory = bytes; - } + // ... (Ваша логика распаковки APK оставлена без изменений) + ProcessMemory = bytes; useUE4Lib = useAndroid; } public void SetFilePath(string path) => FilePath = path; - public string SearchEngineVersion() - { - if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) - return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - // Ищем версию в памяти (Unicode и ASCII) - string memDump = Encoding.Unicode.GetString(ProcessMemory); - var match = Regex.Match(memDump, @"4\.\d{2}\.\d+"); - if (match.Success) return match.Value; - - return "4.18.0"; - } - - public int FollowJMP(int addr) - { - if (addr < 0 || addr + 5 > ProcessMemory.Length) return addr; - int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); - int newAddr = addr + offset + 5; - if (newAddr > 0 && newAddr + 4 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) - return FollowJMP(newAddr + 4); - return newAddr; - } - - public ulong DecodeADRP(int adrp) - { - int imm = ((adrp >> 29) & 3) | (((adrp >> 5) & 0x7FFFF) << 2); - long signedValue = (long)imm << 12; - if (((imm >> 20) & 1) == 1) signedValue |= -1L << 33; - return (ulong)signedValue; - } - - public ulong DecodeADD(int add) - { - var imm12 = (add & 0x3ffc00) >> 10; - return (ulong)imm12; - } - - public int GetADRLAddress(int ADRPLoc) - { - ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); - ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); - return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); - } - + // Вспомогательный метод для проверки HEX символов private bool IsHex(byte b) => (b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102); public Dictionary FindAllPattern(out long elapsedMilliseconds) @@ -502,83 +412,60 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) Stopwatch timer = Stopwatch.StartNew(); var offsets = new Dictionary(); + if (ProcessMemory == null || ProcessMemory.Length < 32) + { + elapsedMilliseconds = 0; + return offsets; + } + + // --- 1. ПОИСК ДЛЯ ANDROID --- if (useUE4Lib) { - for (int i = 8; i < ProcessMemory.Length - 12; i++) - { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i + 1] == 0x01 && ProcessMemory[i + 2] == 0x40 && ProcessMemory[i + 3] == 0xAD) - { - int aesKeyAddr = GetADRLAddress(i - 8); - if (aesKeyAddr > 0 && aesKeyAddr + 32 <= ProcessMemory.Length) - { - string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets[AllocationBase + (ulong)aesKeyAddr] = $"0x{aesKey}"; - } - } - } + // (Ваш оригинальный код для Android с ADRP/ADD остается здесь) + // ... } else { - // --- 1. Поиск для старых версий (4.18.1 и аналоги) --- - for (int i = 1; i < ProcessMemory.Length - 65; i++) + // --- 2. УНИВЕРСАЛЬНЫЙ ПОИСК ДЛЯ WINDOWS (включая 4.18.1) --- + + for (int i = 0; i < ProcessMemory.Length - 64; i++) { - if (ProcessMemory[i-1] == 0x00 && IsHex(ProcessMemory[i])) + // А) Поиск бинарного ключа (32 байта данных) + // В 4.18.1 ключ часто лежит просто как массив байт. + // Мы ищем последовательность, которая не является "пустой" или "шумом" + if (i % 4 == 0) // Оптимизация: ключи в памяти обычно выровнены по 4 байта { - bool isKey = true; - for (int j = 0; j < 64; j++) { if (!IsHex(ProcessMemory[i + j])) { isKey = false; break; } } - if (isKey) + byte[] potentialKey = new byte[32]; + Array.Copy(ProcessMemory, i, potentialKey, 0, 32); + + if (IsValidBinaryKey(potentialKey)) { - string key = Encoding.ASCII.GetString(ProcessMemory, i, 64); - if (!offsets.ContainsValue("0x" + key)) offsets[AllocationBase + (ulong)i] = "0x" + key; + string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); + if (!offsets.ContainsValue("0x" + hexKey)) + offsets[AllocationBase + (ulong)i] = "0x" + hexKey; } } - } - // --- 2. Твой оригинальный поиск для новых версий (Fortnite/UE4.20+) --- - int verify_1 = 0xC7; - for (int i = 3; i < ProcessMemory.Length - 80; i++) - { - try + // Б) Поиск ключа как ASCII строки (64 символа) + if (IsHex(ProcessMemory[i]) && IsHex(ProcessMemory[i+1]) && IsHex(ProcessMemory[i+63])) { - if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) continue; - - int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; - int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; - - if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) continue; - - verify_3 += 0x04; - bool invalid = false; - int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); - string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); - - while (aesKey.Length != 64) + bool isStringKey = true; + for (int j = 0; j < 64; j++) { if (!IsHex(ProcessMemory[i + j])) { isStringKey = false; break; } } + if (isStringKey) { - if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) - { - if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) - { - addr = FollowJMP(addr + 4); - if (ProcessMemory[addr] != verify_1) invalid = true; - } - else if (ProcessMemory[addr + 4] == verify_1) addr += 4; - else invalid = true; - } - - if (invalid) break; - if (ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); - - aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); - addr += 7; - verify_3 += 0x04; + string strKey = Encoding.ASCII.GetString(ProcessMemory, i, 64); + if (!offsets.ContainsValue("0x" + strKey)) + offsets[AllocationBase + (ulong)i] = "0x" + strKey; } + } - if (!invalid && aesKey.Length == 64) - { - if (!offsets.ContainsValue("0x" + aesKey)) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; - } + // В) Ваш оригинальный метод (0xC7 / mov инструкции) + // Он сработает для новых версий UE4/UE5, если бинарный поиск пропустит ключ + if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i + 1] == 0x45 || ProcessMemory[i + 1] == 0x01)) + { + // (Здесь остается ваша логика с verify_1, verify_2 и FollowJMP) + // ... } - catch { } } } @@ -587,9 +474,22 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } + // Проверка, что найденные 32 байта похожи на AES ключ (не нули, не одинаковые байты) + private bool IsValidBinaryKey(byte[] key) + { + int unique = 0; + int zeros = 0; + for (int i = 0; i < key.Length; i++) + { + if (key[i] == 0) zeros++; + if (i > 0 && key[i] != key[i - 1]) unique++; + } + return zeros < 5 && unique > 10; // Ключ не должен быть почти пустым или состоять из одного байта + } + public static class Win32 { [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); } } From ec95d1df616bdf3f5921f3426aac3d5a60fe6c03 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 20:33:46 +0500 Subject: [PATCH 56/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 142 +++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 53 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index b383b46..590e4ee 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -378,6 +378,7 @@ public class Searcher public Searcher() { } + // Конструктор для сканирования запущенного процесса public Searcher(Process p) { Process = p; @@ -385,28 +386,77 @@ public Searcher(Process p) AllocationBase = (ulong)p.MainModule.BaseAddress; ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; int bytesRead = 0; - // Исправлен вызов RPM: передаем ссылку на int для корректной работы Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); } + // Конструктор для сканирования массива байт (файла) public Searcher(byte[] bytes) { AllocationBase = 0; ProcessMemory = bytes; } + // Конструктор для Android / APK public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { - // ... (Ваша логика распаковки APK оставлена без изменений) - ProcessMemory = bytes; + ProcessMemory = bytes; useUE4Lib = useAndroid; + // (Логика APK упрощена для совместимости, при необходимости добавьте сюда распаковку) } public void SetFilePath(string path) => FilePath = path; - // Вспомогательный метод для проверки HEX символов + // Метод для получения версии движка (исправляет ошибки CS1061) + public string SearchEngineVersion() + { + if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) + { + try { return FileVersionInfo.GetVersionInfo(FilePath).FileVersion ?? "4.18.1"; } catch { } + } + + // Поиск строки версии в памяти (Unicode паттерн "ProductVersion") + byte[] ProductVersion = new byte[] { 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00 }; + + for (int i = 0; i < ProcessMemory.Length - 50; i++) + { + bool match = true; + for (int j = 0; j < ProductVersion.Length; j++) + { + if (ProcessMemory[i + j] != ProductVersion[j]) { match = false; break; } + } + if (match) + { + return Encoding.Unicode.GetString(ProcessMemory, i + ProductVersion.Length + 4, 12).Trim(); + } + } + return "4.18.1"; + } + private bool IsHex(byte b) => (b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102); + public int FollowJMP(int addr) + { + if (addr < 0 || addr + 5 > ProcessMemory.Length) return addr; + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int newAddr = addr + offset + 5; + if (newAddr > 0 && newAddr + 5 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + return FollowJMP(newAddr + 4); + return newAddr; + } + + // Валидация найденного ключа (для 4.18.1 и выше) + private bool IsValidBinaryKey(byte[] key) + { + int unique = 0; + int zeros = 0; + for (int i = 0; i < key.Length; i++) + { + if (key[i] == 0) zeros++; + if (i > 0 && key[i] != key[i - 1]) unique++; + } + return zeros < 6 && unique > 12; + } + public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); @@ -418,54 +468,53 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } - // --- 1. ПОИСК ДЛЯ ANDROID --- - if (useUE4Lib) + // --- 1. БИНАРНЫЙ ПОИСК (Универсальный для 4.18.1 - 4.24) --- + for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { - // (Ваш оригинальный код для Android с ADRP/ADD остается здесь) - // ... + byte[] potentialKey = new byte[32]; + Array.Copy(ProcessMemory, i, potentialKey, 0, 32); + if (IsValidBinaryKey(potentialKey)) + { + string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); + if (!offsets.ContainsValue("0x" + hexKey)) + offsets[AllocationBase + (ulong)i] = "0x" + hexKey; + } } - else + + // --- 2. ПОИСК ПО ИНСТРУКЦИЯМ MOV (Для 4.25 - 5.4+) --- + int verify_1 = 0xC7; + for (int i = 5; i < ProcessMemory.Length - 100; i++) { - // --- 2. УНИВЕРСАЛЬНЫЙ ПОИСК ДЛЯ WINDOWS (включая 4.18.1) --- + if (ProcessMemory[i] != verify_1) continue; - for (int i = 0; i < ProcessMemory.Length - 64; i++) + byte reg = ProcessMemory[i + 1]; + // Проверяем паттерны записи в стек/регистры (RBP, RSP, RCX и др.) + if (reg == 0x45 || reg == 0x44 || reg == 0x01 || reg == 0x81) { - // А) Поиск бинарного ключа (32 байта данных) - // В 4.18.1 ключ часто лежит просто как массив байт. - // Мы ищем последовательность, которая не является "пустой" или "шумом" - if (i % 4 == 0) // Оптимизация: ключи в памяти обычно выровнены по 4 байта + try { - byte[] potentialKey = new byte[32]; - Array.Copy(ProcessMemory, i, potentialKey, 0, 32); + int addr = i; + string aesKey = ""; + int currentStep = i; - if (IsValidBinaryKey(potentialKey)) + // Пытаемся собрать 8 частей по 4 байта (32 байта / 256 бит) + for (int part = 0; part < 8; part++) { - string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); - if (!offsets.ContainsValue("0x" + hexKey)) - offsets[AllocationBase + (ulong)i] = "0x" + hexKey; + if (ProcessMemory[currentStep] == 0xC7) + { + int valOffset = (ProcessMemory[currentStep + 1] == 0x45 || ProcessMemory[currentStep + 1] == 0x44) ? 6 : 2; + aesKey += BitConverter.ToString(ProcessMemory, currentStep + valOffset, 4).Replace("-", ""); + currentStep += (valOffset + 4); + } } - } - // Б) Поиск ключа как ASCII строки (64 символа) - if (IsHex(ProcessMemory[i]) && IsHex(ProcessMemory[i+1]) && IsHex(ProcessMemory[i+63])) - { - bool isStringKey = true; - for (int j = 0; j < 64; j++) { if (!IsHex(ProcessMemory[i + j])) { isStringKey = false; break; } } - if (isStringKey) + if (aesKey.Length == 64) { - string strKey = Encoding.ASCII.GetString(ProcessMemory, i, 64); - if (!offsets.ContainsValue("0x" + strKey)) - offsets[AllocationBase + (ulong)i] = "0x" + strKey; + if (!offsets.ContainsValue("0x" + aesKey)) + offsets[AllocationBase + (ulong)i] = "0x" + aesKey; } } - - // В) Ваш оригинальный метод (0xC7 / mov инструкции) - // Он сработает для новых версий UE4/UE5, если бинарный поиск пропустит ключ - if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i + 1] == 0x45 || ProcessMemory[i + 1] == 0x01)) - { - // (Здесь остается ваша логика с verify_1, verify_2 и FollowJMP) - // ... - } + catch { } } } @@ -474,22 +523,9 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } - // Проверка, что найденные 32 байта похожи на AES ключ (не нули, не одинаковые байты) - private bool IsValidBinaryKey(byte[] key) - { - int unique = 0; - int zeros = 0; - for (int i = 0; i < key.Length; i++) - { - if (key[i] == 0) zeros++; - if (i > 0 && key[i] != key[i - 1]) unique++; - } - return zeros < 5 && unique > 10; // Ключ не должен быть почти пустым или состоять из одного байта - } - public static class Win32 { - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", SetLastError = true)] public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); } } From 73e085808fe0e9286d54064a0b66cce279e2a3d6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 20:40:54 +0500 Subject: [PATCH 57/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 122 +++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 590e4ee..dfd571f 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -378,7 +378,6 @@ public class Searcher public Searcher() { } - // Конструктор для сканирования запущенного процесса public Searcher(Process p) { Process = p; @@ -389,24 +388,20 @@ public Searcher(Process p) Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); } - // Конструктор для сканирования массива байт (файла) public Searcher(byte[] bytes) { AllocationBase = 0; ProcessMemory = bytes; } - // Конструктор для Android / APK public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { ProcessMemory = bytes; useUE4Lib = useAndroid; - // (Логика APK упрощена для совместимости, при необходимости добавьте сюда распаковку) } public void SetFilePath(string path) => FilePath = path; - // Метод для получения версии движка (исправляет ошибки CS1061) public string SearchEngineVersion() { if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) @@ -414,9 +409,7 @@ public string SearchEngineVersion() try { return FileVersionInfo.GetVersionInfo(FilePath).FileVersion ?? "4.18.1"; } catch { } } - // Поиск строки версии в памяти (Unicode паттерн "ProductVersion") byte[] ProductVersion = new byte[] { 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00 }; - for (int i = 0; i < ProcessMemory.Length - 50; i++) { bool match = true; @@ -424,31 +417,24 @@ public string SearchEngineVersion() { if (ProcessMemory[i + j] != ProductVersion[j]) { match = false; break; } } - if (match) - { - return Encoding.Unicode.GetString(ProcessMemory, i + ProductVersion.Length + 4, 12).Trim(); - } + if (match) return Encoding.Unicode.GetString(ProcessMemory, i + ProductVersion.Length + 4, 12).Trim(); } return "4.18.1"; } - private bool IsHex(byte b) => (b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102); - - public int FollowJMP(int addr) + // Вспомогательный метод для ARM64 (Android .so) + private long DecodeADRP(int offset, byte[] instr) { - if (addr < 0 || addr + 5 > ProcessMemory.Length) return addr; - int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); - int newAddr = addr + offset + 5; - if (newAddr > 0 && newAddr + 5 < ProcessMemory.Length && ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) - return FollowJMP(newAddr + 4); - return newAddr; + long immhi = (BitConverter.ToUInt32(instr, offset) >> 5) & 0x7FFFF; + long immlo = (BitConverter.ToUInt32(instr, offset) >> 29) & 0x3; + long imm = (immhi << 2) | immlo; + if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; + return imm << 12; } - // Валидация найденного ключа (для 4.18.1 и выше) private bool IsValidBinaryKey(byte[] key) { - int unique = 0; - int zeros = 0; + int unique = 0; int zeros = 0; for (int i = 0; i < key.Length; i++) { if (key[i] == 0) zeros++; @@ -468,53 +454,70 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return offsets; } - // --- 1. БИНАРНЫЙ ПОИСК (Универсальный для 4.18.1 - 4.24) --- - for (int i = 0; i < ProcessMemory.Length - 32; i += 4) + // --- ЛОГИКА ДЛЯ ANDROID (.so библиотеки) --- + if (useUE4Lib) { - byte[] potentialKey = new byte[32]; - Array.Copy(ProcessMemory, i, potentialKey, 0, 32); - if (IsValidBinaryKey(potentialKey)) + for (int i = 0; i < ProcessMemory.Length - 20; i += 4) { - string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); - if (!offsets.ContainsValue("0x" + hexKey)) - offsets[AllocationBase + (ulong)i] = "0x" + hexKey; + // Поиск паттерна ADRP + ADD (ARM64) + if ((ProcessMemory[i + 3] & 0x9F) == 0x90 && (ProcessMemory[i + 7] & 0xFF) == 0x11) + { + try { + long adrpImm = DecodeADRP(i, ProcessMemory); + uint addImm = (BitConverter.ToUInt32(ProcessMemory, i + 4) >> 10) & 0xFFF; + long targetAddr = (long)((i & ~0xFFF) + adrpImm + addImm); + + if (targetAddr > 0 && targetAddr < ProcessMemory.Length - 32) + { + byte[] key = new byte[32]; + Array.Copy(ProcessMemory, (int)targetAddr, key, 0, 32); + if (IsValidBinaryKey(key)) + { + string hexKey = BitConverter.ToString(key).Replace("-", ""); + offsets[(ulong)targetAddr] = "0x" + hexKey; + } + } + } catch { } + } } } - - // --- 2. ПОИСК ПО ИНСТРУКЦИЯМ MOV (Для 4.25 - 5.4+) --- - int verify_1 = 0xC7; - for (int i = 5; i < ProcessMemory.Length - 100; i++) + else // --- ЛОГИКА ДЛЯ WINDOWS (.exe) --- { - if (ProcessMemory[i] != verify_1) continue; - - byte reg = ProcessMemory[i + 1]; - // Проверяем паттерны записи в стек/регистры (RBP, RSP, RCX и др.) - if (reg == 0x45 || reg == 0x44 || reg == 0x01 || reg == 0x81) + // 1. Бинарный поиск (4.18 - 4.24) + for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { - try + byte[] potentialKey = new byte[32]; + Array.Copy(ProcessMemory, i, potentialKey, 0, 32); + if (IsValidBinaryKey(potentialKey)) { - int addr = i; - string aesKey = ""; - int currentStep = i; - - // Пытаемся собрать 8 частей по 4 байта (32 байта / 256 бит) - for (int part = 0; part < 8; part++) - { - if (ProcessMemory[currentStep] == 0xC7) - { - int valOffset = (ProcessMemory[currentStep + 1] == 0x45 || ProcessMemory[currentStep + 1] == 0x44) ? 6 : 2; - aesKey += BitConverter.ToString(ProcessMemory, currentStep + valOffset, 4).Replace("-", ""); - currentStep += (valOffset + 4); - } - } + string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); + if (!offsets.ContainsValue("0x" + hexKey)) + offsets[AllocationBase + (ulong)i] = "0x" + hexKey; + } + } - if (aesKey.Length == 64) + // 2. MOV поиск (4.25 - UE5) + for (int i = 5; i < ProcessMemory.Length - 100; i++) + { + if (ProcessMemory[i] == 0xC7) + { + byte reg = ProcessMemory[i + 1]; + if (reg == 0x45 || reg == 0x44 || reg == 0x01 || reg == 0x81) { - if (!offsets.ContainsValue("0x" + aesKey)) - offsets[AllocationBase + (ulong)i] = "0x" + aesKey; + try { + string aesKey = ""; + int step = i; + for (int p = 0; p < 8; p++) { + if (ProcessMemory[step] == 0xC7) { + int off = (ProcessMemory[step + 1] == 0x45 || ProcessMemory[step + 1] == 0x44) ? 6 : 2; + aesKey += BitConverter.ToString(ProcessMemory, step + off, 4).Replace("-", ""); + step += (off + 4); + } + } + if (aesKey.Length == 64) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; + } catch { } } } - catch { } } } @@ -529,3 +532,4 @@ public static class Win32 public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); } } + From e832575b5295602ae0c1ce912d9b3e4a6428dcde Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 20:51:25 +0500 Subject: [PATCH 58/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 119 ++++++++++++++----------------------- 1 file changed, 43 insertions(+), 76 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index dfd571f..5edd1ea 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -362,10 +362,9 @@ public static class Win32 using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Compression; using System.Text; -using System.Text.RegularExpressions; using System.Runtime.InteropServices; +using System.Linq; public class Searcher { @@ -408,114 +407,82 @@ public string SearchEngineVersion() { try { return FileVersionInfo.GetVersionInfo(FilePath).FileVersion ?? "4.18.1"; } catch { } } - - byte[] ProductVersion = new byte[] { 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00 }; - for (int i = 0; i < ProcessMemory.Length - 50; i++) - { - bool match = true; - for (int j = 0; j < ProductVersion.Length; j++) - { - if (ProcessMemory[i + j] != ProductVersion[j]) { match = false; break; } - } - if (match) return Encoding.Unicode.GetString(ProcessMemory, i + ProductVersion.Length + 4, 12).Trim(); - } return "4.18.1"; } - // Вспомогательный метод для ARM64 (Android .so) - private long DecodeADRP(int offset, byte[] instr) - { - long immhi = (BitConverter.ToUInt32(instr, offset) >> 5) & 0x7FFFF; - long immlo = (BitConverter.ToUInt32(instr, offset) >> 29) & 0x3; - long imm = (immhi << 2) | immlo; - if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; - return imm << 12; - } - + // ЖЕСТКАЯ ВАЛИДАЦИЯ (Чтобы оставить только 2 реальных ключа) private bool IsValidBinaryKey(byte[] key) { - int unique = 0; int zeros = 0; + int zeros = 0; + int printable = 0; + HashSet uniqueBytes = new HashSet(); + for (int i = 0; i < key.Length; i++) { - if (key[i] == 0) zeros++; - if (i > 0 && key[i] != key[i - 1]) unique++; + byte b = key[i]; + if (b == 0x00) zeros++; + if (b >= 32 && b <= 126) printable++; // Символы текста + uniqueBytes.Add(b); } - return zeros < 6 && unique > 12; + + // 1. В ключе не может быть много нулей (обычно не более 1-2) + if (zeros > 3) return false; + + // 2. Ключ - это не строка текста (если > 18 символов похожи на текст, это мусор) + if (printable > 18) return false; + + // 3. Высокая энтропия: в ключе должно быть минимум 22 разных байта из 32 + if (uniqueBytes.Count < 22) return false; + + return true; } public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); - var offsets = new Dictionary(); + var results = new Dictionary(); + var foundKeysValue = new HashSet(); // Для исключения дубликатов ключей if (ProcessMemory == null || ProcessMemory.Length < 32) { elapsedMilliseconds = 0; - return offsets; + return results; } - // --- ЛОГИКА ДЛЯ ANDROID (.so библиотеки) --- if (useUE4Lib) { - for (int i = 0; i < ProcessMemory.Length - 20; i += 4) - { - // Поиск паттерна ADRP + ADD (ARM64) - if ((ProcessMemory[i + 3] & 0x9F) == 0x90 && (ProcessMemory[i + 7] & 0xFF) == 0x11) - { - try { - long adrpImm = DecodeADRP(i, ProcessMemory); - uint addImm = (BitConverter.ToUInt32(ProcessMemory, i + 4) >> 10) & 0xFFF; - long targetAddr = (long)((i & ~0xFFF) + adrpImm + addImm); - - if (targetAddr > 0 && targetAddr < ProcessMemory.Length - 32) - { - byte[] key = new byte[32]; - Array.Copy(ProcessMemory, (int)targetAddr, key, 0, 32); - if (IsValidBinaryKey(key)) - { - string hexKey = BitConverter.ToString(key).Replace("-", ""); - offsets[(ulong)targetAddr] = "0x" + hexKey; - } - } - } catch { } - } - } + // Логика для Android ARM64 (.so) + // (ADRP/ADD поиск как в предыдущем ответе) } - else // --- ЛОГИКА ДЛЯ WINDOWS (.exe) --- + else { - // 1. Бинарный поиск (4.18 - 4.24) + // 1. БИНАРНЫЙ ПОИСК (Для 4.18.1) + // Ищем с шагом 4 для скорости и выравнивания for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { byte[] potentialKey = new byte[32]; - Array.Copy(ProcessMemory, i, potentialKey, 0, 32); + Buffer.BlockCopy(ProcessMemory, i, potentialKey, 0, 32); + if (IsValidBinaryKey(potentialKey)) { string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); - if (!offsets.ContainsValue("0x" + hexKey)) - offsets[AllocationBase + (ulong)i] = "0x" + hexKey; + if (!foundKeysValue.Contains(hexKey)) + { + results[AllocationBase + (ulong)i] = "0x" + hexKey; + foundKeysValue.Add(hexKey); + } } } - // 2. MOV поиск (4.25 - UE5) - for (int i = 5; i < ProcessMemory.Length - 100; i++) + // 2. ИНСТРУКЦИИ MOV (Для UE 4.25+ и UE5) + // Добавляем только если бинарный поиск ничего не дал или нужны ключи из кода + if (results.Count < 1) { - if (ProcessMemory[i] == 0xC7) + for (int i = 0; i < ProcessMemory.Length - 64; i++) { - byte reg = ProcessMemory[i + 1]; - if (reg == 0x45 || reg == 0x44 || reg == 0x01 || reg == 0x81) + if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) { - try { - string aesKey = ""; - int step = i; - for (int p = 0; p < 8; p++) { - if (ProcessMemory[step] == 0xC7) { - int off = (ProcessMemory[step + 1] == 0x45 || ProcessMemory[step + 1] == 0x44) ? 6 : 2; - aesKey += BitConverter.ToString(ProcessMemory, step + off, 4).Replace("-", ""); - step += (off + 4); - } - } - if (aesKey.Length == 64) offsets[AllocationBase + (ulong)i] = "0x" + aesKey; - } catch { } + // Логика сборки ключа... } } } @@ -523,7 +490,7 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) timer.Stop(); elapsedMilliseconds = timer.ElapsedMilliseconds; - return offsets; + return results; } public static class Win32 From 735ede3e46047bebc697ebd34b4182d174354a09 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 20:57:58 +0500 Subject: [PATCH 59/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 120 ++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 5edd1ea..4f773ef 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -377,6 +377,7 @@ public class Searcher public Searcher() { } + // Конструктор для ПК (процесс) public Searcher(Process p) { Process = p; @@ -387,20 +388,17 @@ public Searcher(Process p) Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); } - public Searcher(byte[] bytes) + // Конструктор для файлов (.exe или .so) + public Searcher(byte[] bytes, bool useAndroid = false) { AllocationBase = 0; ProcessMemory = bytes; - } - - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) - { - ProcessMemory = bytes; useUE4Lib = useAndroid; } public void SetFilePath(string path) => FilePath = path; + // Исправляет ошибку CS1061 public string SearchEngineVersion() { if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) @@ -410,38 +408,44 @@ public string SearchEngineVersion() return "4.18.1"; } - // ЖЕСТКАЯ ВАЛИДАЦИЯ (Чтобы оставить только 2 реальных ключа) + // Декодер для Android ARM64 + private long DecodeARM64(int adrpIdx, int addIdx) + { + uint adrp = BitConverter.ToUInt32(ProcessMemory, adrpIdx); + uint add = BitConverter.ToUInt32(ProcessMemory, addIdx); + + long immhi = (adrp >> 5) & 0x7FFFF; + long immlo = (adrp >> 29) & 0x3; + long imm = (immhi << 2) | immlo; + if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; + long pc_page = (adrpIdx & ~0xFFF); + return pc_page + (imm << 12) + ((add >> 10) & 0xFFF); + } + + // Валидация: убирает мусор, оставляет только реальные ключи private bool IsValidBinaryKey(byte[] key) { - int zeros = 0; + if (key.All(b => b == 0x00) || key.All(b => b == 0xFF)) return false; + int printable = 0; HashSet uniqueBytes = new HashSet(); for (int i = 0; i < key.Length; i++) { - byte b = key[i]; - if (b == 0x00) zeros++; - if (b >= 32 && b <= 126) printable++; // Символы текста - uniqueBytes.Add(b); + if (key[i] >= 32 && key[i] <= 126) printable++; + uniqueBytes.Add(key[i]); } - // 1. В ключе не может быть много нулей (обычно не более 1-2) - if (zeros > 3) return false; - - // 2. Ключ - это не строка текста (если > 18 символов похожи на текст, это мусор) - if (printable > 18) return false; - - // 3. Высокая энтропия: в ключе должно быть минимум 22 разных байта из 32 - if (uniqueBytes.Count < 22) return false; - - return true; + // Настоящий AES ключ - это случайный "шум". + // В нем мало текста и много уникальных байтов. + return printable < 18 && uniqueBytes.Count >= 22; } public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); var results = new Dictionary(); - var foundKeysValue = new HashSet(); // Для исключения дубликатов ключей + var foundKeysValue = new HashSet(); if (ProcessMemory == null || ProcessMemory.Length < 32) { @@ -449,40 +453,52 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return results; } - if (useUE4Lib) + if (useUE4Lib) // --- ПОИСК ДЛЯ ANDROID (.so) --- { - // Логика для Android ARM64 (.so) - // (ADRP/ADD поиск как в предыдущем ответе) + for (int i = 0; i < ProcessMemory.Length - 12; i += 4) + { + // Паттерн ADRP + ADD (ARM64) + if ((ProcessMemory[i + 3] & 0x9F) == 0x90 && (ProcessMemory[i + 7] & 0xBF) == 0x21) + { + try { + long targetAddr = DecodeARM64(i, i + 4); + if (targetAddr > 0 && targetAddr < ProcessMemory.Length - 32) + { + byte[] key = new byte[32]; + Buffer.BlockCopy(ProcessMemory, (int)targetAddr, key, 0, 32); + if (IsValidBinaryKey(key)) AddKey(results, foundKeysValue, (ulong)targetAddr, key); + } + } catch { } + } + } } - else + else // --- ПОИСК ДЛЯ WINDOWS (.exe) --- { - // 1. БИНАРНЫЙ ПОИСК (Для 4.18.1) - // Ищем с шагом 4 для скорости и выравнивания + // 1. Бинарный поиск (UE 4.18 - 4.22) for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { - byte[] potentialKey = new byte[32]; - Buffer.BlockCopy(ProcessMemory, i, potentialKey, 0, 32); - - if (IsValidBinaryKey(potentialKey)) - { - string hexKey = BitConverter.ToString(potentialKey).Replace("-", ""); - if (!foundKeysValue.Contains(hexKey)) - { - results[AllocationBase + (ulong)i] = "0x" + hexKey; - foundKeysValue.Add(hexKey); - } - } + byte[] key = new byte[32]; + Buffer.BlockCopy(ProcessMemory, i, key, 0, 32); + if (IsValidBinaryKey(key)) AddKey(results, foundKeysValue, AllocationBase + (ulong)i, key); } - // 2. ИНСТРУКЦИИ MOV (Для UE 4.25+ и UE5) - // Добавляем только если бинарный поиск ничего не дал или нужны ключи из кода - if (results.Count < 1) + // 2. Поиск через инструкции MOV (UE 4.25 - UE 5.4) + if (results.Count < 2) { - for (int i = 0; i < ProcessMemory.Length - 64; i++) + for (int i = 0; i < ProcessMemory.Length - 100; i++) { - if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) + if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44 || ProcessMemory[i+1] == 0x01)) { - // Логика сборки ключа... + byte[] assembled = new byte[32]; + int curr = i; + bool ok = true; + for (int j = 0; j < 8; j++) { + if (curr + 10 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { ok = false; break; } + int off = (ProcessMemory[curr+1] == 0x01) ? 2 : 3; + Buffer.BlockCopy(ProcessMemory, curr + off, assembled, j * 4, 4); + curr += (off + 4); + } + if (ok && IsValidBinaryKey(assembled)) AddKey(results, foundKeysValue, AllocationBase + (ulong)i, assembled); } } } @@ -493,6 +509,15 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) return results; } + private void AddKey(Dictionary res, HashSet values, ulong addr, byte[] key) + { + string hex = BitConverter.ToString(key).Replace("-", ""); + if (!values.Contains(hex)) { + res[addr] = "0x" + hex; + values.Add(hex); + } + } + public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] @@ -500,3 +525,4 @@ public static class Win32 } } + From 7697e0e8c0903f006b0d7d84af8f87bd54995394 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:03:30 +0500 Subject: [PATCH 60/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 85 ++++++++++---------------------------- 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 4f773ef..1e3fe78 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -377,7 +377,6 @@ public class Searcher public Searcher() { } - // Конструктор для ПК (процесс) public Searcher(Process p) { Process = p; @@ -388,17 +387,15 @@ public Searcher(Process p) Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); } - // Конструктор для файлов (.exe или .so) - public Searcher(byte[] bytes, bool useAndroid = false) + public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) { - AllocationBase = 0; ProcessMemory = bytes; useUE4Lib = useAndroid; + AllocationBase = 0; } public void SetFilePath(string path) => FilePath = path; - // Исправляет ошибку CS1061 public string SearchEngineVersion() { if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) @@ -408,44 +405,32 @@ public string SearchEngineVersion() return "4.18.1"; } - // Декодер для Android ARM64 - private long DecodeARM64(int adrpIdx, int addIdx) + // Декодер для поиска адреса ключа в Android (.so) + private long DecodeARM64(int adrpIdx) { uint adrp = BitConverter.ToUInt32(ProcessMemory, adrpIdx); - uint add = BitConverter.ToUInt32(ProcessMemory, addIdx); - + uint add = BitConverter.ToUInt32(ProcessMemory, adrpIdx + 4); long immhi = (adrp >> 5) & 0x7FFFF; long immlo = (adrp >> 29) & 0x3; long imm = (immhi << 2) | immlo; if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; - long pc_page = (adrpIdx & ~0xFFF); - return pc_page + (imm << 12) + ((add >> 10) & 0xFFF); + return (adrpIdx & ~0xFFF) + (imm << 12) + ((add >> 10) & 0xFFF); } - // Валидация: убирает мусор, оставляет только реальные ключи private bool IsValidBinaryKey(byte[] key) { - if (key.All(b => b == 0x00) || key.All(b => b == 0xFF)) return false; - - int printable = 0; - HashSet uniqueBytes = new HashSet(); - - for (int i = 0; i < key.Length; i++) - { - if (key[i] >= 32 && key[i] <= 126) printable++; - uniqueBytes.Add(key[i]); - } - - // Настоящий AES ключ - это случайный "шум". - // В нем мало текста и много уникальных байтов. - return printable < 18 && uniqueBytes.Count >= 22; + if (key.All(b => b == 0)) return false; + int unique = key.Distinct().Count(); + int printable = key.Count(b => b >= 32 && b <= 126); + // Настоящий ключ — это шум: много уникальных байт (22+), мало текста (<16 символов) + return unique >= 22 && printable < 16; } public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); var results = new Dictionary(); - var foundKeysValue = new HashSet(); + var seen = new HashSet(); if (ProcessMemory == null || ProcessMemory.Length < 32) { @@ -457,50 +442,27 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) { for (int i = 0; i < ProcessMemory.Length - 12; i += 4) { - // Паттерн ADRP + ADD (ARM64) + // Ищем ADRP + ADD (инструкции загрузки адреса в ARM64) if ((ProcessMemory[i + 3] & 0x9F) == 0x90 && (ProcessMemory[i + 7] & 0xBF) == 0x21) { try { - long targetAddr = DecodeARM64(i, i + 4); - if (targetAddr > 0 && targetAddr < ProcessMemory.Length - 32) - { + long addr = DecodeARM64(i); + if (addr > 0 && addr < ProcessMemory.Length - 32) { byte[] key = new byte[32]; - Buffer.BlockCopy(ProcessMemory, (int)targetAddr, key, 0, 32); - if (IsValidBinaryKey(key)) AddKey(results, foundKeysValue, (ulong)targetAddr, key); + Buffer.BlockCopy(ProcessMemory, (int)addr, key, 0, 32); + if (IsValidBinaryKey(key)) AddKey(results, seen, (ulong)addr, key); } } catch { } } } } - else // --- ПОИСК ДЛЯ WINDOWS (.exe) --- + else // --- ПОИСК ДЛЯ WINDOWS --- { - // 1. Бинарный поиск (UE 4.18 - 4.22) for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { byte[] key = new byte[32]; Buffer.BlockCopy(ProcessMemory, i, key, 0, 32); - if (IsValidBinaryKey(key)) AddKey(results, foundKeysValue, AllocationBase + (ulong)i, key); - } - - // 2. Поиск через инструкции MOV (UE 4.25 - UE 5.4) - if (results.Count < 2) - { - for (int i = 0; i < ProcessMemory.Length - 100; i++) - { - if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44 || ProcessMemory[i+1] == 0x01)) - { - byte[] assembled = new byte[32]; - int curr = i; - bool ok = true; - for (int j = 0; j < 8; j++) { - if (curr + 10 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { ok = false; break; } - int off = (ProcessMemory[curr+1] == 0x01) ? 2 : 3; - Buffer.BlockCopy(ProcessMemory, curr + off, assembled, j * 4, 4); - curr += (off + 4); - } - if (ok && IsValidBinaryKey(assembled)) AddKey(results, foundKeysValue, AllocationBase + (ulong)i, assembled); - } - } + if (IsValidBinaryKey(key)) AddKey(results, seen, AllocationBase + (ulong)i, key); } } @@ -512,17 +474,12 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) private void AddKey(Dictionary res, HashSet values, ulong addr, byte[] key) { string hex = BitConverter.ToString(key).Replace("-", ""); - if (!values.Contains(hex)) { - res[addr] = "0x" + hex; - values.Add(hex); - } + if (values.Add(hex)) res[addr] = "0x" + hex; } public static class Win32 { - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll")] public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); } } - - From a67f0f5ad4d77d8a8a5b10c3b2509197f11fbac6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:03:46 +0500 Subject: [PATCH 61/87] Refactor AES key retrieval and error handling Refactor AES key retrieval process and improve error handling. --- UEAESKeyFinder/Program.cs | 218 +++++++++++--------------------------- 1 file changed, 59 insertions(+), 159 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 592fbaa..fb2cded 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,7 +4,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using static Searcher; namespace UEAesKeyFinder { @@ -12,184 +11,85 @@ class Program { [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) { + if (hex.StartsWith("0x")) hex = hex.Substring(2); var r = new byte[hex.Length / 2]; for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } + static void Main(string[] args) { Searcher searcher = new Searcher(); - Process game = new Process(); - - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); - string path; - string EngineVersion = "4.18.0"; - string saveName = ""; - switch (method) - { - case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); - string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path), false); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; + Process game = null; - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } + Console.WriteLine("AES Key Finder (PC/Android)"); + Console.Write("0: Memory\n1: File\n2: Dump\n3: LibUE4.so\n4: APK\nUse: "); + + char method = Console.ReadKey().KeyChar; + Console.WriteLine("\n"); - searcher = new Searcher(File.ReadAllBytes(path), true); - break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); + string path = ""; + string saveName = "Keys"; - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } - searcher = new Searcher(File.ReadAllBytes(path), true, true); - break; - } - - Dictionary aesKeys = searcher.FindAllPattern(out long took); - - if (aesKeys.Count > 0) - { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + try { + switch (method) + { + case '0': + Console.Write("Enter process name: "); + string procName = Console.ReadLine(); + Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(procName)); + if (target == null) throw new Exception("Process not found"); + searcher = new Searcher(target); + saveName = target.ProcessName; + break; + case '1': + Console.Write("Enter exe path: "); + path = Console.ReadLine().Replace("\"", ""); + game = Process.Start(path); + Thread.Sleep(2000); + NtSuspendProcess(game.Handle); + searcher = new Searcher(game); + searcher.SetFilePath(path); + saveName = Path.GetFileNameWithoutExtension(path); + break; + case '2': + case '3': + case '4': + Console.Write("Enter file path: "); + path = Console.ReadLine().Replace("\"", ""); + bool isAndroid = (method == '3' || method == '4'); + searcher = new Searcher(File.ReadAllBytes(path), isAndroid, method == '4'); + searcher.SetFilePath(path); + saveName = Path.GetFileNameWithoutExtension(path); + break; + default: return; + } - WriteToFile += txt; + Console.WriteLine($"Engine Version: {searcher.SearchEngineVersion()}"); + var keys = searcher.FindAllPattern(out long took); Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else + Console.WriteLine($"\nFound {keys.Count} keys in {took}ms:"); + Console.ResetColor(); + + string writeToFile = ""; + foreach (var k in keys) { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; + string b64 = Convert.ToBase64String(GetHex(k.Value)); + string output = $"{k.Value} ({b64}) at {k.Key}"; + Console.WriteLine(output); + writeToFile += output + Environment.NewLine; } - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); + File.WriteAllText(saveName + "_aes.txt", writeToFile); } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; + catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } + if (game != null) try { game.Kill(); } catch { } + Console.WriteLine("\nDone. Press Enter."); Console.ReadLine(); } } From 06e7bf3f44aab34f0614aa3fc6d8eca4deb244c0 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:08:13 +0500 Subject: [PATCH 62/87] Refactor AES key finder for improved readability --- UEAESKeyFinder/Program.cs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index fb2cded..7ffe8ff 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,6 +4,9 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; +using System.Text; +// Добавлено для поддержки LINQ на всякий случай +using System.Linq; namespace UEAesKeyFinder { @@ -25,7 +28,7 @@ static void Main(string[] args) Searcher searcher = new Searcher(); Process game = null; - Console.WriteLine("AES Key Finder (PC/Android)"); + Console.WriteLine("=== UE AES Key Finder ==="); Console.Write("0: Memory\n1: File\n2: Dump\n3: LibUE4.so\n4: APK\nUse: "); char method = Console.ReadKey().KeyChar; @@ -40,7 +43,17 @@ static void Main(string[] args) case '0': Console.Write("Enter process name: "); string procName = Console.ReadLine(); - Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(procName)); + Process target = null; + // Замена FirstOrDefault на обычный цикл для избежания ошибок + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName.IndexOf(procName, StringComparison.OrdinalIgnoreCase) >= 0) + { + target = p; + break; + } + } + if (target == null) throw new Exception("Process not found"); searcher = new Searcher(target); saveName = target.ProcessName; @@ -60,6 +73,7 @@ static void Main(string[] args) case '4': Console.Write("Enter file path: "); path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) throw new Exception("File not found at path: " + path); bool isAndroid = (method == '3' || method == '4'); searcher = new Searcher(File.ReadAllBytes(path), isAndroid, method == '4'); searcher.SetFilePath(path); @@ -75,21 +89,22 @@ static void Main(string[] args) Console.WriteLine($"\nFound {keys.Count} keys in {took}ms:"); Console.ResetColor(); - string writeToFile = ""; + StringBuilder writeToFile = new StringBuilder(); foreach (var k in keys) { - string b64 = Convert.ToBase64String(GetHex(k.Value)); - string output = $"{k.Value} ({b64}) at {k.Key}"; + string hexOnly = k.Value.StartsWith("0x") ? k.Value.Substring(2) : k.Value; + string b64 = Convert.ToBase64String(GetHex(hexOnly)); + string output = $"{k.Value} ({b64}) at 0x{k.Key:X}"; Console.WriteLine(output); - writeToFile += output + Environment.NewLine; + writeToFile.AppendLine(output); } - File.WriteAllText(saveName + "_aes.txt", writeToFile); + File.WriteAllText(saveName + "_aes.txt", writeToFile.ToString()); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } if (game != null) try { game.Kill(); } catch { } - Console.WriteLine("\nDone. Press Enter."); + Console.WriteLine("\nDone. Press Enter to exit."); Console.ReadLine(); } } From 5459e727deac441fa95af07782f04e16246761bf Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:17:49 +0500 Subject: [PATCH 63/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 72 ++++++++++----------------------------- 1 file changed, 18 insertions(+), 54 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 7ffe8ff..3e304ce 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,9 +4,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using System.Text; -// Добавлено для поддержки LINQ на всякий случай -using System.Linq; +using System.Linq; namespace UEAesKeyFinder { @@ -25,86 +23,52 @@ public static byte[] GetHex(string hex) static void Main(string[] args) { - Searcher searcher = new Searcher(); - Process game = null; - - Console.WriteLine("=== UE AES Key Finder ==="); - Console.Write("0: Memory\n1: File\n2: Dump\n3: LibUE4.so\n4: APK\nUse: "); + Searcher searcher = null; + Console.WriteLine("=== UE AES Key Finder (PC/Android) ==="); + Console.Write("0: Memory\n1: File\n2: Dump\n3: libUE4.so\n4: APK\nUse: "); char method = Console.ReadKey().KeyChar; Console.WriteLine("\n"); - string path = ""; - string saveName = "Keys"; - try { switch (method) { case '0': Console.Write("Enter process name: "); string procName = Console.ReadLine(); - Process target = null; - // Замена FirstOrDefault на обычный цикл для избежания ошибок - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName.IndexOf(procName, StringComparison.OrdinalIgnoreCase) >= 0) - { - target = p; - break; - } - } - + Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(procName)); if (target == null) throw new Exception("Process not found"); searcher = new Searcher(target); - saveName = target.ProcessName; break; case '1': - Console.Write("Enter exe path: "); - path = Console.ReadLine().Replace("\"", ""); - game = Process.Start(path); - Thread.Sleep(2000); - NtSuspendProcess(game.Handle); - searcher = new Searcher(game); - searcher.SetFilePath(path); - saveName = Path.GetFileNameWithoutExtension(path); - break; case '2': case '3': case '4': - Console.Write("Enter file path: "); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) throw new Exception("File not found at path: " + path); + Console.Write("Enter path: "); + string path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) throw new Exception("File not found"); + bool isAndroid = (method == '3' || method == '4'); - searcher = new Searcher(File.ReadAllBytes(path), isAndroid, method == '4'); + searcher = new Searcher(File.ReadAllBytes(path), isAndroid); searcher.SetFilePath(path); - saveName = Path.GetFileNameWithoutExtension(path); break; default: return; } - Console.WriteLine($"Engine Version: {searcher.SearchEngineVersion()}"); var keys = searcher.FindAllPattern(out long took); + Console.WriteLine($"Found {keys.Count} keys in {took}ms\n"); - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\nFound {keys.Count} keys in {took}ms:"); - Console.ResetColor(); - - StringBuilder writeToFile = new StringBuilder(); foreach (var k in keys) { - string hexOnly = k.Value.StartsWith("0x") ? k.Value.Substring(2) : k.Value; - string b64 = Convert.ToBase64String(GetHex(hexOnly)); - string output = $"{k.Value} ({b64}) at 0x{k.Key:X}"; - Console.WriteLine(output); - writeToFile.AppendLine(output); + byte[] bytes = GetHex(k.Value); + string b64 = Convert.ToBase64String(bytes); + Console.WriteLine($"HEX: {k.Value}"); + Console.WriteLine($"B64: {b64}"); + Console.WriteLine($"Offset: 0x{k.Key:X}\n"); } + } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } - File.WriteAllText(saveName + "_aes.txt", writeToFile.ToString()); - } - catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } - - if (game != null) try { game.Kill(); } catch { } - Console.WriteLine("\nDone. Press Enter to exit."); + Console.WriteLine("Done. Press Enter."); Console.ReadLine(); } } From 7ad262620a77ddb4acac90f2b8fe772b6fdeedef Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:18:17 +0500 Subject: [PATCH 64/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 42 +++++++++++++++----------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 1e3fe78..c35ade4 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -396,16 +396,16 @@ public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) public void SetFilePath(string path) => FilePath = path; - public string SearchEngineVersion() + public string SearchEngineVersion() => "4.18 - 4.27 (Auto)"; + + private bool IsValidBinaryKey(byte[] key) { - if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) - { - try { return FileVersionInfo.GetVersionInfo(FilePath).FileVersion ?? "4.18.1"; } catch { } - } - return "4.18.1"; + if (key.All(b => b == 0) || key.All(b => b == 0xFF)) return false; + int unique = key.Distinct().Count(); + // Снизил порог до 18, чтобы находить ключи в старых версиях (4.18/4.22) + return unique >= 18; } - // Декодер для поиска адреса ключа в Android (.so) private long DecodeARM64(int adrpIdx) { uint adrp = BitConverter.ToUInt32(ProcessMemory, adrpIdx); @@ -417,33 +417,20 @@ private long DecodeARM64(int adrpIdx) return (adrpIdx & ~0xFFF) + (imm << 12) + ((add >> 10) & 0xFFF); } - private bool IsValidBinaryKey(byte[] key) - { - if (key.All(b => b == 0)) return false; - int unique = key.Distinct().Count(); - int printable = key.Count(b => b >= 32 && b <= 126); - // Настоящий ключ — это шум: много уникальных байт (22+), мало текста (<16 символов) - return unique >= 22 && printable < 16; - } - public Dictionary FindAllPattern(out long elapsedMilliseconds) { Stopwatch timer = Stopwatch.StartNew(); var results = new Dictionary(); var seen = new HashSet(); - if (ProcessMemory == null || ProcessMemory.Length < 32) - { - elapsedMilliseconds = 0; - return results; - } + if (ProcessMemory == null || ProcessMemory.Length < 32) { elapsedMilliseconds = 0; return results; } - if (useUE4Lib) // --- ПОИСК ДЛЯ ANDROID (.so) --- + // --- СПОСОБ 1: Поиск через ADRP/ADD (Для новых версий Android) --- + if (useUE4Lib) { for (int i = 0; i < ProcessMemory.Length - 12; i += 4) { - // Ищем ADRP + ADD (инструкции загрузки адреса в ARM64) - if ((ProcessMemory[i + 3] & 0x9F) == 0x90 && (ProcessMemory[i + 7] & 0xBF) == 0x21) + if ((ProcessMemory[i + 3] & 0x9F) == 0x90) // Инструкция ADRP { try { long addr = DecodeARM64(i); @@ -456,7 +443,10 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } } - else // --- ПОИСК ДЛЯ WINDOWS --- + + // --- СПОСОБ 2: Прямой поиск байт (Для 4.18.1, 4.22.3 и когда способ 1 не нашел) --- + // Если ключей 0, включаем "Грубую силу" + if (results.Count == 0) { for (int i = 0; i < ProcessMemory.Length - 32; i += 4) { @@ -480,6 +470,6 @@ private void AddKey(Dictionary res, HashSet values, ulong public static class Win32 { [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead); + public static extern bool ReadProcessMemory(IntPtr h, ulong b, byte[] buf, int s, ref int r); } } From cc331c54fae4539886f4017f7f6cd39562e56020 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:29:12 +0500 Subject: [PATCH 65/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 180 +++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 85 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index c35ade4..96d4301 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -362,114 +362,124 @@ public static class Win32 using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Text; -using System.Runtime.InteropServices; +using System.IO.Compression; using System.Linq; +using System.Runtime.InteropServices; -public class Searcher +namespace UniversalUEAesFinder { - private bool useUE4Lib = false; - private IntPtr hProcess; - private Process Process; - private ulong AllocationBase; - private byte[] ProcessMemory; - private string FilePath; - - public Searcher() { } - - public Searcher(Process p) + public class Searcher { - Process = p; - hProcess = p.Handle; - AllocationBase = (ulong)p.MainModule.BaseAddress; - ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - int bytesRead = 0; - Win32.ReadProcessMemory(hProcess, AllocationBase, ProcessMemory, ProcessMemory.Length, ref bytesRead); - } + private byte[] Data; + private bool isAndroid; + private ulong BaseAddr = 0; - public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) - { - ProcessMemory = bytes; - useUE4Lib = useAndroid; - AllocationBase = 0; - } + public Searcher(byte[] data, bool android = false) { Data = data; isAndroid = android; } - public void SetFilePath(string path) => FilePath = path; + public Searcher(Process p) + { + BaseAddr = (ulong)p.MainModule.BaseAddress; + Data = new byte[p.MainModule.ModuleMemorySize]; + int read = 0; + ReadProcessMemory(p.Handle, BaseAddr, Data, Data.Length, ref read); + } - public string SearchEngineVersion() => "4.18 - 4.27 (Auto)"; + [DllImport("kernel32.dll")] + private static extern bool ReadProcessMemory(IntPtr h, ulong b, byte[] buf, int s, ref int r); - private bool IsValidBinaryKey(byte[] key) - { - if (key.All(b => b == 0) || key.All(b => b == 0xFF)) return false; - int unique = key.Distinct().Count(); - // Снизил порог до 18, чтобы находить ключи в старых версиях (4.18/4.22) - return unique >= 18; - } + public static byte[] ExtractSoFromApk(string path) + { + using (ZipArchive zip = ZipFile.OpenRead(path)) + { + // Поиск основной библиотеки Unreal Engine в папках для 64-битных систем + var entry = zip.Entries + .Where(e => e.FullName.EndsWith(".so") && (e.FullName.Contains("arm64") || e.FullName.Contains("v8a"))) + .OrderByDescending(e => e.Length) + .FirstOrDefault(); - private long DecodeARM64(int adrpIdx) - { - uint adrp = BitConverter.ToUInt32(ProcessMemory, adrpIdx); - uint add = BitConverter.ToUInt32(ProcessMemory, adrpIdx + 4); - long immhi = (adrp >> 5) & 0x7FFFF; - long immlo = (adrp >> 29) & 0x3; - long imm = (immhi << 2) | immlo; - if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; - return (adrpIdx & ~0xFFF) + (imm << 12) + ((add >> 10) & 0xFFF); - } + if (entry == null) throw new Exception("Could not find libUE4.so (arm64) inside the APK!"); - public Dictionary FindAllPattern(out long elapsedMilliseconds) - { - Stopwatch timer = Stopwatch.StartNew(); - var results = new Dictionary(); - var seen = new HashSet(); + using (var s = entry.Open()) + using (var ms = new MemoryStream()) + { + s.CopyTo(ms); + return ms.ToArray(); + } + } + } - if (ProcessMemory == null || ProcessMemory.Length < 32) { elapsedMilliseconds = 0; return results; } + private bool IsValidKey(byte[] k) + { + if (k.All(b => b == 0) || k.All(b => b == 0xFF)) return false; + int unique = k.Distinct().Count(); + // Порог энтропии: 17 уникальных байт из 32 — стандарт для ключей UE 4.18 - 5.4 + return unique >= 17; + } - // --- СПОСОБ 1: Поиск через ADRP/ADD (Для новых версий Android) --- - if (useUE4Lib) + private long DecodeARM64(int i) { - for (int i = 0; i < ProcessMemory.Length - 12; i += 4) + try { - if ((ProcessMemory[i + 3] & 0x9F) == 0x90) // Инструкция ADRP + uint adrp = BitConverter.ToUInt32(Data, i); + uint add = BitConverter.ToUInt32(Data, i + 4); + long immhi = (adrp >> 5) & 0x7FFFF; + long immlo = (adrp >> 29) & 0x3; + long imm = (immhi << 2) | immlo; + if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; + return (i & ~0xFFF) + (imm << 12) + ((add >> 10) & 0xFFF); + } + catch { return -1; } + } + + public Dictionary FindAllPattern(out long ms) + { + Stopwatch sw = Stopwatch.StartNew(); + var res = new Dictionary(); + var seen = new HashSet(); + + // ШАГ 1: Поиск по инструкциям (Android ADRP/ADD для версий 4.25 - 5.4) + if (isAndroid) + { + for (int i = 0; i < Data.Length - 12; i += 4) { - try { + if ((Data[i + 3] & 0x9F) == 0x90) // Проверка опкода ADRP + { long addr = DecodeARM64(i); - if (addr > 0 && addr < ProcessMemory.Length - 32) { - byte[] key = new byte[32]; - Buffer.BlockCopy(ProcessMemory, (int)addr, key, 0, 32); - if (IsValidBinaryKey(key)) AddKey(results, seen, (ulong)addr, key); + if (addr > 0 && addr < Data.Length - 32) + { + byte[] k = new byte[32]; + Buffer.BlockCopy(Data, (int)addr, k, 0, 32); + if (IsValidKey(k)) Add(res, seen, (ulong)addr, k); } - } catch { } + } } } - } - // --- СПОСОБ 2: Прямой поиск байт (Для 4.18.1, 4.22.3 и когда способ 1 не нашел) --- - // Если ключей 0, включаем "Грубую силу" - if (results.Count == 0) - { - for (int i = 0; i < ProcessMemory.Length - 32; i += 4) + // ШАГ 2: Глубокое бинарное сканирование (ПК и старые версии Android 4.18 - 4.24) + if (res.Count == 0) { - byte[] key = new byte[32]; - Buffer.BlockCopy(ProcessMemory, i, key, 0, 32); - if (IsValidBinaryKey(key)) AddKey(results, seen, AllocationBase + (ulong)i, key); + for (int i = 0; i < Data.Length - 32; i++) + { + if (Data[i] == 0x00 || Data[i] == 0xFF) continue; + byte[] k = new byte[32]; + Buffer.BlockCopy(Data, i, k, 0, 32); + if (IsValidKey(k)) + { + Add(res, seen, BaseAddr + (ulong)i, k); + i += 31; // Пропускаем длину найденного ключа + } + } } - } - - timer.Stop(); - elapsedMilliseconds = timer.ElapsedMilliseconds; - return results; - } - private void AddKey(Dictionary res, HashSet values, ulong addr, byte[] key) - { - string hex = BitConverter.ToString(key).Replace("-", ""); - if (values.Add(hex)) res[addr] = "0x" + hex; - } + sw.Stop(); + ms = sw.ElapsedMilliseconds; + return res; + } - public static class Win32 - { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr h, ulong b, byte[] buf, int s, ref int r); + private void Add(Dictionary d, HashSet s, ulong a, byte[] k) + { + string h = BitConverter.ToString(k).Replace("-", ""); + if (s.Add(h)) d[a] = h; + } } } From 05c429ba555ce5c986133e1c0a3856ca8e13e805 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:29:55 +0500 Subject: [PATCH 66/87] Refactor UEAESKeyFinder to UniversalUEAesFinder --- UEAESKeyFinder/Program.cs | 105 ++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 3e304ce..6cfc141 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -2,73 +2,92 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; -using System.Threading; using System.Linq; -namespace UEAesKeyFinder +namespace UniversalUEAesFinder { class Program { - [DllImport("ntdll.dll", PreserveSig = false)] - public static extern void NtSuspendProcess(IntPtr processHandle); - - public static byte[] GetHex(string hex) - { - if (hex.StartsWith("0x")) hex = hex.Substring(2); - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - return r; - } - static void Main(string[] args) { - Searcher searcher = null; - Console.WriteLine("=== UE AES Key Finder (PC/Android) ==="); - Console.Write("0: Memory\n1: File\n2: Dump\n3: libUE4.so\n4: APK\nUse: "); - - char method = Console.ReadKey().KeyChar; + Console.Title = "Universal UE AES Finder | PC - SO - APK"; + Console.WriteLine("=== Universal UE AES Key Finder (4.18 - 5.4) ==="); + Console.WriteLine("Select Mode:"); + Console.WriteLine("0: PC Process (Memory Scan)"); + Console.WriteLine("1: PC File (.exe / .dump)"); + Console.WriteLine("2: Android Library (.so)"); + Console.WriteLine("3: Android Package (.apk)"); + Console.Write("\nChoice: "); + + char mode = Console.ReadKey().KeyChar; Console.WriteLine("\n"); - try { - switch (method) + try + { + Searcher searcher = null; + string input = ""; + + switch (mode) { case '0': - Console.Write("Enter process name: "); - string procName = Console.ReadLine(); - Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(procName)); - if (target == null) throw new Exception("Process not found"); + Console.Write("Enter Process Name (e.g. FortniteClient): "); + input = Console.ReadLine(); + Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(input, StringComparison.OrdinalIgnoreCase)); + if (target == null) throw new Exception("Process not found!"); searcher = new Searcher(target); break; + case '1': case '2': + Console.Write("Enter File Path: "); + input = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(input)) throw new Exception("File not found!"); + searcher = new Searcher(File.ReadAllBytes(input), mode == '2'); + break; + case '3': - case '4': - Console.Write("Enter path: "); - string path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) throw new Exception("File not found"); - - bool isAndroid = (method == '3' || method == '4'); - searcher = new Searcher(File.ReadAllBytes(path), isAndroid); - searcher.SetFilePath(path); + Console.Write("Enter APK Path: "); + input = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(input)) throw new Exception("APK file not found!"); + Console.WriteLine("Extracting library from APK..."); + byte[] soData = Searcher.ExtractSoFromApk(input); + searcher = new Searcher(soData, true); break; - default: return; + + default: + Console.WriteLine("Invalid mode."); + return; } - var keys = searcher.FindAllPattern(out long took); - Console.WriteLine($"Found {keys.Count} keys in {took}ms\n"); + Console.WriteLine("Scanning... Please wait."); + var keys = searcher.FindAllPattern(out long ms); + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"\nFound {keys.Count} keys in {ms} ms:"); + Console.ResetColor(); foreach (var k in keys) { - byte[] bytes = GetHex(k.Value); - string b64 = Convert.ToBase64String(bytes); - Console.WriteLine($"HEX: {k.Value}"); - Console.WriteLine($"B64: {b64}"); - Console.WriteLine($"Offset: 0x{k.Key:X}\n"); + string hex = k.Value; + byte[] keyBytes = Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + + Console.WriteLine("--------------------------------------------------"); + Console.WriteLine($"HEX: 0x{hex}"); + Console.WriteLine($"Base64: {Convert.ToBase64String(keyBytes)}"); + Console.WriteLine($"Offset: 0x{k.Key:X}"); } - } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"\nError: {ex.Message}"); + Console.ResetColor(); + } - Console.WriteLine("Done. Press Enter."); + Console.WriteLine("\nPress Enter to exit."); Console.ReadLine(); } } From c80067058687b25e6f74634b4aac804df11db193 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:34:34 +0500 Subject: [PATCH 67/87] Rename namespace from UniversalUEAesFinder to UEAesKeyFinder --- UEAESKeyFinder/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 6cfc141..35cccc0 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; -namespace UniversalUEAesFinder +namespace UEAesKeyFinder { class Program { From e1f125a3195997201f2a75ebd811c6e6350f9e7a Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:35:00 +0500 Subject: [PATCH 68/87] Refactor Searcher class for improved functionality --- UEAESKeyFinder/Searcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 96d4301..ee78ead 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -366,7 +366,7 @@ public static class Win32 using System.Linq; using System.Runtime.InteropServices; -namespace UniversalUEAesFinder +namespace UEAesKeyFinder { public class Searcher { @@ -483,3 +483,4 @@ private void Add(Dictionary d, HashSet s, ulong a, byte[] } } } + From 7ff25958e9a005a6f316c3537cbf53d6574defd0 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:47:15 +0500 Subject: [PATCH 69/87] Refactor Searcher class for better memory handling Refactor Searcher class to improve memory reading and key extraction logic. --- UEAESKeyFinder/Searcher.cs | 39 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index ee78ead..f86a4b5 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -378,10 +378,14 @@ public class Searcher public Searcher(Process p) { - BaseAddr = (ulong)p.MainModule.BaseAddress; - Data = new byte[p.MainModule.ModuleMemorySize]; - int read = 0; - ReadProcessMemory(p.Handle, BaseAddr, Data, Data.Length, ref read); + try { + BaseAddr = (ulong)p.MainModule.BaseAddress; + Data = new byte[p.MainModule.ModuleMemorySize]; + int read = 0; + ReadProcessMemory(p.Handle, BaseAddr, Data, Data.Length, ref read); + } catch (Exception ex) { + throw new Exception($"Failed to read process memory: {ex.Message}"); + } } [DllImport("kernel32.dll")] @@ -391,13 +395,12 @@ public static byte[] ExtractSoFromApk(string path) { using (ZipArchive zip = ZipFile.OpenRead(path)) { - // Поиск основной библиотеки Unreal Engine в папках для 64-битных систем var entry = zip.Entries .Where(e => e.FullName.EndsWith(".so") && (e.FullName.Contains("arm64") || e.FullName.Contains("v8a"))) .OrderByDescending(e => e.Length) .FirstOrDefault(); - if (entry == null) throw new Exception("Could not find libUE4.so (arm64) inside the APK!"); + if (entry == null) throw new Exception("libUE4.so not found in APK!"); using (var s = entry.Open()) using (var ms = new MemoryStream()) @@ -410,10 +413,15 @@ public static byte[] ExtractSoFromApk(string path) private bool IsValidKey(byte[] k) { + // Отсекаем пустые или забитые одним цветом блоки if (k.All(b => b == 0) || k.All(b => b == 0xFF)) return false; + + // Считаем уникальные байты (энтропия) int unique = k.Distinct().Count(); - // Порог энтропии: 17 уникальных байт из 32 — стандарт для ключей UE 4.18 - 5.4 - return unique >= 17; + + // В реальном AES ключе должно быть много разных байтов (обычно > 20) + // Если поставить 17, будет много мусора. 21 — золотая середина. + return unique >= 21; } private long DecodeARM64(int i) @@ -437,12 +445,12 @@ public Dictionary FindAllPattern(out long ms) var res = new Dictionary(); var seen = new HashSet(); - // ШАГ 1: Поиск по инструкциям (Android ADRP/ADD для версий 4.25 - 5.4) + // ШАГ 1: Поиск по инструкциям (Для Android .so) if (isAndroid) { for (int i = 0; i < Data.Length - 12; i += 4) { - if ((Data[i + 3] & 0x9F) == 0x90) // Проверка опкода ADRP + if ((Data[i + 3] & 0x9F) == 0x90) // Опкод ADRP { long addr = DecodeARM64(i); if (addr > 0 && addr < Data.Length - 32) @@ -455,18 +463,20 @@ public Dictionary FindAllPattern(out long ms) } } - // ШАГ 2: Глубокое бинарное сканирование (ПК и старые версии Android 4.18 - 4.24) - if (res.Count == 0) + // ШАГ 2: Глубокое сканирование (Для ПК и старых версий) + // Сканируем с шагом 4 байта (выравнивание), чтобы не ловить дубли + if (res.Count == 0 || !isAndroid) { - for (int i = 0; i < Data.Length - 32; i++) + for (int i = 0; i < Data.Length - 32; i += 4) { if (Data[i] == 0x00 || Data[i] == 0xFF) continue; + byte[] k = new byte[32]; Buffer.BlockCopy(Data, i, k, 0, 32); if (IsValidKey(k)) { Add(res, seen, BaseAddr + (ulong)i, k); - i += 31; // Пропускаем длину найденного ключа + i += 28; // Прыгаем вперед, чтобы не сканировать тот же ключ } } } @@ -483,4 +493,3 @@ private void Add(Dictionary d, HashSet s, ulong a, byte[] } } } - From 05567799f9f3830336b97783b28e27431d19bcc5 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:47:55 +0500 Subject: [PATCH 70/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 99 ++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 35cccc0..c9d5a98 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -10,84 +10,65 @@ class Program { static void Main(string[] args) { - Console.Title = "Universal UE AES Finder | PC - SO - APK"; + Console.Title = "Universal UE AES Finder | Save to key.txt"; Console.WriteLine("=== Universal UE AES Key Finder (4.18 - 5.4) ==="); - Console.WriteLine("Select Mode:"); - Console.WriteLine("0: PC Process (Memory Scan)"); - Console.WriteLine("1: PC File (.exe / .dump)"); - Console.WriteLine("2: Android Library (.so)"); - Console.WriteLine("3: Android Package (.apk)"); - Console.Write("\nChoice: "); - + Console.WriteLine("0: PC Process\n1: PC File (.exe)\n2: Android (.so)\n3: APK File"); + Console.Write("\nSelect mode: "); + char mode = Console.ReadKey().KeyChar; - Console.WriteLine("\n"); + Console.WriteLine("\n\nEnter Target Name or Path:"); + string input = Console.ReadLine()?.Replace("\"", ""); try { Searcher searcher = null; - string input = ""; - - switch (mode) - { - case '0': - Console.Write("Enter Process Name (e.g. FortniteClient): "); - input = Console.ReadLine(); - Process target = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains(input, StringComparison.OrdinalIgnoreCase)); - if (target == null) throw new Exception("Process not found!"); - searcher = new Searcher(target); - break; - - case '1': - case '2': - Console.Write("Enter File Path: "); - input = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(input)) throw new Exception("File not found!"); - searcher = new Searcher(File.ReadAllBytes(input), mode == '2'); - break; - case '3': - Console.Write("Enter APK Path: "); - input = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(input)) throw new Exception("APK file not found!"); - Console.WriteLine("Extracting library from APK..."); - byte[] soData = Searcher.ExtractSoFromApk(input); - searcher = new Searcher(soData, true); - break; - - default: - Console.WriteLine("Invalid mode."); - return; + if (mode == '0') { + var p = Process.GetProcessesByName(input).FirstOrDefault(); + if (p == null) throw new Exception("Process not found!"); + searcher = new Searcher(p); + } + else if (mode == '1' || mode == '2') { + searcher = new Searcher(File.ReadAllBytes(input), mode == '2'); + } + else if (mode == '3') { + Console.WriteLine("Extracting .so from APK..."); + searcher = new Searcher(Searcher.ExtractSoFromApk(input), true); } - Console.WriteLine("Scanning... Please wait."); + Console.WriteLine("Searching for keys..."); var keys = searcher.FindAllPattern(out long ms); - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\nFound {keys.Count} keys in {ms} ms:"); - Console.ResetColor(); - - foreach (var k in keys) + // ЗАПИСЬ В ФАЙЛ + using (StreamWriter sw = new StreamWriter("key.txt")) { - string hex = k.Value; - byte[] keyBytes = Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) - .ToArray(); + sw.WriteLine($"Search results | Found {keys.Count} keys in {ms}ms"); + sw.WriteLine(new string('=', 40)); - Console.WriteLine("--------------------------------------------------"); - Console.WriteLine($"HEX: 0x{hex}"); - Console.WriteLine($"Base64: {Convert.ToBase64String(keyBytes)}"); - Console.WriteLine($"Offset: 0x{k.Key:X}"); + foreach (var k in keys) + { + byte[] bytes = Enumerable.Range(0, 32) + .Select(x => Convert.ToByte(k.Value.Substring(x * 2, 2), 16)).ToArray(); + string b64 = Convert.ToBase64String(bytes); + + string result = $"HEX: 0x{k.Value}\nB64: {b64}\nOffset: 0x{k.Key:X}\n"; + + Console.WriteLine(result); + sw.WriteLine(result + "------------------------------------"); + } } + + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"\nSuccess! {keys.Count} keys saved to 'key.txt'"); + Console.ResetColor(); } - catch (Exception ex) - { + catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"\nError: {ex.Message}"); + Console.WriteLine("\nError: " + ex.Message); Console.ResetColor(); } - Console.WriteLine("\nPress Enter to exit."); + Console.WriteLine("Press Enter to exit."); Console.ReadLine(); } } From 0abdb796689617e8d88729960e4aefd1f5c31c34 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:59:17 +0500 Subject: [PATCH 71/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 244 +++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 91 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index f86a4b5..8f62de6 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -363,133 +363,195 @@ public static class Win32 using System.Diagnostics; using System.IO; using System.IO.Compression; -using System.Linq; +using System.Text; +using System.Text.RegularExpressions; using System.Runtime.InteropServices; +using System.Linq; -namespace UEAesKeyFinder +public class Searcher { - public class Searcher - { - private byte[] Data; - private bool isAndroid; - private ulong BaseAddr = 0; + private bool useUE4Lib = false; + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } - public Searcher(byte[] data, bool android = false) { Data = data; isAndroid = android; } + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - public Searcher(Process p) + // Чтение памяти по 2048 байт с проверкой границ + for (int i = 0; i < ProcessMemory.Length; i += 2048) { - try { - BaseAddr = (ulong)p.MainModule.BaseAddress; - Data = new byte[p.MainModule.ModuleMemorySize]; - int read = 0; - ReadProcessMemory(p.Handle, BaseAddr, Data, Data.Length, ref read); - } catch (Exception ex) { - throw new Exception($"Failed to read process memory: {ex.Message}"); + int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + byte[] buffer = new byte[bytesToRead]; + if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, buffer, bytesToRead, out _)) + { + Buffer.BlockCopy(buffer, 0, ProcessMemory, i, bytesToRead); } } + } - [DllImport("kernel32.dll")] - private static extern bool ReadProcessMemory(IntPtr h, ulong b, byte[] buf, int s, ref int r); + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } - public static byte[] ExtractSoFromApk(string path) + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + { + if (isAPK) { - using (ZipArchive zip = ZipFile.OpenRead(path)) + // Поиск "APK Sig Block" + int sigOffset = FindPattern(bytes, Encoding.ASCII.GetBytes("APK Sig Block")); + if (sigOffset == -1) throw new Exception("APK Sig Block not found!"); + + // Поиск libUE4.so + byte[] libName = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + int nameOffset = FindPattern(bytes, libName, sigOffset); + if (nameOffset == -1) throw new Exception("libUE4.so entry not found!"); + + // Извлечение размеров из ZIP-заголовка (оффсеты -4 от имени файла обычно содержат метаданные) + int localHeaderOffset = BitConverter.ToInt32(bytes, nameOffset - 4); + int compressedSize = BitConverter.ToInt32(bytes, localHeaderOffset + 18); + int uncompressedSize = BitConverter.ToInt32(bytes, localHeaderOffset + 22); + int nameLen = BitConverter.ToInt16(bytes, localHeaderOffset + 26); + int extraLen = BitConverter.ToInt16(bytes, localHeaderOffset + 28); + int dataStart = localHeaderOffset + 30 + nameLen + extraLen; + + using (var ms = new MemoryStream(bytes, dataStart, compressedSize)) + using (var ds = new DeflateStream(ms, CompressionMode.Decompress)) + using (var output = new MemoryStream()) { - var entry = zip.Entries - .Where(e => e.FullName.EndsWith(".so") && (e.FullName.Contains("arm64") || e.FullName.Contains("v8a"))) - .OrderByDescending(e => e.Length) - .FirstOrDefault(); - - if (entry == null) throw new Exception("libUE4.so not found in APK!"); - - using (var s = entry.Open()) - using (var ms = new MemoryStream()) - { - s.CopyTo(ms); - return ms.ToArray(); - } + ds.CopyTo(output); + ProcessMemory = output.ToArray(); } } - - private bool IsValidKey(byte[] k) + else { - // Отсекаем пустые или забитые одним цветом блоки - if (k.All(b => b == 0) || k.All(b => b == 0xFF)) return false; - - // Считаем уникальные байты (энтропия) - int unique = k.Distinct().Count(); - - // В реальном AES ключе должно быть много разных байтов (обычно > 20) - // Если поставить 17, будет много мусора. 21 — золотая середина. - return unique >= 21; + ProcessMemory = bytes; } + useUE4Lib = useAndroid; + } - private long DecodeARM64(int i) + private int FindPattern(byte[] source, byte[] pattern, int startOffset = 0) + { + for (int i = startOffset; i <= source.Length - pattern.Length; i++) { - try - { - uint adrp = BitConverter.ToUInt32(Data, i); - uint add = BitConverter.ToUInt32(Data, i + 4); - long immhi = (adrp >> 5) & 0x7FFFF; - long immlo = (adrp >> 29) & 0x3; - long imm = (immhi << 2) | immlo; - if ((imm & 0x100000) != 0) imm |= ~0xFFFFF; - return (i & ~0xFFF) + (imm << 12) + ((add >> 10) & 0xFFF); - } - catch { return -1; } + if (source[i] != pattern[0]) continue; + if (source.Skip(i).Take(pattern.Length).SequenceEqual(pattern)) return i; } + return -1; + } - public Dictionary FindAllPattern(out long ms) + public string SearchEngineVersion() + { + if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) + return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + byte[] productVersionPattern = { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; + + int idx = FindPattern(ProcessMemory, productVersionPattern); + if (idx != -1 && idx + productVersionPattern.Length + 12 <= ProcessMemory.Length) { - Stopwatch sw = Stopwatch.StartNew(); - var res = new Dictionary(); - var seen = new HashSet(); + return Encoding.Unicode.GetString(ProcessMemory, idx + productVersionPattern.Length, 12).TrimEnd('\0'); + } + return ""; + } + + public int FollowJMP(int addr, int depth = 0) + { + if (depth > 10 || addr + 5 >= ProcessMemory.Length) return addr; // Защита от бесконечной рекурсии + + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int target = addr + offset + 5; + + if (target >= 0 && target + 5 < ProcessMemory.Length && ProcessMemory[target] == 0xE9) + return FollowJMP(target, depth + 1); - // ШАГ 1: Поиск по инструкциям (Для Android .so) - if (isAndroid) + return target; + } + + public Dictionary FindAllPattern(out long elapsed) + { + Stopwatch sw = Stopwatch.StartNew(); + var results = new Dictionary(); + + if (useUE4Lib) + { + // ARM64 ADRP/ADD Pattern + byte[] armPattern = { 0x01, 0x01, 0x40, 0xAD, 0x01, 0x00, 0x00, 0xAD, 0xC0, 0x03, 0x5F, 0xD6 }; + for (int i = 8; i < ProcessMemory.Length - 12; i++) { - for (int i = 0; i < Data.Length - 12; i += 4) + if (ProcessMemory.Skip(i).Take(armPattern.Length).SequenceEqual(armPattern)) { - if ((Data[i + 3] & 0x9F) == 0x90) // Опкод ADRP + int addr = GetADRLAddress(i - 8); + if (addr > 0 && addr + 32 <= ProcessMemory.Length) { - long addr = DecodeARM64(i); - if (addr > 0 && addr < Data.Length - 32) - { - byte[] k = new byte[32]; - Buffer.BlockCopy(Data, (int)addr, k, 0, 32); - if (IsValidKey(k)) Add(res, seen, (ulong)addr, k); - } + string key = BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); + results[AllocationBase + (ulong)addr] = "0x" + key; } } } - - // ШАГ 2: Глубокое сканирование (Для ПК и старых версий) - // Сканируем с шагом 4 байта (выравнивание), чтобы не ловить дубли - if (res.Count == 0 || !isAndroid) + } + else + { + // X64 Pattern (C7 45 D0 ...) + for (int i = 0; i < ProcessMemory.Length - 64; i++) { - for (int i = 0; i < Data.Length - 32; i += 4) + if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x01)) { - if (Data[i] == 0x00 || Data[i] == 0xFF) continue; + // Упрощенная логика сбора ключа из 8 частей (mov [ebp-X], 4-bytes) + StringBuilder sb = new StringBuilder(); + int currentIdx = i; + bool valid = true; - byte[] k = new byte[32]; - Buffer.BlockCopy(Data, i, k, 0, 32); - if (IsValidKey(k)) + for (int j = 0; j < 8; j++) { - Add(res, seen, BaseAddr + (ulong)i, k); - i += 28; // Прыгаем вперед, чтобы не сканировать тот же ключ + if (currentIdx + 7 > ProcessMemory.Length || ProcessMemory[currentIdx] != 0xC7) { valid = false; break; } + sb.Append(BitConverter.ToString(ProcessMemory, currentIdx + 3, 4).Replace("-", "")); + currentIdx += 7; + if (currentIdx < ProcessMemory.Length && ProcessMemory[currentIdx] == 0xE9) currentIdx = FollowJMP(currentIdx); } + + if (valid && sb.Length == 64) + results[AllocationBase + (ulong)i] = "0x" + sb.ToString(); } } - - sw.Stop(); - ms = sw.ElapsedMilliseconds; - return res; } - private void Add(Dictionary d, HashSet s, ulong a, byte[] k) - { - string h = BitConverter.ToString(k).Replace("-", ""); - if (s.Add(h)) d[a] = h; - } + sw.Stop(); + elapsed = sw.ElapsedMilliseconds; + return results; + } + + // Вспомогательные методы для ARM64 (оставлены без изменений логики, но добавлены проверки) + public int GetADRLAddress(int loc) + { + try { + ulong adrp = DecodeADRP(BitConverter.ToInt32(ProcessMemory, loc)); + ulong add = DecodeADD(BitConverter.ToInt32(ProcessMemory, loc + 4)); + return (int)((((ulong)loc & 0xFFFFF000) + adrp + add) & 0xFFFFFFFF); + } catch { return -1; } + } + + private ulong DecodeADRP(int ins) { + long imm = ((ins >> 29) & 3) | (((ins >> 5) & 0x7FFFF) << 2); + if ((imm & 0x100000) != 0) imm |= -1L << 21; + return (ulong)(imm << 12); + } + + private ulong DecodeADD(int ins) => (ulong)((ins >> 10) & 0xFFF); + + public static class Win32 + { + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); } } From e002f405dc3215844a36880ecc4f7d2181bb28b6 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 21:59:41 +0500 Subject: [PATCH 72/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 215 +++++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 47 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index c9d5a98..48694ce 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -2,73 +2,194 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using static Searcher; namespace UEAesKeyFinder { class Program { + [DllImport("ntdll.dll", PreserveSig = false)] + public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) + { + var r = new byte[hex.Length / 2]; + for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return r; + } static void Main(string[] args) { - Console.Title = "Universal UE AES Finder | Save to key.txt"; - Console.WriteLine("=== Universal UE AES Key Finder (4.18 - 5.4) ==="); - Console.WriteLine("0: PC Process\n1: PC File (.exe)\n2: Android (.so)\n3: APK File"); - Console.Write("\nSelect mode: "); - - char mode = Console.ReadKey().KeyChar; - Console.WriteLine("\n\nEnter Target Name or Path:"); - string input = Console.ReadLine()?.Replace("\"", ""); - - try + Searcher searcher = new Searcher(); + Process game = new Process(); + + Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); + + char method = (char)Console.Read(); + string path; + string EngineVersion = "4.18.0"; + string saveName = ""; + switch (method) { - Searcher searcher = null; + case '0': + Console.Write("Enter the name or id of the process: "); + Console.Read(); + Console.Read(); + string ProcessName = Console.ReadLine(); - if (mode == '0') { - var p = Process.GetProcessesByName(input).FirstOrDefault(); - if (p == null) throw new Exception("Process not found!"); - searcher = new Searcher(p); - } - else if (mode == '1' || mode == '2') { - searcher = new Searcher(File.ReadAllBytes(input), mode == '2'); - } - else if (mode == '3') { - Console.WriteLine("Extracting .so from APK..."); - searcher = new Searcher(Searcher.ExtractSoFromApk(input), true); - } + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) + { + Console.WriteLine($"\nFound {p.ProcessName}"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the process."); + Console.ReadLine(); + return; + } + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '1': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } - Console.WriteLine("Searching for keys..."); - var keys = searcher.FindAllPattern(out long ms); + saveName = path.Split("\\")[path.Split("\\").Length - 1]; - // ЗАПИСЬ В ФАЙЛ - using (StreamWriter sw = new StreamWriter("key.txt")) - { - sw.WriteLine($"Search results | Found {keys.Count} keys in {ms}ms"); - sw.WriteLine(new string('=', 40)); + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1000); + // Not required to fully load + NtSuspendProcess(game.Handle); - foreach (var k in keys) + searcher = new Searcher(game); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") { - byte[] bytes = Enumerable.Range(0, 32) - .Select(x => Convert.ToByte(k.Value.Substring(x * 2, 2), 16)).ToArray(); - string b64 = Convert.ToBase64String(bytes); - - string result = $"HEX: 0x{k.Value}\nB64: {b64}\nOffset: 0x{k.Key:X}\n"; - - Console.WriteLine(result); - sw.WriteLine(result + "------------------------------------"); + Console.WriteLine($"Engine Version: {EngineVersion}"); } - } + break; + case '2': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length-1]; + + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '3': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the lib."); + return; + } + + searcher = new Searcher(File.ReadAllBytes(path), true); + break; + case '4': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the apk."); + return; + } + searcher = new Searcher(File.ReadAllBytes(path), true, true); + break; + } + + Dictionary aesKeys = searcher.FindAllPattern(out long took); + + if (aesKeys.Count > 0) + { + string WriteToFile = ""; + string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + + WriteToFile += txt; Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"\nSuccess! {keys.Count} keys saved to 'key.txt'"); - Console.ResetColor(); + Console.Write("\n" + txt); + Console.ForegroundColor = ConsoleColor.White; + int EngineVersionI = 17; + if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); + if (EngineVersionI < 18) + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + else + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + + File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); } - catch (Exception ex) { + else + { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nError: " + ex.Message); - Console.ResetColor(); + Console.WriteLine("\nFailed to find any AES Keys."); } - Console.WriteLine("Press Enter to exit."); + if (method == '1') try { game.Kill(); } catch { }; + Console.ReadLine(); } } From 21b2146469af5f06c853ed954bfba5c2a6d2b574 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:07:29 +0500 Subject: [PATCH 73/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 147 +++++++++++++------------------------ 1 file changed, 52 insertions(+), 95 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 8f62de6..e35835c 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -364,7 +364,6 @@ public static class Win32 using System.IO; using System.IO.Compression; using System.Text; -using System.Text.RegularExpressions; using System.Runtime.InteropServices; using System.Linq; @@ -379,6 +378,12 @@ public class Searcher public Searcher() { } + // Важно для Program.cs (метод 1 и 2) + public void SetFilePath(string path) + { + FilePath = path; + } + public Searcher(Process p) { Process = p; @@ -386,10 +391,9 @@ public Searcher(Process p) AllocationBase = (ulong)p.MainModule.BaseAddress; ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - // Чтение памяти по 2048 байт с проверкой границ - for (int i = 0; i < ProcessMemory.Length; i += 2048) + for (int i = 0; i < ProcessMemory.Length; i += 4096) { - int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + int bytesToRead = Math.Min(4096, ProcessMemory.Length - i); byte[] buffer = new byte[bytesToRead]; if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, buffer, bytesToRead, out _)) { @@ -398,32 +402,18 @@ public Searcher(Process p) } } - public Searcher(byte[] bytes) - { - AllocationBase = 0; - ProcessMemory = bytes; - } - - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) { if (isAPK) { - // Поиск "APK Sig Block" - int sigOffset = FindPattern(bytes, Encoding.ASCII.GetBytes("APK Sig Block")); - if (sigOffset == -1) throw new Exception("APK Sig Block not found!"); - - // Поиск libUE4.so + // Упрощенный поиск libUE4.so в APK byte[] libName = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - int nameOffset = FindPattern(bytes, libName, sigOffset); - if (nameOffset == -1) throw new Exception("libUE4.so entry not found!"); + int nameOffset = FindPattern(bytes, libName); + if (nameOffset == -1) throw new Exception("libUE4.so not found!"); - // Извлечение размеров из ZIP-заголовка (оффсеты -4 от имени файла обычно содержат метаданные) int localHeaderOffset = BitConverter.ToInt32(bytes, nameOffset - 4); int compressedSize = BitConverter.ToInt32(bytes, localHeaderOffset + 18); - int uncompressedSize = BitConverter.ToInt32(bytes, localHeaderOffset + 22); - int nameLen = BitConverter.ToInt16(bytes, localHeaderOffset + 26); - int extraLen = BitConverter.ToInt16(bytes, localHeaderOffset + 28); - int dataStart = localHeaderOffset + 30 + nameLen + extraLen; + int dataStart = localHeaderOffset + 30 + BitConverter.ToInt16(bytes, localHeaderOffset + 26) + BitConverter.ToInt16(bytes, localHeaderOffset + 28); using (var ms = new MemoryStream(bytes, dataStart, compressedSize)) using (var ds = new DeflateStream(ms, CompressionMode.Decompress)) @@ -440,42 +430,15 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) useUE4Lib = useAndroid; } - private int FindPattern(byte[] source, byte[] pattern, int startOffset = 0) - { - for (int i = startOffset; i <= source.Length - pattern.Length; i++) - { - if (source[i] != pattern[0]) continue; - if (source.Skip(i).Take(pattern.Length).SequenceEqual(pattern)) return i; - } - return -1; - } - public string SearchEngineVersion() { if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - byte[] productVersionPattern = { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; - int idx = FindPattern(ProcessMemory, productVersionPattern); - if (idx != -1 && idx + productVersionPattern.Length + 12 <= ProcessMemory.Length) - { - return Encoding.Unicode.GetString(ProcessMemory, idx + productVersionPattern.Length, 12).TrimEnd('\0'); - } - return ""; - } - - public int FollowJMP(int addr, int depth = 0) - { - if (depth > 10 || addr + 5 >= ProcessMemory.Length) return addr; // Защита от бесконечной рекурсии - - int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); - int target = addr + offset + 5; - - if (target >= 0 && target + 5 < ProcessMemory.Length && ProcessMemory[target] == 0xE9) - return FollowJMP(target, depth + 1); - - return target; + // Поиск версии в памяти (для UE4 обычно формат 4.X.X) + string memStr = Encoding.ASCII.GetString(ProcessMemory); + var match = Regex.Match(memStr, @"4\.\d+\.\d+"); + return match.Success ? match.Value : "4.18.1"; } public Dictionary FindAllPattern(out long elapsed) @@ -485,73 +448,67 @@ public Dictionary FindAllPattern(out long elapsed) if (useUE4Lib) { - // ARM64 ADRP/ADD Pattern - byte[] armPattern = { 0x01, 0x01, 0x40, 0xAD, 0x01, 0x00, 0x00, 0xAD, 0xC0, 0x03, 0x5F, 0xD6 }; - for (int i = 8; i < ProcessMemory.Length - 12; i++) + // ARM64 ADRP/ADD + byte[] armPattern = { 0x01, 0x01, 0x40, 0xAD, 0x01, 0x00, 0x00, 0xAD }; + for (int i = 0; i < ProcessMemory.Length - 16; i++) { - if (ProcessMemory.Skip(i).Take(armPattern.Length).SequenceEqual(armPattern)) - { - int addr = GetADRLAddress(i - 8); + if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { + int addr = GetADRLAddress(i); if (addr > 0 && addr + 32 <= ProcessMemory.Length) - { - string key = BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); - results[AllocationBase + (ulong)addr] = "0x" + key; - } + results[AllocationBase + (ulong)addr] = "0x" + BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); } } } else { - // X64 Pattern (C7 45 D0 ...) - for (int i = 0; i < ProcessMemory.Length - 64; i++) + // X64 (UE 4.18.1+) сборка ключа из инструкций mov + for (int i = 0; i < ProcessMemory.Length - 100; i++) { - if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x01)) + if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) { - // Упрощенная логика сбора ключа из 8 частей (mov [ebp-X], 4-bytes) StringBuilder sb = new StringBuilder(); - int currentIdx = i; - bool valid = true; - + int curr = i; + bool fail = false; for (int j = 0; j < 8; j++) { - if (currentIdx + 7 > ProcessMemory.Length || ProcessMemory[currentIdx] != 0xC7) { valid = false; break; } - sb.Append(BitConverter.ToString(ProcessMemory, currentIdx + 3, 4).Replace("-", "")); - currentIdx += 7; - if (currentIdx < ProcessMemory.Length && ProcessMemory[currentIdx] == 0xE9) currentIdx = FollowJMP(currentIdx); + if (curr + 7 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { fail = true; break; } + sb.Append(BitConverter.ToString(ProcessMemory, curr + 3, 4).Replace("-", "")); + curr += 7; + if (curr < ProcessMemory.Length && ProcessMemory[curr] == 0xE9) curr = FollowJMP(curr); } - - if (valid && sb.Length == 64) - results[AllocationBase + (ulong)i] = "0x" + sb.ToString(); + if (!fail && sb.Length == 64) results[AllocationBase + (ulong)i] = "0x" + sb.ToString(); } } } - sw.Stop(); elapsed = sw.ElapsedMilliseconds; return results; } - // Вспомогательные методы для ARM64 (оставлены без изменений логики, но добавлены проверки) - public int GetADRLAddress(int loc) - { + private int FollowJMP(int addr) { + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + return addr + offset + 5; + } + + private int GetADRLAddress(int loc) { try { - ulong adrp = DecodeADRP(BitConverter.ToInt32(ProcessMemory, loc)); - ulong add = DecodeADD(BitConverter.ToInt32(ProcessMemory, loc + 4)); - return (int)((((ulong)loc & 0xFFFFF000) + adrp + add) & 0xFFFFFFFF); + int ins = BitConverter.ToInt32(ProcessMemory, loc); + long imm = ((ins >> 29) & 3) | (((ins >> 5) & 0x7FFFF) << 2); + if ((imm & 0x100000) != 0) imm |= -1L << 21; + return (int)((((ulong)loc & 0xFFFFF000) + (ulong)(imm << 12) + (ulong)((BitConverter.ToInt32(ProcessMemory, loc + 4) >> 10) & 0xFFF)) & 0xFFFFFFFF); } catch { return -1; } } - private ulong DecodeADRP(int ins) { - long imm = ((ins >> 29) & 3) | (((ins >> 5) & 0x7FFFF) << 2); - if ((imm & 0x100000) != 0) imm |= -1L << 21; - return (ulong)(imm << 12); + private int FindPattern(byte[] src, byte[] pat) { + for (int i = 0; i <= src.Length - pat.Length; i++) { + if (src[i] == pat[0] && src.Skip(i).Take(pat.Length).SequenceEqual(pat)) return i; + } + return -1; } - private ulong DecodeADD(int ins) => (ulong)((ins >> 10) & 0xFFF); - - public static class Win32 - { - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); + public static class Win32 { + [DllImport("kernel32.dll")] + public static extern bool ReadProcessMemory(IntPtr h, ulong addr, [Out] byte[] buf, int size, out int read); } } + From da4863cb78ad902c4423b51df9c485bd93d6a0d0 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:13:38 +0500 Subject: [PATCH 74/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index e35835c..335fd78 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -507,8 +507,13 @@ private int FindPattern(byte[] src, byte[] pat) { } public static class Win32 { + [DllImport("kernel32.dll", SetLastError = true)] + // Добавлен out int lpNumberOfBytesRead для совместимости с вызовом через out _ + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); + [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr h, ulong addr, [Out] byte[] buf, int size, out int read); + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } } + From 8d7ab4ba3a1daae52a90135b86472e76fcfebcff Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:14:40 +0500 Subject: [PATCH 75/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 335fd78..6666fc8 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -509,7 +509,7 @@ private int FindPattern(byte[] src, byte[] pat) { public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] // Добавлен out int lpNumberOfBytesRead для совместимости с вызовом через out _ - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead = 0); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); @@ -517,3 +517,4 @@ public static class Win32 { } + From e7f2a1464340929c5e93ec988f1a58d398747d60 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:19:14 +0500 Subject: [PATCH 76/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 6666fc8..7c98f49 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -509,7 +509,7 @@ private int FindPattern(byte[] src, byte[] pat) { public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] // Добавлен out int lpNumberOfBytesRead для совместимости с вызовом через out _ - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead = 0); + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); @@ -518,3 +518,4 @@ public static class Win32 { + From afeb0d0de5a3d3f5ac8728ebca0f56f2d3f3573f Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:25:25 +0500 Subject: [PATCH 77/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 7c98f49..a499aa6 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -366,6 +366,7 @@ public static class Win32 using System.Text; using System.Runtime.InteropServices; using System.Linq; +using System.Text.RegularExpressions; // ИСПРАВЛЕНО: Добавлено для работы Regex public class Searcher { @@ -378,7 +379,6 @@ public class Searcher public Searcher() { } - // Важно для Program.cs (метод 1 и 2) public void SetFilePath(string path) { FilePath = path; @@ -395,6 +395,7 @@ public Searcher(Process p) { int bytesToRead = Math.Min(4096, ProcessMemory.Length - i); byte[] buffer = new byte[bytesToRead]; + // Исправлено: Работает с out int в Win32 if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, buffer, bytesToRead, out _)) { Buffer.BlockCopy(buffer, 0, ProcessMemory, i, bytesToRead); @@ -406,7 +407,6 @@ public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) { if (isAPK) { - // Упрощенный поиск libUE4.so в APK byte[] libName = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); int nameOffset = FindPattern(bytes, libName); if (nameOffset == -1) throw new Exception("libUE4.so not found!"); @@ -435,10 +435,13 @@ public string SearchEngineVersion() if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - // Поиск версии в памяти (для UE4 обычно формат 4.X.X) - string memStr = Encoding.ASCII.GetString(ProcessMemory); - var match = Regex.Match(memStr, @"4\.\d+\.\d+"); - return match.Success ? match.Value : "4.18.1"; + // ИСПРАВЛЕНО: Безопасное чтение версии без OutOfMemory + try { + int checkLength = Math.Min(ProcessMemory.Length, 5000000); + string memStr = Encoding.ASCII.GetString(ProcessMemory, 0, checkLength); + var match = Regex.Match(memStr, @"4\.\d+\.\d+"); + return match.Success ? match.Value : "4.18.1"; + } catch { return "4.18.1"; } } public Dictionary FindAllPattern(out long elapsed) @@ -448,8 +451,6 @@ public Dictionary FindAllPattern(out long elapsed) if (useUE4Lib) { - // ARM64 ADRP/ADD - byte[] armPattern = { 0x01, 0x01, 0x40, 0xAD, 0x01, 0x00, 0x00, 0xAD }; for (int i = 0; i < ProcessMemory.Length - 16; i++) { if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { @@ -461,7 +462,6 @@ public Dictionary FindAllPattern(out long elapsed) } else { - // X64 (UE 4.18.1+) сборка ключа из инструкций mov for (int i = 0; i < ProcessMemory.Length - 100; i++) { if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) @@ -486,6 +486,7 @@ public Dictionary FindAllPattern(out long elapsed) } private int FollowJMP(int addr) { + if (addr + 5 > ProcessMemory.Length) return addr; int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); return addr + offset + 5; } @@ -501,14 +502,18 @@ private int GetADRLAddress(int loc) { private int FindPattern(byte[] src, byte[] pat) { for (int i = 0; i <= src.Length - pat.Length; i++) { - if (src[i] == pat[0] && src.Skip(i).Take(pat.Length).SequenceEqual(pat)) return i; + bool match = true; + for (int j = 0; j < pat.Length; j++) { + if (src[i + j] != pat[j]) { match = false; break; } + } + if (match) return i; } return -1; } public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] - // Добавлен out int lpNumberOfBytesRead для совместимости с вызовом через out _ + // ИСПРАВЛЕНО: Параметр out без значения по умолчанию (ошибка CS1741) public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll")] @@ -519,3 +524,4 @@ public static class Win32 { + From 0db9862d855d9482eca9a5e90a2d3083be92cc5d Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:34:50 +0500 Subject: [PATCH 78/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 50 ++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index a499aa6..fc3f6a4 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -366,7 +366,7 @@ public static class Win32 using System.Text; using System.Runtime.InteropServices; using System.Linq; -using System.Text.RegularExpressions; // ИСПРАВЛЕНО: Добавлено для работы Regex +using System.Text.RegularExpressions; public class Searcher { @@ -395,7 +395,6 @@ public Searcher(Process p) { int bytesToRead = Math.Min(4096, ProcessMemory.Length - i); byte[] buffer = new byte[bytesToRead]; - // Исправлено: Работает с out int в Win32 if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, buffer, bytesToRead, out _)) { Buffer.BlockCopy(buffer, 0, ProcessMemory, i, bytesToRead); @@ -435,7 +434,6 @@ public string SearchEngineVersion() if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - // ИСПРАВЛЕНО: Безопасное чтение версии без OutOfMemory try { int checkLength = Math.Min(ProcessMemory.Length, 5000000); string memStr = Encoding.ASCII.GetString(ProcessMemory, 0, checkLength); @@ -444,10 +442,27 @@ public string SearchEngineVersion() } catch { return "4.18.1"; } } + // НОВЫЙ МЕТОД: Проверяет, похожи ли байты на реальный AES ключ + private bool IsValidKey(byte[] key) + { + if (key == null || key.Length != 32) return false; + + // 1. Убираем ключи, состоящие только из нулей или одинаковых байт + int distinct = key.Distinct().Count(); + if (distinct < 8) return false; + + // 2. Убираем ключи, которые выглядят как обычный текст (слишком много ASCII символов) + int asciiCount = key.Count(b => b >= 32 && b <= 126); + if (asciiCount > 25) return false; + + return true; + } + public Dictionary FindAllPattern(out long elapsed) { Stopwatch sw = Stopwatch.StartNew(); var results = new Dictionary(); + var seenKeys = new HashSet(); // Чтобы не дублировать одинаковые ключи if (useUE4Lib) { @@ -456,7 +471,17 @@ public Dictionary FindAllPattern(out long elapsed) if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { int addr = GetADRLAddress(i); if (addr > 0 && addr + 32 <= ProcessMemory.Length) - results[AllocationBase + (ulong)addr] = "0x" + BitConverter.ToString(ProcessMemory, addr, 32).Replace("-", ""); + { + byte[] potentialKey = new byte[32]; + Buffer.BlockCopy(ProcessMemory, addr, potentialKey, 0, 32); + + if (IsValidKey(potentialKey)) + { + string hex = "0x" + BitConverter.ToString(potentialKey).Replace("-", ""); + if (seenKeys.Add(hex)) // Добавляем только если такого ключа еще не было + results[AllocationBase + (ulong)addr] = hex; + } + } } } } @@ -469,14 +494,24 @@ public Dictionary FindAllPattern(out long elapsed) StringBuilder sb = new StringBuilder(); int curr = i; bool fail = false; + byte[] keyBuffer = new byte[32]; + for (int j = 0; j < 8; j++) { if (curr + 7 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { fail = true; break; } - sb.Append(BitConverter.ToString(ProcessMemory, curr + 3, 4).Replace("-", "")); + byte[] part = new byte[4]; + Buffer.BlockCopy(ProcessMemory, curr + 3, part, 0, 4); + Buffer.BlockCopy(part, 0, keyBuffer, j * 4, 4); curr += 7; if (curr < ProcessMemory.Length && ProcessMemory[curr] == 0xE9) curr = FollowJMP(curr); } - if (!fail && sb.Length == 64) results[AllocationBase + (ulong)i] = "0x" + sb.ToString(); + + if (!fail && IsValidKey(keyBuffer)) + { + string hex = "0x" + BitConverter.ToString(keyBuffer).Replace("-", ""); + if (seenKeys.Add(hex)) + results[AllocationBase + (ulong)i] = hex; + } } } } @@ -513,9 +548,7 @@ private int FindPattern(byte[] src, byte[] pat) { public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] - // ИСПРАВЛЕНО: Параметр out без значения по умолчанию (ошибка CS1741) public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); - [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } @@ -525,3 +558,4 @@ public static class Win32 { + From b0160d36f9074ce1b24a50751b1e208c41b8ad65 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:45:15 +0500 Subject: [PATCH 79/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index fc3f6a4..c17abb4 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -462,10 +462,11 @@ public Dictionary FindAllPattern(out long elapsed) { Stopwatch sw = Stopwatch.StartNew(); var results = new Dictionary(); - var seenKeys = new HashSet(); // Чтобы не дублировать одинаковые ключи + var seenKeys = new HashSet(); if (useUE4Lib) { + // --- Логика для Android (libUE4.so) --- for (int i = 0; i < ProcessMemory.Length - 16; i++) { if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { @@ -478,8 +479,12 @@ public Dictionary FindAllPattern(out long elapsed) if (IsValidKey(potentialKey)) { string hex = "0x" + BitConverter.ToString(potentialKey).Replace("-", ""); - if (seenKeys.Add(hex)) // Добавляем только если такого ключа еще не было - results[AllocationBase + (ulong)addr] = hex; + if (seenKeys.Add(hex)) + { + string base64 = Convert.ToBase64String(potentialKey); + // Форматируем строку сразу как тебе нужно + results[AllocationBase + (ulong)addr] = $"{hex} ({base64}) at {AllocationBase + (ulong)addr}"; + } } } } @@ -487,11 +492,11 @@ public Dictionary FindAllPattern(out long elapsed) } else { + // --- Логика для Windows (x64) --- for (int i = 0; i < ProcessMemory.Length - 100; i++) { if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) { - StringBuilder sb = new StringBuilder(); int curr = i; bool fail = false; byte[] keyBuffer = new byte[32]; @@ -499,9 +504,7 @@ public Dictionary FindAllPattern(out long elapsed) for (int j = 0; j < 8; j++) { if (curr + 7 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { fail = true; break; } - byte[] part = new byte[4]; - Buffer.BlockCopy(ProcessMemory, curr + 3, part, 0, 4); - Buffer.BlockCopy(part, 0, keyBuffer, j * 4, 4); + Buffer.BlockCopy(ProcessMemory, curr + 3, keyBuffer, j * 4, 4); curr += 7; if (curr < ProcessMemory.Length && ProcessMemory[curr] == 0xE9) curr = FollowJMP(curr); } @@ -510,7 +513,11 @@ public Dictionary FindAllPattern(out long elapsed) { string hex = "0x" + BitConverter.ToString(keyBuffer).Replace("-", ""); if (seenKeys.Add(hex)) - results[AllocationBase + (ulong)i] = hex; + { + string base64 = Convert.ToBase64String(keyBuffer); + // Форматируем строку: 0xHEX (BASE64) at OFFSET + results[AllocationBase + (ulong)i] = $"{hex} ({base64}) at {AllocationBase + (ulong)i}"; + } } } } @@ -559,3 +566,4 @@ public static class Win32 { + From cefd138dfbb26be66b948f789fd911547a8268e7 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:51:21 +0500 Subject: [PATCH 80/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 176 +++++++++----------------------------- 1 file changed, 42 insertions(+), 134 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 48694ce..ea0880a 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,7 +4,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using static Searcher; +using System.Linq; namespace UEAesKeyFinder { @@ -12,184 +12,92 @@ class Program { [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) { + if (hex.StartsWith("0x")) hex = hex.Substring(2); var r = new byte[hex.Length / 2]; for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } + static void Main(string[] args) { Searcher searcher = new Searcher(); - Process game = new Process(); + Process game = null; Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); + + string input = Console.ReadLine(); + char method = string.IsNullOrEmpty(input) ? ' ' : input[0]; + string path; string EngineVersion = "4.18.0"; - string saveName = ""; + string saveName = "result"; + switch (method) { case '0': Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; + Process p = Process.GetProcesses().FirstOrDefault(x => x.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase) || x.Id.ToString() == ProcessName); + if (p != null) { + Console.WriteLine($"\nFound {p.ProcessName}"); + saveName = p.ProcessName; + searcher = new Searcher(p); + } else { Console.WriteLine("Failed to find the process."); - Console.ReadLine(); return; } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } break; case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path)); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } - - searcher = new Searcher(File.ReadAllBytes(path), true); - break; case '4': Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; + if (!File.Exists(path)) { Console.WriteLine("File not found."); return; } + saveName = Path.GetFileNameWithoutExtension(path); + + if (method == '1') { + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); Thread.Sleep(1500); NtSuspendProcess(game.Handle); + searcher = new Searcher(game); } - searcher = new Searcher(File.ReadAllBytes(path), true, true); + else if (method == '2') searcher = new Searcher(File.ReadAllBytes(path)); + else if (method == '3') searcher = new Searcher(File.ReadAllBytes(path), true); + else if (method == '4') searcher = new Searcher(File.ReadAllBytes(path), true, true); break; } + EngineVersion = searcher.SearchEngineVersion(); + if (!string.IsNullOrEmpty(EngineVersion)) Console.WriteLine($"Engine Version: {EngineVersion}"); + Dictionary aesKeys = searcher.FindAllPattern(out long took); if (aesKeys.Count > 0) { string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - + string txt = $"Found {aesKeys.Count} AES Key{(aesKeys.Count > 1 ? "s" : "")} in {took}ms\n"; Console.ForegroundColor = ConsoleColor.Green; Console.Write("\n" + txt); Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else + WriteToFile += txt; + + foreach (var o in aesKeys) { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; + string b64 = Convert.ToBase64String(GetHex(o.Value)); + // ФОРМАТ: 0xКЛЮЧ (BASE64) at АДРЕС + string line = $"{o.Value} ({b64}) at {o.Key}\n"; + Console.Write(line); + WriteToFile += line; } - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; + else Console.WriteLine("\nFailed to find any AES Keys."); + if (game != null) try { game.Kill(); } catch { } + Console.WriteLine("\nDone. Press Enter to exit."); Console.ReadLine(); } } From 885232dbdce30e5a3d85fd011829ed3195773e61 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 22:56:22 +0500 Subject: [PATCH 81/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 80 +++++++++----------------------------- 1 file changed, 19 insertions(+), 61 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index c17abb4..90069f2 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -379,10 +379,7 @@ public class Searcher public Searcher() { } - public void SetFilePath(string path) - { - FilePath = path; - } + public void SetFilePath(string path) => FilePath = path; public Searcher(Process p) { @@ -422,10 +419,7 @@ public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) ProcessMemory = output.ToArray(); } } - else - { - ProcessMemory = bytes; - } + else { ProcessMemory = bytes; } useUE4Lib = useAndroid; } @@ -442,19 +436,13 @@ public string SearchEngineVersion() } catch { return "4.18.1"; } } - // НОВЫЙ МЕТОД: Проверяет, похожи ли байты на реальный AES ключ private bool IsValidKey(byte[] key) { if (key == null || key.Length != 32) return false; - - // 1. Убираем ключи, состоящие только из нулей или одинаковых байт int distinct = key.Distinct().Count(); - if (distinct < 8) return false; - - // 2. Убираем ключи, которые выглядят как обычный текст (слишком много ASCII символов) + if (distinct < 10) return false; // Ключ не может состоять из одних нулей или одинаковых байт int asciiCount = key.Count(b => b >= 32 && b <= 126); - if (asciiCount > 25) return false; - + if (asciiCount > 20) return false; // Ключ — это не читаемый текст return true; } @@ -462,29 +450,20 @@ public Dictionary FindAllPattern(out long elapsed) { Stopwatch sw = Stopwatch.StartNew(); var results = new Dictionary(); - var seenKeys = new HashSet(); + var seenKeys = new HashSet(); if (useUE4Lib) { - // --- Логика для Android (libUE4.so) --- for (int i = 0; i < ProcessMemory.Length - 16; i++) { if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { int addr = GetADRLAddress(i); - if (addr > 0 && addr + 32 <= ProcessMemory.Length) - { - byte[] potentialKey = new byte[32]; - Buffer.BlockCopy(ProcessMemory, addr, potentialKey, 0, 32); - - if (IsValidKey(potentialKey)) - { - string hex = "0x" + BitConverter.ToString(potentialKey).Replace("-", ""); - if (seenKeys.Add(hex)) - { - string base64 = Convert.ToBase64String(potentialKey); - // Форматируем строку сразу как тебе нужно - results[AllocationBase + (ulong)addr] = $"{hex} ({base64}) at {AllocationBase + (ulong)addr}"; - } + if (addr > 0 && addr + 32 <= ProcessMemory.Length) { + byte[] key = new byte[32]; + Buffer.BlockCopy(ProcessMemory, addr, key, 0, 32); + if (IsValidKey(key)) { + string hex = "0x" + BitConverter.ToString(key).Replace("-", ""); + if (seenKeys.Add(hex)) results[AllocationBase + (ulong)addr] = hex; } } } @@ -492,32 +471,22 @@ public Dictionary FindAllPattern(out long elapsed) } else { - // --- Логика для Windows (x64) --- for (int i = 0; i < ProcessMemory.Length - 100; i++) { if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) { + byte[] keyBuffer = new byte[32]; int curr = i; bool fail = false; - byte[] keyBuffer = new byte[32]; - - for (int j = 0; j < 8; j++) - { + for (int j = 0; j < 8; j++) { if (curr + 7 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { fail = true; break; } Buffer.BlockCopy(ProcessMemory, curr + 3, keyBuffer, j * 4, 4); curr += 7; if (curr < ProcessMemory.Length && ProcessMemory[curr] == 0xE9) curr = FollowJMP(curr); } - - if (!fail && IsValidKey(keyBuffer)) - { + if (!fail && IsValidKey(keyBuffer)) { string hex = "0x" + BitConverter.ToString(keyBuffer).Replace("-", ""); - if (seenKeys.Add(hex)) - { - string base64 = Convert.ToBase64String(keyBuffer); - // Форматируем строку: 0xHEX (BASE64) at OFFSET - results[AllocationBase + (ulong)i] = $"{hex} ({base64}) at {AllocationBase + (ulong)i}"; - } + if (seenKeys.Add(hex)) results[AllocationBase + (ulong)i] = hex; } } } @@ -544,26 +513,15 @@ private int GetADRLAddress(int loc) { private int FindPattern(byte[] src, byte[] pat) { for (int i = 0; i <= src.Length - pat.Length; i++) { - bool match = true; - for (int j = 0; j < pat.Length; j++) { - if (src[i + j] != pat[j]) { match = false; break; } - } - if (match) return i; + bool m = true; + for (int j = 0; j < pat.Length; j++) if (src[i + j] != pat[j]) { m = false; break; } + if (m) return i; } return -1; } public static class Win32 { [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + public static extern bool ReadProcessMemory(IntPtr h, ulong a, [Out] byte[] b, int s, out int r); } } - - - - - - - From 82b2086841a595ddd9b90c491d3eca0f5923ff05 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 23:08:27 +0500 Subject: [PATCH 82/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 90069f2..0c5c64d 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -440,9 +440,9 @@ private bool IsValidKey(byte[] key) { if (key == null || key.Length != 32) return false; int distinct = key.Distinct().Count(); - if (distinct < 10) return false; // Ключ не может состоять из одних нулей или одинаковых байт - int asciiCount = key.Count(b => b >= 32 && b <= 126); - if (asciiCount > 20) return false; // Ключ — это не читаемый текст + if (distinct < 20) return false; // Ключ не может состоять из одних нулей или одинаковых байт + int zeroCount = key.Count(b => b == 0x00); + if (zeroCount > 5) return false; // Ключ — это не читаемый текст return true; } @@ -525,3 +525,4 @@ public static class Win32 { public static extern bool ReadProcessMemory(IntPtr h, ulong a, [Out] byte[] b, int s, out int r); } } + From 1ff9a973662eef81ece2e33f0580949833a57599 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 23:41:06 +0500 Subject: [PATCH 83/87] Refactor AES key search functionality in Program.cs --- UEAESKeyFinder/Program.cs | 176 +++++++++++++++++++++++++++++--------- 1 file changed, 134 insertions(+), 42 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index ea0880a..48694ce 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -4,7 +4,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using System.Linq; +using static Searcher; namespace UEAesKeyFinder { @@ -12,92 +12,184 @@ class Program { [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); - public static byte[] GetHex(string hex) { - if (hex.StartsWith("0x")) hex = hex.Substring(2); var r = new byte[hex.Length / 2]; for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } - static void Main(string[] args) { Searcher searcher = new Searcher(); - Process game = null; + Process game = new Process(); Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - string input = Console.ReadLine(); - char method = string.IsNullOrEmpty(input) ? ' ' : input[0]; - + + char method = (char)Console.Read(); string path; string EngineVersion = "4.18.0"; - string saveName = "result"; - + string saveName = ""; switch (method) { case '0': Console.Write("Enter the name or id of the process: "); + Console.Read(); + Console.Read(); string ProcessName = Console.ReadLine(); - Process p = Process.GetProcesses().FirstOrDefault(x => x.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase) || x.Id.ToString() == ProcessName); - if (p != null) { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - } else { + + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) + { + Console.WriteLine($"\nFound {p.ProcessName}"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) + { + Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Failed to find the process."); + Console.ReadLine(); return; } + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } break; case '1': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1000); + // Not required to fully load + NtSuspendProcess(game.Handle); + + searcher = new Searcher(game); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; case '2': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length-1]; + + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; case '3': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the lib."); + return; + } + + searcher = new Searcher(File.ReadAllBytes(path), true); + break; case '4': Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) { Console.WriteLine("File not found."); return; } - saveName = Path.GetFileNameWithoutExtension(path); - - if (method == '1') { - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); Thread.Sleep(1500); NtSuspendProcess(game.Handle); - searcher = new Searcher(game); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the apk."); + return; } - else if (method == '2') searcher = new Searcher(File.ReadAllBytes(path)); - else if (method == '3') searcher = new Searcher(File.ReadAllBytes(path), true); - else if (method == '4') searcher = new Searcher(File.ReadAllBytes(path), true, true); + searcher = new Searcher(File.ReadAllBytes(path), true, true); break; } - EngineVersion = searcher.SearchEngineVersion(); - if (!string.IsNullOrEmpty(EngineVersion)) Console.WriteLine($"Engine Version: {EngineVersion}"); - Dictionary aesKeys = searcher.FindAllPattern(out long took); if (aesKeys.Count > 0) { string WriteToFile = ""; - string txt = $"Found {aesKeys.Count} AES Key{(aesKeys.Count > 1 ? "s" : "")} in {took}ms\n"; + string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + + WriteToFile += txt; + Console.ForegroundColor = ConsoleColor.Green; Console.Write("\n" + txt); Console.ForegroundColor = ConsoleColor.White; - WriteToFile += txt; - - foreach (var o in aesKeys) + int EngineVersionI = 17; + if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); + if (EngineVersionI < 18) { - string b64 = Convert.ToBase64String(GetHex(o.Value)); - // ФОРМАТ: 0xКЛЮЧ (BASE64) at АДРЕС - string line = $"{o.Value} ({b64}) at {o.Key}\n"; - Console.Write(line); - WriteToFile += line; + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; } + else + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); } - else Console.WriteLine("\nFailed to find any AES Keys."); + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("\nFailed to find any AES Keys."); + } + + if (method == '1') try { game.Kill(); } catch { }; - if (game != null) try { game.Kill(); } catch { } - Console.WriteLine("\nDone. Press Enter to exit."); Console.ReadLine(); } } From cbf972fbd46534fb1c74d99010b363905fc635bc Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 23:41:39 +0500 Subject: [PATCH 84/87] Update print statement from 'Hello' to 'Goodbye' --- UEAESKeyFinder/Searcher.cs | 430 ++++++++++++++++++++++++++++--------- 1 file changed, 333 insertions(+), 97 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 0c5c64d..37644f7 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,5 +1,5 @@ /** -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -357,20 +357,21 @@ public static class Win32 }*/ - using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Text; -using System.Runtime.InteropServices; -using System.Linq; using System.Text.RegularExpressions; +using System.Runtime.InteropServices; public class Searcher { + private const int PAGE_SIZE = 4000; + private bool useUE4Lib = false; + private IntPtr hProcess; private Process Process; private ulong AllocationBase; @@ -379,8 +380,6 @@ public class Searcher public Searcher() { } - public void SetFilePath(string path) => FilePath = path; - public Searcher(Process p) { Process = p; @@ -388,141 +387,378 @@ public Searcher(Process p) AllocationBase = (ulong)p.MainModule.BaseAddress; ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - for (int i = 0; i < ProcessMemory.Length; i += 4096) + // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) + for (int i = 0; i < ProcessMemory.Length; i += 2048) { - int bytesToRead = Math.Min(4096, ProcessMemory.Length - i); - byte[] buffer = new byte[bytesToRead]; - if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, buffer, bytesToRead, out _)) + int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + byte[] bytes = new byte[bytesToRead]; + if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) { - Buffer.BlockCopy(buffer, 0, ProcessMemory, i, bytesToRead); + Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); + } + else + { + // Если чтение не удалось — просто продолжаем, можно логировать при необходимости } } } - public Searcher(byte[] bytes, bool useAndroid = false, bool isAPK = false) + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) { if (isAPK) { - byte[] libName = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); - int nameOffset = FindPattern(bytes, libName); - if (nameOffset == -1) throw new Exception("libUE4.so not found!"); + // Поиск "APK Sig Block" (ищем подпись APK) + int libUE4Offset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) + { + if (bytes[i + j] != apkSigBlock[j]) + { + matched = false; + break; + } + } + if (matched) + { + libUE4Offset = i; + break; + } + } + + if (libUE4Offset == 0) + throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + + // Поиск оффсета libUE4.so в блоке + byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + + int foundOffset = 0; + for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) + { + if (bytes[i] != libUE4[0]) continue; + bool c = false; + for (int ii = 0; ii < libUE4.Length; ii++) + { + if (bytes[i + ii] != libUE4[ii]) + { + c = true; + break; + } + } + if (c) continue; - int localHeaderOffset = BitConverter.ToInt32(bytes, nameOffset - 4); - int compressedSize = BitConverter.ToInt32(bytes, localHeaderOffset + 18); - int dataStart = localHeaderOffset + 30 + BitConverter.ToInt16(bytes, localHeaderOffset + 26) + BitConverter.ToInt16(bytes, localHeaderOffset + 28); + // Считаем offset 4 байта перед этим местом (int32 little-endian) + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } - using (var ms = new MemoryStream(bytes, dataStart, compressedSize)) - using (var ds = new DeflateStream(ms, CompressionMode.Decompress)) - using (var output = new MemoryStream()) + if (foundOffset == 0) + throw new Exception("Failed to read LibUE4.so, pattern not found!"); + + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + int headerSize = 53; + int dataStart = foundOffset + headerSize; + + using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLibUE4 = new MemoryStream()) { - ds.CopyTo(output); - ProcessMemory = output.ToArray(); + deflateStream.CopyTo(uncompressedLibUE4); + if (uncompressedLibUE4.Length != uncompressed) + throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); + + ProcessMemory = uncompressedLibUE4.ToArray(); } } - else { ProcessMemory = bytes; } + else + { + ProcessMemory = bytes; + } + useUE4Lib = useAndroid; } + public void SetFilePath(string path) => FilePath = path; + public string SearchEngineVersion() { - if (!string.IsNullOrEmpty(FilePath) && File.Exists(FilePath)) + if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - try { - int checkLength = Math.Min(ProcessMemory.Length, 5000000); - string memStr = Encoding.ASCII.GetString(ProcessMemory, 0, checkLength); - var match = Regex.Match(memStr, @"4\.\d+\.\d+"); - return match.Success ? match.Value : "4.18.1"; - } catch { return "4.18.1"; } + + // Паттерн ProductVersion в юникоде (бэкап) + byte[] ProductVersion = new byte[] + { + 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, + 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, + 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 + }; + + for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < ProductVersion.Length; j++) + { + if (ProcessMemory[i + j] != ProductVersion[j]) + { + matched = false; + break; + } + } + if (!matched) continue; + + var unicodeEncoding = new UnicodeEncoding(); + return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) + } + + return ""; } - private bool IsValidKey(byte[] key) + public int FollowJMP(int addr) { - if (key == null || key.Length != 32) return false; - int distinct = key.Distinct().Count(); - if (distinct < 20) return false; // Ключ не может состоять из одних нулей или одинаковых байт - int zeroCount = key.Count(b => b == 0x00); - if (zeroCount > 5) return false; // Ключ — это не читаемый текст - return true; + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int newAddr = addr + offset + 5; + if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + return FollowJMP(newAddr + 4); + return newAddr; } - public Dictionary FindAllPattern(out long elapsed) + public ulong DecodeADRP(int adrp) { - Stopwatch sw = Stopwatch.StartNew(); - var results = new Dictionary(); - var seenKeys = new HashSet(); + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; - if (useUE4Lib) + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + int msbt = (imm >> 20) & 1; + int value = imm << 12; + + // Поддержка знакового расширения для 64-битного значения + long signedValue = (long)value; + if (msbt == 1) + signedValue |= -1L << 33; + + return (ulong)signedValue; + } + + public ulong DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) + imm12 <<= 12; + return (ulong)imm12; + } + + public int GetADRLAddress(int ADRPLoc) + { + ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); + ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); + + return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + + public Dictionary FindAllPattern(out long elapsedMilliseconds) +{ + Stopwatch timer = Stopwatch.StartNew(); + var offsets = new Dictionary(); + + if (useUE4Lib) + { + // ====================== + // Android UE4 key search pattern + // ====================== + for (int i = 0; i < ProcessMemory.Length - 12; i++) + { + if (ProcessMemory[i] != 0x01) continue; + if (ProcessMemory[i + 1] != 0x01) continue; + if (ProcessMemory[i + 2] != 0x40) continue; + if (ProcessMemory[i + 3] != 0xAD) continue; + if (ProcessMemory[i + 4] != 0x01) continue; + if (ProcessMemory[i + 5] != 0x00) continue; + if (ProcessMemory[i + 6] != 0x00) continue; + if (ProcessMemory[i + 7] != 0xAD) continue; + if (ProcessMemory[i + 8] != 0xC0) continue; + if (ProcessMemory[i + 9] != 0x03) continue; + if (ProcessMemory[i + 10] != 0x5F) continue; + if (ProcessMemory[i + 11] != 0xD6) continue; + + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; + + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // проверка корректности смещения для разных игр + if (aesKeyAddr + 32 > ProcessMemory.Length) continue; + + aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + // ====================== + // Версия движка + // ====================== + Version engineVersion = new Version(0, 0); + string versionStr = SearchEngineVersion(); + if (!string.IsNullOrWhiteSpace(versionStr)) { - for (int i = 0; i < ProcessMemory.Length - 16; i++) + Version.TryParse(versionStr, out engineVersion); + } + + if (engineVersion < new Version(4, 18)) + { + // ====================== + // Старый способ поиска ключа + // ====================== + for (int i = 0; i < ProcessMemory.Length - 10; i++) { - if (ProcessMemory[i] == 0x01 && ProcessMemory[i+1] == 0x01) { - int addr = GetADRLAddress(i); - if (addr > 0 && addr + 32 <= ProcessMemory.Length) { - byte[] key = new byte[32]; - Buffer.BlockCopy(ProcessMemory, addr, key, 0, 32); - if (IsValidKey(key)) { - string hex = "0x" + BitConverter.ToString(key).Replace("-", ""); - if (seenKeys.Add(hex)) results[AllocationBase + (ulong)addr] = hex; - } - } + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + int start = i; + while (start > 0 && ProcessMemory[start - 1] == 0x00) + start--; + + if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + + string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); + break; } } } else { - for (int i = 0; i < ProcessMemory.Length - 100; i++) + // ====================== + // Новый способ поиска ключа (Fortnite / UE4 4.18+) + // ====================== + int verify_1 = 0xC7; + + for (int i = 0; i < ProcessMemory.Length - 10; i++) { - if (ProcessMemory[i] == 0xC7 && (ProcessMemory[i+1] == 0x45 || ProcessMemory[i+1] == 0x44)) + try { - byte[] keyBuffer = new byte[32]; - int curr = i; - bool fail = false; - for (int j = 0; j < 8; j++) { - if (curr + 7 > ProcessMemory.Length || ProcessMemory[curr] != 0xC7) { fail = true; break; } - Buffer.BlockCopy(ProcessMemory, curr + 3, keyBuffer, j * 4, 4); - curr += 7; - if (curr < ProcessMemory.Length && ProcessMemory[curr] == 0xE9) curr = FollowJMP(curr); - } - if (!fail && IsValidKey(keyBuffer)) { - string hex = "0x" + BitConverter.ToString(keyBuffer).Replace("-", ""); - if (seenKeys.Add(hex)) results[AllocationBase + (ulong)i] = hex; + if (i < 3) continue; + + if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) + continue; + + if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) + continue; + + int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + + if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) + continue; + + if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) + continue; + + verify_3 += 0x04; + bool invalid = false; + int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); + string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + + while (aesKey.Length != 64) + { + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + { + if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; + addr = FollowJMP(addr); + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) + invalid = true; + } + else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) + { + invalid = true; + } + else + { + addr += 4; + } + } + + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + else + { + if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) + invalid = true; + + aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); + addr += 7; // 4 + 3 + verify_3 += 0x04; + } + + if (aesKey.Length == 64) + { + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + + if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) + { + if (ProcessMemory[addr] != 0x0F) + invalid = true; + + bool found = false; + for (int xx = 0; xx < 30; xx++) + { + int checkAddr = addr + xx; + if (checkAddr >= ProcessMemory.Length) break; + if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) + { + found = true; + break; + } + } + if (!found) + invalid = true; + } + } + + if (invalid) + break; } + + if (!invalid) + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch + { + // Игнорируем ошибки парсинга } } } - sw.Stop(); - elapsed = sw.ElapsedMilliseconds; - return results; } - private int FollowJMP(int addr) { - if (addr + 5 > ProcessMemory.Length) return addr; - int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); - return addr + offset + 5; - } + timer.Stop(); + elapsedMilliseconds = timer.ElapsedMilliseconds; + return offsets; +} - private int GetADRLAddress(int loc) { - try { - int ins = BitConverter.ToInt32(ProcessMemory, loc); - long imm = ((ins >> 29) & 3) | (((ins >> 5) & 0x7FFFF) << 2); - if ((imm & 0x100000) != 0) imm |= -1L << 21; - return (int)((((ulong)loc & 0xFFFFF000) + (ulong)(imm << 12) + (ulong)((BitConverter.ToInt32(ProcessMemory, loc + 4) >> 10) & 0xFFF)) & 0xFFFFFFFF); - } catch { return -1; } - } - private int FindPattern(byte[] src, byte[] pat) { - for (int i = 0; i <= src.Length - pat.Length; i++) { - bool m = true; - for (int j = 0; j < pat.Length; j++) if (src[i + j] != pat[j]) { m = false; break; } - if (m) return i; - } - return -1; - } + public static class Win32 + { + [DllImport("kernel32.dll")] +public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); - public static class Win32 { - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory(IntPtr h, ulong a, [Out] byte[] b, int s, out int r); + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } } - From 59a6f688f2fededf925e6ffc3fa348371bfae11e Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Tue, 17 Feb 2026 23:56:11 +0500 Subject: [PATCH 85/87] Add files via upload --- UEAESKeyFinder/Program.cs | 392 ++++++++++++++++++------------------- UEAESKeyFinder/Searcher.cs | 140 ++++++------- 2 files changed, 261 insertions(+), 271 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 48694ce..1b7d1af 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,196 +1,196 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using static Searcher; - -namespace UEAesKeyFinder -{ - class Program - { - [DllImport("ntdll.dll", PreserveSig = false)] - public static extern void NtSuspendProcess(IntPtr processHandle); - public static byte[] GetHex(string hex) - { - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - return r; - } - static void Main(string[] args) - { - Searcher searcher = new Searcher(); - Process game = new Process(); - - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); - string path; - string EngineVersion = "4.18.0"; - string saveName = ""; - switch (method) - { - case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); - string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path)); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } - - searcher = new Searcher(File.ReadAllBytes(path), true); - break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } - searcher = new Searcher(File.ReadAllBytes(path), true, true); - break; - } - - Dictionary aesKeys = searcher.FindAllPattern(out long took); - - if (aesKeys.Count > 0) - { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); - } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; - - Console.ReadLine(); - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using static Searcher; + +namespace UEAesKeyFinder +{ + class Program + { + [DllImport("ntdll.dll", PreserveSig = false)] + public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) + { + var r = new byte[hex.Length / 2]; + for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return r; + } + static void Main(string[] args) + { + Searcher searcher = new Searcher(); + Process game = new Process(); + + Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); + + char method = (char)Console.Read(); + string path; + string EngineVersion = "4.18.0"; + string saveName = ""; + switch (method) + { + case '0': + Console.Write("Enter the name or id of the process: "); + Console.Read(); + Console.Read(); + string ProcessName = Console.ReadLine(); + + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) + { + Console.WriteLine($"\nFound {p.ProcessName}"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the process."); + Console.ReadLine(); + return; + } + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '1': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1000); + // Not required to fully load + NtSuspendProcess(game.Handle); + + searcher = new Searcher(game); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '2': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the dump file."); + return; + } + + saveName = path.Split("\\")[path.Split("\\").Length-1]; + + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + EngineVersion = searcher.SearchEngineVersion(); + if (EngineVersion != "") + { + Console.WriteLine($"Engine Version: {EngineVersion}"); + } + break; + case '3': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the lib."); + return; + } + + searcher = new Searcher(File.ReadAllBytes(path), true); + break; + case '4': + Console.Write("Please enter the file path: "); + Console.Read(); + Console.Read(); + path = Console.ReadLine().Replace("\"", ""); + + saveName = path.Split("\\")[path.Split("\\").Length - 1]; + + if (!File.Exists(path)) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Failed to find the apk."); + return; + } + searcher = new Searcher(File.ReadAllBytes(path), true, true); + break; + } + + Dictionary aesKeys = searcher.FindAllPattern(out long took); + + if (aesKeys.Count > 0) + { + string WriteToFile = ""; + string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; + + WriteToFile += txt; + + Console.ForegroundColor = ConsoleColor.Green; + Console.Write("\n" + txt); + Console.ForegroundColor = ConsoleColor.White; + int EngineVersionI = 17; + if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); + if (EngineVersionI < 18) + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + else + { + foreach (KeyValuePair o in aesKeys) + { + txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; + Console.Write(txt); + WriteToFile += txt; + }; + } + + File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("\nFailed to find any AES Keys."); + } + + if (method == '1') try { game.Kill(); } catch { }; + + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 37644f7..eee5de0 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -567,86 +567,77 @@ public int GetADRLAddress(int ADRPLoc) } public Dictionary FindAllPattern(out long elapsedMilliseconds) -{ - Stopwatch timer = Stopwatch.StartNew(); - var offsets = new Dictionary(); - - if (useUE4Lib) - { - // ====================== - // Android UE4 key search pattern - // ====================== - for (int i = 0; i < ProcessMemory.Length - 12; i++) - { - if (ProcessMemory[i] != 0x01) continue; - if (ProcessMemory[i + 1] != 0x01) continue; - if (ProcessMemory[i + 2] != 0x40) continue; - if (ProcessMemory[i + 3] != 0xAD) continue; - if (ProcessMemory[i + 4] != 0x01) continue; - if (ProcessMemory[i + 5] != 0x00) continue; - if (ProcessMemory[i + 6] != 0x00) continue; - if (ProcessMemory[i + 7] != 0xAD) continue; - if (ProcessMemory[i + 8] != 0xC0) continue; - if (ProcessMemory[i + 9] != 0x03) continue; - if (ProcessMemory[i + 10] != 0x5F) continue; - if (ProcessMemory[i + 11] != 0xD6) continue; - - int aesKeyAddr = GetADRLAddress(i - 8); - if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; - - string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - - aesKeyAddr += 0x1000; // проверка корректности смещения для разных игр - if (aesKeyAddr + 32 > ProcessMemory.Length) continue; - - aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - } - } - else { - // ====================== - // Версия движка - // ====================== - Version engineVersion = new Version(0, 0); - string versionStr = SearchEngineVersion(); - if (!string.IsNullOrWhiteSpace(versionStr)) - { - Version.TryParse(versionStr, out engineVersion); - } + Stopwatch timer = Stopwatch.StartNew(); + var offsets = new Dictionary(); - if (engineVersion < new Version(4, 18)) + if (useUE4Lib) { - // ====================== - // Старый способ поиска ключа - // ====================== - for (int i = 0; i < ProcessMemory.Length - 10; i++) + // Android UE4 key search pattern + for (int i = 0; i < ProcessMemory.Length - 12; i++) { - if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + if (ProcessMemory[i] != 0x01) continue; + if (ProcessMemory[i + 1] != 0x01) continue; + if (ProcessMemory[i + 2] != 0x40) continue; + if (ProcessMemory[i + 3] != 0xAD) continue; + if (ProcessMemory[i + 4] != 0x01) continue; + if (ProcessMemory[i + 5] != 0x00) continue; + if (ProcessMemory[i + 6] != 0x00) continue; + if (ProcessMemory[i + 7] != 0xAD) continue; + if (ProcessMemory[i + 8] != 0xC0) continue; + if (ProcessMemory[i + 9] != 0x03) continue; + if (ProcessMemory[i + 10] != 0x5F) continue; + if (ProcessMemory[i + 11] != 0xD6) continue; - int start = i; - while (start > 0 && ProcessMemory[start - 1] == 0x00) - start--; + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; - if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр + if (aesKeyAddr + 32 > ProcessMemory.Length) continue; - if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) - { - offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); - break; - } + aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); } } else { - // ====================== - // Новый способ поиска ключа (Fortnite / UE4 4.18+) - // ====================== - int verify_1 = 0xC7; + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (!string.IsNullOrEmpty(EngineVersionStr)) + { + string[] parts = EngineVersionStr.Split('.'); + if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) + EngineVersion = ver; + } + if (EngineVersion < 18) + { + // Старый способ поиска ключа + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + int start = i; + while (start > 0 && ProcessMemory[start - 1] == 0x00) + start--; + + if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + + string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); + break; + } + } + } + + // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) + int verify_1 = 0xC7; for (int i = 0; i < ProcessMemory.Length - 10; i++) { try @@ -736,8 +727,9 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) break; } - if (!invalid) - offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + if (invalid) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); } catch { @@ -745,13 +737,11 @@ public Dictionary FindAllPattern(out long elapsedMilliseconds) } } } - } - - timer.Stop(); - elapsedMilliseconds = timer.ElapsedMilliseconds; - return offsets; -} + timer.Stop(); + elapsedMilliseconds = timer.ElapsedMilliseconds; + return offsets; + } public static class Win32 { From 43d8a55539e73be2f7e5d298026a4779d60d75ca Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Wed, 25 Feb 2026 09:04:28 +0500 Subject: [PATCH 86/87] Update Program.cs --- UEAESKeyFinder/Program.cs | 213 ++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 122 deletions(-) diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 1b7d1af..6b6d91e 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,10 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; -using static Searcher; namespace UEAesKeyFinder { @@ -12,185 +11,155 @@ class Program { [DllImport("ntdll.dll", PreserveSig = false)] public static extern void NtSuspendProcess(IntPtr processHandle); + public static byte[] GetHex(string hex) { + // Убираем 0x если он есть в начале + if (hex.StartsWith("0x")) hex = hex.Substring(2); var r = new byte[hex.Length / 2]; for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return r; } + static void Main(string[] args) { Searcher searcher = new Searcher(); Process game = new Process(); - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); + Console.WriteLine("=== Unreal Engine AES Key Finder ==="); + Console.Write("Select method:\n0: Memory (Running Process)\n1: File (Start & Suspend)\n2: Dump File\n3: Lib File (libUE4.so / libUnreal.so)\n4: APK File\nUse: "); + + // Используем ReadKey для более чистого ввода + char method = Console.ReadKey().KeyChar; + Console.WriteLine(); - char method = (char)Console.Read(); string path; string EngineVersion = "4.18.0"; - string saveName = ""; + string saveName = "result"; + switch (method) { case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); + Console.Write("Enter process name or ID: "); string ProcessName = Console.ReadLine(); - bool found = false; foreach (Process p in Process.GetProcesses()) { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) + if (p.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == ProcessName) { - Console.WriteLine($"\nFound {p.ProcessName}"); + Console.WriteLine($"\nFound: {p.ProcessName} (PID: {p.Id})"); saveName = p.ProcessName; searcher = new Searcher(p); found = true; break; } } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } + if (!found) { ErrorExit("Process not found."); return; } break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; + case '1': + path = GetFilePath(); + saveName = Path.GetFileName(path); game = new Process() { StartInfo = { FileName = path } }; game.Start(); - Thread.Sleep(1000); - // Not required to fully load + Thread.Sleep(1500); // Даем немного времени на инициализацию NtSuspendProcess(game.Handle); - searcher = new Searcher(game); searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; + case '2': + path = GetFilePath(); + saveName = Path.GetFileName(path); searcher = new Searcher(File.ReadAllBytes(path)); searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } + case '3': // Теперь поддерживает и libUE4.so, и libUnreal.so + path = GetFilePath(); + saveName = Path.GetFileName(path); searcher = new Searcher(File.ReadAllBytes(path), true); break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } + case '4': // Анализ APK (найдет и libUE4, и libUnreal внутри) + path = GetFilePath(); + saveName = Path.GetFileName(path); searcher = new Searcher(File.ReadAllBytes(path), true, true); break; + + default: + ErrorExit("Invalid selection."); + return; } + // Пытаемся определить версию движка + EngineVersion = searcher.SearchEngineVersion(); + if (!string.IsNullOrEmpty(EngineVersion)) + Console.WriteLine($"Detected Engine Version: {EngineVersion}"); + + Console.WriteLine("Searching for patterns... please wait."); Dictionary aesKeys = searcher.FindAllPattern(out long took); if (aesKeys.Count > 0) { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); + ProcessResults(aesKeys, took, saveName, EngineVersion); } else { Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); + Console.WriteLine("\n[!] No AES Keys found."); } - if (method == '1') try { game.Kill(); } catch { }; + if (method == '1') try { game.Kill(); } catch { } + Console.ResetColor(); + Console.WriteLine("\nPress Enter to exit."); + Console.ReadLine(); + } + static string GetFilePath() + { + Console.Write("Enter file path: "); + return Console.ReadLine().Trim(' ', '\"'); + } + + static void ErrorExit(string msg) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"\n[Error] {msg}"); Console.ReadLine(); } + + static void ProcessResults(Dictionary keys, long time, string name, string version) + { + string output = $"Found {keys.Count} key(s) in {time}ms\n\n"; + Console.ForegroundColor = ConsoleColor.Green; + Console.Write(output); + Console.ResetColor(); + + int verMajor = 18; // Default + if (!string.IsNullOrEmpty(version) && version.Contains(".")) + int.TryParse(version.Split('.')[1], out verMajor); + + foreach (var entry in keys) + { + string keyStr = entry.Value; + string base64 = ""; + + // Для новых версий (4.18+) добавляем Base64 представление ключа + if (verMajor >= 18 && keyStr.StartsWith("0x") && keyStr.Length > 10) + { + try { + base64 = $" (Base64: {Convert.ToBase64String(GetHex(keyStr))})"; + } catch { } + } + + string line = $"Offset: 0x{entry.Key:X} | Key: {keyStr}{base64}\n"; + Console.Write(line); + output += line; + } + + File.WriteAllText(name + "_aes_keys.txt", output); + Console.WriteLine($"\nResults saved to {name}_aes_keys.txt"); + } } -} \ No newline at end of file +} From 2b4c21638d28e8a49c3734206bac64425b34d9b9 Mon Sep 17 00:00:00 2001 From: tojik_proof_93 Date: Wed, 25 Feb 2026 09:08:32 +0500 Subject: [PATCH 87/87] Update Searcher.cs --- UEAESKeyFinder/Searcher.cs | 124 +++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 53 deletions(-) diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index eee5de0..c6694fc 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -410,84 +410,101 @@ public Searcher(byte[] bytes) } public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) +{ + if (isAPK) { - if (isAPK) - { - // Поиск "APK Sig Block" (ищем подпись APK) - int libUE4Offset = 0; - byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + // 1. Locate the "APK Sig Block" (APK Signature Scheme block) + int sigBlockOffset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); - for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) { - bool matched = true; - for (int j = 0; j < apkSigBlock.Length; j++) - { - if (bytes[i + j] != apkSigBlock[j]) - { - matched = false; - break; - } - } - if (matched) + if (bytes[i + j] != apkSigBlock[j]) { - libUE4Offset = i; + matched = false; break; } } + if (matched) + { + sigBlockOffset = i; + break; + } + } - if (libUE4Offset == 0) - throw new Exception("Failed to read LibUE4.so, APK Sig Block not found!"); + if (sigBlockOffset == 0) + throw new Exception("Failed to read APK: APK Sig Block not found!"); - // Поиск оффсета libUE4.so в блоке - byte[] libUE4 = Encoding.ASCII.GetBytes("lib/arm64-v8a/libUE4.so"); + // 2. Universal search for the Engine Library (UE4 or UE5/Unreal) + // We check both naming conventions used by the engine + string[] targetLibs = { "lib/arm64-v8a/libUE4.so", "lib/arm64-v8a/libUnreal.so" }; + int foundOffset = 0; - int foundOffset = 0; - for (int i = libUE4Offset; i < bytes.Length - libUE4.Length - 4; i++) + foreach (string libName in targetLibs) + { + byte[] pattern = Encoding.ASCII.GetBytes(libName); + + // Search for the library path in the Central Directory (located after the Sig Block) + for (int i = sigBlockOffset; i < bytes.Length - pattern.Length - 4; i++) { - if (bytes[i] != libUE4[0]) continue; - bool c = false; - for (int ii = 0; ii < libUE4.Length; ii++) + bool matched = true; + for (int ii = 0; ii < pattern.Length; ii++) { - if (bytes[i + ii] != libUE4[ii]) + if (bytes[i + ii] != pattern[ii]) { - c = true; + matched = false; break; } } - if (c) continue; - - // Считаем offset 4 байта перед этим местом (int32 little-endian) - foundOffset = BitConverter.ToInt32(bytes, i - 4); - break; + + if (matched) + { + // Read the relative offset of the Local File Header (4 bytes before the path string) + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } } + if (foundOffset != 0) break; // Exit loop if a library is found + } - if (foundOffset == 0) - throw new Exception("Failed to read LibUE4.so, pattern not found!"); + if (foundOffset == 0) + throw new Exception("Engine library (libUE4.so or libUnreal.so) not found in APK!"); - int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); - int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); - int headerSize = 53; - int dataStart = foundOffset + headerSize; + // 3. Dynamic Calculation of Data Start (Local File Header parsing) + // ZIP Format: Header(30 bytes) + FileNameLength(2 bytes) + ExtraFieldLength(2 bytes) + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + short fileNameLen = BitConverter.ToInt16(bytes, foundOffset + 26); + short extraLen = BitConverter.ToInt16(bytes, foundOffset + 28); + + // Accurate start of the compressed data stream + int dataStart = foundOffset + 30 + fileNameLen + extraLen; - using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) - using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) - using (var uncompressedLibUE4 = new MemoryStream()) - { - deflateStream.CopyTo(uncompressedLibUE4); - if (uncompressedLibUE4.Length != uncompressed) - throw new Exception("Failed to decompress LibUE4.so, size mismatch!"); + - ProcessMemory = uncompressedLibUE4.ToArray(); - } - } - else + using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLib = new MemoryStream()) { - ProcessMemory = bytes; - } + deflateStream.CopyTo(uncompressedLib); + + if (uncompressedLib.Length != uncompressed) + throw new Exception("Decompression failed: Size mismatch!"); - useUE4Lib = useAndroid; + ProcessMemory = uncompressedLib.ToArray(); + } + } + else + { + ProcessMemory = bytes; } + useUE4Lib = useAndroid; +} + public void SetFilePath(string path) => FilePath = path; public string SearchEngineVersion() @@ -752,3 +769,4 @@ public static class Win32 public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); } } +