-
-
[原创]UE4逆向笔记之GObjectArray
-
发表于:
2023-1-11 16:01
10709
-
GObjectArray逆向:
首先查看源代码:
此处上面存在字符串特征:
1 | Cannot Cancel Async Loading while async loading is suspended.
|
直接搜索该字符串就可以定位到GUObjectArray.GetObjectArrayNum()
:
然后查看GetObjectArrayNum的源代码:
1 2 3 4 | FORCEINLINE int32 GetObjectArrayNum() const
{
return ObjObjects.Num();
}
|
跟进ObjObjects
发现定义如下:
1 | TUObjectArray ObjObjects;
|
继续跟进:
1 | typedef FChunkedFixedUObjectArray TUObjectArray;
|
得到如下类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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;
|
找到方法的定义如下:
1 2 3 4 | FORCEINLINE int32 Num() const
{
return NumElements;
}
|
发现返回的就是NumElements
这个,所以7FF78E0D11A4
该地址其实就是ObjObjects.NumElements
,而上面存在5个元素,5*4=0x14,所以该地址-0x14就可以得到GObjectArray:
此时的第一个元素为:
1 2 3 4 5 6 7 8 9 10 11 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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是一个二级指针:
也就是说他和Gname一样是以表的形式存在的,所以我们想要遍历他就需要知道当前需要遍历的对象在哪张表中,算法如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 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代码中就有,可以复制过来直接用:
遍历代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 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的名称,具体可以查看我之前的文章,部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
代码下载及交流群:662851495
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!