66using System . Linq ;
77using System . Runtime . InteropServices ;
88using System . Text ;
9+ using System . Text . RegularExpressions ;
910
1011public 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