GObjectArray逆向:
首先查看源代码:
此处上面存在字符串特征:
Cannot Cancel Async Loading while async loading is suspended.
直接搜索该字符串就可以定位到GUObjectArray.GetObjectArrayNum()
:
然后查看GetObjectArrayNum的源代码:
FORCEINLINE int32 GetObjectArrayNum() const
{
return ObjObjects.Num();
}
跟进ObjObjects
发现定义如下:
TUObjectArray ObjObjects;
继续跟进:
typedef FChunkedFixedUObjectArray TUObjectArray;
得到如下类:
class FChunkedFixedUObjectArray
{
enum
{
NumElementsPerChunk = 64 * 1024,
};
/** Master table to chunks of pointers **/
FUObjectItem** Objects;
/** If requested, a contiguous memory where all objects are allocated **/
FUObjectItem* PreAllocatedObjects;
/** Maximum number of elements **/
int32 MaxElements;
/** Number of elements we currently have **/
int32 NumElements;
/** Maximum number of chunks **/
int32 MaxChunks;
/** Number of chunks we currently have **/
int32 NumChunks;
找到方法的定义如下:
FORCEINLINE int32 Num() const
{
return NumElements;
}
发现返回的就是NumElements
这个,所以7FF78E0D11A4
该地址其实就是ObjObjects.NumElements
,而上面存在5个元素,5*4=0x14,所以该地址-0x14就可以得到GObjectArray:
此时的第一个元素为:
class FUObjectItem
{
// Pointer to the allocated object
class UObjectBase* Object;
// Internal flags
int32 Flags;
// UObject Owner Cluster Index
int32 ClusterRootIndex;
// Weak Object Pointer Serial number associated with the object
int32 SerialNumber;
}
继续查看第一个元素就是UObject:
class UObject
{
/** virtual function table */
void* vtf;
/** Flags used to track and report various object states. This needs to be 8 byte
aligned on 32-bit
platforms to reduce memory waste */
EObjectFlags ObjectFlags;
/** Index into GObjectArray...very private. */
int32 InternalIndex; //表明该对象在
GObjectArray中的第几个
/** Class the object belongs to. */
UClass* ClassPrivate;
/** Name of this object */
FName NamePrivate;
/** Object this object resides in. */
UObject* OuterPrivate;
}
通过UObject就可以遍历出UE4引擎的所有对象了
使用代码遍历:
通过观察代码我们知道Objct是一个二级指针:
FUObjectItem** Objects;
也就是说他和Gname一样是以表的形式存在的,所以我们想要遍历他就需要知道当前需要遍历的对象在哪张表中,算法如下:
UObject* UobjectArray::GetObjectPtr(int id) {
if (id >= NumElements) return 0;
DWORD64 chunkIndex = id / 65536; //chunkIndex = 0
if (chunkIndex >= NumChunks) return 0;
auto chunk = Process::ReadProcess<void*>(Objects+(chunkIndex * 8));//
if (!chunk) return 0;
DWORD withinChunkIndex = id % 65536 * 32; //这里的32是根据游戏实际结构体大小决定了,默认情况下为32
auto item = Process::ReadProcess<PVOID>((PVOID)((DWORD64)chunk + withinChunkIndex));
return (DWORD64)item;
}
官方给出的SDK代码中就有,可以复制过来直接用:
遍历代码如下:
GobjectArray = Process::ReadProcess<UobjectArray>((PVOID)((DWORD64)Process::GetProcessMoudleBase() + 0x8031190));
//int NumElements = Process::ReadProcess<int>((PVOID)(GobjectArray + 0x14));
cout << "NumElements: " << hex << GobjectArray.NumElements << endl;
for (int i = 0; i < GobjectArray.NumElements; i++) {
cout <<"ID:" << i << " Uobject Address: " << GobjectArray.GetObjectPtr(i) << endl;
}
测试结果:
当能够遍历出Object后还需要使用到Gname遍历Object的名称,具体可以查看我之前的文章,部分代码如下:
string GetObjectName(DWORD64 Address) {
string Name;
DWORD64 UobjectOuter = Process::ReadProcess<DWORD64>((PVOID)(Address + 0x20)); //获取UObject->OuterPrivate
if (UobjectOuter != 0)
{
for (DWORD64 Outer = UobjectOuter; Outer; Outer = Process::ReadProcess<DWORD64>((PVOID)(Outer + 0x20)))
{
Name = GetName_New(GetFName(Outer).ComparisonIndex) + "." + Name;
}
Name = GetKlassName(Address) + " " + Name + GetName_New(GetFName(Address).ComparisonIndex);
return Name;
}
Name = GetKlassName(Address) + " " + GetName_New(GetFName(Address).ComparisonIndex);
return Name;
}
遍历结果如下:
参考代码:
https://github.com/guttir14/CheatIt
https://github.com/XiaoTouMingyo/UE4ForeachClass