What is known to us is that UnrealEngine uses reflection to achieve various dynamic features, such as object management, garbage collection systems, etc. UnrealEngine generates reflection data during compilation, including class names, class methods, class fields and other class properties. And these data are packed and saved. In this situation, we can retrieve class information and symbols through reverse engineering. Usually, we refer to this process as Dump SDK.
In this blog, I downloaded UE4 4.25 source code and compiled a demo program UE4Game-Win64-Shipping.exe. I will dump symbols from this program to introduce the UE Dump SDK.
GNames
A golbal variable named NamePoolData is a important varible, which introduce UE reflect data structure. And all of programs compiled by UnrealEngine have this varible. Lots of people refer to it as GNames. The type of NamePoolData is FNamePool
0:000> dt FNamePool
UE4Game_Win64_Shipping!FNamePool
+0x000 Entries : FNameEntryAllocator
+0x10040 ComparisonShards : [16] FNamePoolShard<1>
+0x10440 ENameToEntry : [702] FNameEntryId
+0x10f38 LargestEnameUnstableId : Uint4B
+0x10f40 EntryToEName : TMap<FNameEntryId,enum EName,TInlineSetAllocator<512,TSetAllocator<TSparseArrayAllocator<TSizedDefaultAllocator<32>,TSizedDefaultAllocator<32> >,TSizedDefaultAllocator<32>,2,8,4>,2,4>,TDefaultMapHashableKeyFuncs<FNameEntryId,enum EName,0> >
0:000> dt FNameEntryAllocator
UE4Game_Win64_Shipping!FNameEntryAllocator
+0x000 Lock : FWindowsRWLock
+0x008 CurrentBlock : Uint4B
+0x00c CurrentByteCursor : Uint4B
+0x010 Blocks : [8192] Ptr64 UChar
And we can browse memory region about NamePoolData。
We should notice a pointer array FNamePool->FNameEntryAllocator->Blocks, which saved pointer of block. And FNameEntries were stores in these blocks. These are structures definition:
|
|
Okay, we can enumerate Blocks and FNameEntry to retrieve all the FName string.
Sometimes, certain games encrypt FNameEntry.FNameBuffer. However, UnrealEngine needs the original FName to achieve the goal of reflection. Therefore these games encrypt FNameEntry.FNameBuffer and decrypt these data when programs use. We can disassemble FNameEntry::AppendNameToString to analyze the encryption algorithm.
GObjects
There is a global variable named GUObjectArray that stores all instances of the UObject in the UnrealEngine program. We commonly refer it as GObjects. The type of GUObjectArray is FUObjectArray.
|
|
We can access all UObject pointers by GUObjectArray->ObjObjects->Objects[i]->Object
Total objects in the UnrealEngine program are derived from UObject, including UField, UEnum, UStruct, UScriptStruct, UFunction, UClass, and more. Therefore, we can dump symbols if we can solve the following problems:
- Identifying the type of any object.
- Retrieving the name of any object from NamePoolData
In UnrealEngine, we can obtain the name of any object from NamePoolData. Let’s take a closer look at the UObject structure.
0:000> dt UObject
UE4Game_Win64_Shipping!UObject
+0x000 __VFN_table : Ptr64
+0x008 ObjectFlags : EObjectFlags
+0x00c InternalIndex : Int4B
+0x010 ClassPrivate : Ptr64 UClass
+0x018 NamePrivate : FName
+0x020 OuterPrivate : Ptr64 UObject
There is a field named NamePrivate. We can use this field to obtain the name of the object. However, the type of UObject->NamePrivate is FName, which does not directly store the string. We know that symbols of the UnrealEngine program are stored in the structure FNameEntry.
0:000> dt FName
UE4Game_Win64_Shipping!FName
+0x000 ComparisonIndex : FNameEntryId
+0x004 Number : Uint4B
We can disassemble and analyze FName::GetEntry to obtain the conversion relationship between FName and FNameEntry.
I imple this method using C language.
|
|
We need to take note of a feature in UnrealEngine called subobject. This means that each UObject can have an optional “Outer Object”. Therefore, when trying to obtain the accurate name of an object, we should iterate through of its outer objects.
|
|
Okay, let’s discuss how to identify the type of any objects. Before we talk about how to retrieve an object’s type, we first need to understand UnrealEngine Types.
It is clearly that all UE Objects based on UObject, including UFunction, UClass, UEnum, and so on. We need to distinguish whether the object belongs to UClass, UEnum, or UFunction.
|
|
After that, we can cast the object pointer to ue type and retrieve information from the object fields.