diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..03e977d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,44 @@ +permissions: + contents: write +name: Build UEAESKeyFinder + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: windows-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 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/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 Build (${{ env.now }})" + body: "Build time: ${{ env.now }}" + files: ./publish/** + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file 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 | 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 | diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md new file mode 100644 index 0000000..6711e30 --- /dev/null +++ b/AESKeys/SpecialForcesGroup3.md @@ -0,0 +1,6 @@ +# AES Keys for SpecialForcesGroup3 + +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | 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 | 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 | 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 | diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..2b63181 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,11 @@ + + + true + true + $(NoWarn);NU1507 + + + + + + \ No newline at end of file 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 + +``` + +
+ + + + + + + + +
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/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 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