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