Skip to content

Commit 3b0b98c

Browse files
committed
Added support for older games
Should now work with games that use UE4.17 and lower
1 parent aa4ab78 commit 3b0b98c

4 files changed

Lines changed: 133 additions & 72 deletions

File tree

AESKeys/Fortnite.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@
88
## Season 15
99
| Version | Key |
1010
| ----------------- | --------------------------------------------------------------------- |
11-
| 15.4 | 0x67BA1ACFB49BE607B053FF06173B7595912C91AEDB9C59418713823FE335FBA4 |
11+
| 15.4 | 0x67BA1ACFB49BE607B053FF06173B7595912C91AEDB9C59418713823FE335FBA4 |
12+
13+
## Season 1
14+
| Version | Key |
15+
| ----------------- | --------------------------------------------------------------------- |
16+
| 1.9 | y298qjSb115NqQ3Agad30DWn2QYrTI8CT6aP05l2PBV9Qe92S94PdoVCCy06A38L |
17+
| 1.8 | y298qjSb115NqQ3Agad30DWn2QYrTI8CT6aP05l2PBV9Qe92S94PdoVCCy06A38L |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# AES Keys for Sword Art Online : Fatal Bullet
2+
Never changed afaik
3+
4+
Key: h67GrjX2aGMgrAQeNwf9VmCYbt50ylJFeP3rIhbxh4e9bZXnqm8sbvEjWGOi6rgs

UEAESKeyFinder/Program.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ static void Main(string[] args)
2727

2828
char method = (char)Console.Read();
2929
string path;
30-
string EngineVersion;
30+
string EngineVersion = "4.18.0";
3131
switch (method)
3232
{
3333
case '0':
@@ -79,6 +79,7 @@ static void Main(string[] args)
7979
NtSuspendProcess(game.Handle);
8080

8181
searcher = new Searcher(game);
82+
searcher.SetFilePath(path);
8283
EngineVersion = searcher.SearchEngineVersion();
8384
if (EngineVersion != "")
8485
{
@@ -98,6 +99,7 @@ static void Main(string[] args)
9899
}
99100

100101
searcher = new Searcher(File.ReadAllBytes(path));
102+
searcher.SetFilePath(path);
101103
EngineVersion = searcher.SearchEngineVersion();
102104
if (EngineVersion != "")
103105
{
@@ -140,10 +142,22 @@ static void Main(string[] args)
140142
Console.ForegroundColor = ConsoleColor.Green;
141143
Console.WriteLine(aesKeys.Count == 1 ? $"\nFound {aesKeys.Count} AES Key in {took}ms" : $"\nFound {aesKeys.Count} AES Keys in {took}ms");
142144
Console.ForegroundColor = ConsoleColor.White;
143-
foreach (KeyValuePair<ulong, string> o in aesKeys)
145+
int EngineVersionI = 17;
146+
if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]);
147+
if (EngineVersionI < 18)
144148
{
145-
Console.WriteLine($"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}");
146-
};
149+
foreach (KeyValuePair<ulong, string> o in aesKeys)
150+
{
151+
Console.WriteLine($"{aesKeys[o.Key]} at {o.Key}");
152+
};
153+
}
154+
else
155+
{
156+
foreach (KeyValuePair<ulong, string> o in aesKeys)
157+
{
158+
Console.WriteLine($"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}");
159+
};
160+
}
147161
}
148162
else
149163
{

UEAESKeyFinder/Searcher.cs

Lines changed: 104 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Runtime.InteropServices;
88
using System.Text;
9+
using System.Text.RegularExpressions;
910

1011
public class Searcher
1112
{
@@ -17,6 +18,7 @@ public class Searcher
1718
private Process Process;
1819
private ulong AllocationBase;
1920
private byte[] ProcessMemory;
21+
private string FilePath;
2022

2123
public Searcher() { }
2224

@@ -110,8 +112,11 @@ public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false)
110112

111113
useUE4Lib = useAndroid;
112114
}
115+
public void SetFilePath(string path) { FilePath = path; }
113116
public string SearchEngineVersion()
114117
{
118+
if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion;
119+
115120
// We search backwards because its mostly at the end
116121
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 };
117122
for (int i = ProcessMemory.Length - 1; i > 0; i--)
@@ -218,89 +223,121 @@ public Dictionary<ulong, string> FindAllPattern(out long t)
218223
}
219224
else
220225
{
221-
// Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z"
222-
// https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841
226+
string EngineVersionStr = SearchEngineVersion();
227+
int EngineVersion = 17;
228+
if (EngineVersionStr != "") EngineVersion = Convert.ToInt32(EngineVersionStr.Split(".")[1]);
229+
if (EngineVersion < 18)
230+
{
231+
// Let's just try something, not sure if that works for all games
232+
string aesKey = "";
223233

224-
// Should work for all newer Fortnite versions
225-
// C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ?
234+
for (int i = 0; i < ProcessMemory.Length-10; i++)
235+
{
236+
if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue;
226237

227-
string aesKey = "";
228-
int verify_1 = 0xC7;
229-
for (int i = 0; i < ProcessMemory.Length - 10; i++)
238+
// Now we need to find where the first 00 starts and go back to that location
239+
int start = i;
240+
while (ProcessMemory[start-1] == 0x00) start -= 1;
241+
242+
// Key is 64 letters long, lets make sure the first byte before the key is 0x00
243+
if (ProcessMemory[start - 65] != 0x00) continue;
244+
245+
aesKey = Encoding.Default.GetString(ProcessMemory[(start - 64)..start]);
246+
247+
// Lets make sure the key is as valid string
248+
if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$"))
249+
{
250+
offsets.Add(AllocationBase + (ulong)start-64, aesKey);
251+
break;
252+
}
253+
}
254+
}
255+
else
230256
{
231-
try
257+
// Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z"
258+
// https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841
259+
260+
// Should work for all newer Fortnite versions
261+
// C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ?
262+
263+
string aesKey = "";
264+
int verify_1 = 0xC7;
265+
for (int i = 0; i < ProcessMemory.Length - 10; i++)
232266
{
233-
// Should start with smth like 48 8D 64 24 08 and end with it
234-
if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue;
235-
if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue;
236-
int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45;
237-
int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0;
238-
if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue;
239-
240-
// It should be the first keypart
241-
if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue;
242-
243-
verify_3 += 0x04;
244-
// Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps
245-
bool c = false;
246-
int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1);
247-
aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck
248-
249-
while (aesKey.Length != 64)
250-
// 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4
251-
// older versions have a simple mov rcx, but never have mov rcx+4
267+
try
252268
{
253-
if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games
269+
// Should start with smth like 48 8D 64 24 08 and end with it
270+
if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue;
271+
if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue;
272+
int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45;
273+
int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0;
274+
if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue;
275+
276+
// It should be the first keypart
277+
if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue;
278+
279+
verify_3 += 0x04;
280+
// Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps
281+
bool c = false;
282+
int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1);
283+
aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck
284+
285+
while (aesKey.Length != 64)
286+
// 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4
287+
// older versions have a simple mov rcx, but never have mov rcx+4
254288
{
255-
// Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start
256-
// JMP Right after it is possible too
257-
if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)
289+
if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games
258290
{
259-
addr += 4; // Skip the useless bytes
260-
// jump to the address and check if the bytes are valid
261-
addr = FollowJMP(addr);
262-
if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true;
263-
}
264-
else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true;
265-
else addr += 4;
266-
};
291+
// Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start
292+
// JMP Right after it is possible too
293+
if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)
294+
{
295+
addr += 4; // Skip the useless bytes
296+
// jump to the address and check if the bytes are valid
297+
addr = FollowJMP(addr);
298+
if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true;
299+
}
300+
else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true;
301+
else addr += 4;
302+
};
267303

268-
if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr);
269-
else
270-
{
271-
if (this.ProcessMemory[addr + 1] != verify_2) c = true;
272-
if ((this.ProcessMemory[addr + 2] != verify_3)) c = true;
273-
aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", "");
274-
addr += 4 + 3; // C7 4x xx
275-
verify_3 += 0x04;
276-
};
277-
278-
if (aesKey.Length == 64)
279-
{
280-
// This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys.
281-
// if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true;
282304
if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr);
283-
// && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08
284-
if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48)
305+
else
306+
{
307+
if (this.ProcessMemory[addr + 1] != verify_2) c = true;
308+
if ((this.ProcessMemory[addr + 2] != verify_3)) c = true;
309+
aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", "");
310+
addr += 4 + 3; // C7 4x xx
311+
verify_3 += 0x04;
312+
};
313+
314+
if (aesKey.Length == 64)
285315
{
286-
// There might be movups so lets check 50 bytes if we still get 48 8D
287-
if (this.ProcessMemory[addr] != 0x0F) c = true;
288-
for (int xx = 0; xx < 30; xx++)
316+
// This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys.
317+
// if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true;
318+
if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr);
319+
// && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08
320+
if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48)
289321
{
290-
addr = addr + xx;
291-
if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break;
322+
// There might be movups so lets check 50 bytes if we still get 48 8D
323+
if (this.ProcessMemory[addr] != 0x0F) c = true;
324+
for (int xx = 0; xx < 30; xx++)
325+
{
326+
addr = addr + xx;
327+
if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break;
328+
}
329+
// We should probably delete this...
330+
if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true;
292331
}
293-
// We should probably delete this...
294-
if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true;
295332
}
333+
if (c) break;
296334
}
297-
if (c) break;
298-
}
299-
if (c) continue;
335+
if (c) continue;
300336

301-
offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}");
337+
offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}");
338+
}
339+
catch { }
302340
}
303-
catch { }
304341
}
305342
}
306343

0 commit comments

Comments
 (0)