- 0.前言
- 1.准备
- 2.开始阅读
- 2.1 设置版本和Offset
- 2.2 获取GName
- 2.3 使用GName
- 2.4 获取GUObjectArray
- 2.5 使用GUObjectArray
- 2.6 寻找dump主流程
- 2.6.1 ObjectsManager::copyGObjectPtrs
- 2.6.2 ObjectsManager::copyUBigObjects
- 2.6.3 EngineCore::cacheFNames
- 2.6.4 EngineCore::generatePackages
- 3.疑问
- 4.结尾
0.前言
笔记(二)中我们拿到了GWorld,GName和GUObjectArray的偏移值,接下来就以这些为输入,来学习一下UEDumper的源码中是怎么使用这些偏移的,以及UEDumper的工作流程。
UEDumper利用GWorld,GName和GUObjectArray,结合UE的反射机制dump出SDK
在了解了UE引擎的反射机制后,我猜测,UEDumper的基本工作流程是:
a.通过偏移+基址的方式获取到目标进程中的GWorld,GName,GUObjectArray
b.遍历GUObjectArray和GWorld中的所有实例
c.再通过GName查询到实例中的各种名称
d.输出
1.准备
源码地址:https://github.com/Spuckwaffel/UEDumper
版本:UEDumper-1.11.1
我这边工程可直接打开并编译,如此步有问题欢迎交流。
如果有对使用UEDumper有疑惑的,可先查看此视频,或自己试试看。
2.开始阅读
2.1 设置版本和Offset
- 打开工程后定位到UEdefinitions.h,或者从代码中的UE_VERSION宏F12过去
在此处设置好被Dump程序的UE版本
- 再定位到Offset.h
这里需要填入GWorld,GName和GUObjectArray的偏移值
setOffsets函数里,代码将GWorld,GName和GUObjectArray的偏移值与对应的名称关联并存入了容器offsets中
2.2 获取GName
搜索函数名setOffsets
定位到如下位置:
在EngineCore类的构造里,调用了setOffsets,将返回的偏移值们存入成员offsets中,再调用函数getOffsetForName和getOffsetAddress来获取GName并存入成员gName。
2.3 使用GName
搜索gName,定位到函数std::string EngineCore::FNameToString(FName fname);这个函数可以将fname(字符串索引)转化为string(字符串)。
功能对应了我猜想中的:
c.再通过GName查询到实例中的各种名称
我们继续看看怎么查询GName,下图是我大概的理解。
注释上有说道,此函数的算法是来自UE引擎源码的,关于FNamePool,FName等类型的具体的结构留到UE源码学习的时候再了解。
2.4 获取GUObjectArray
本来想通过追踪函数FNameToString的引用来追踪遍历GUObjectArray的部分,但是引用的地方比较多且有上层包装。
但是通过上面的分析,我知道了使用offset的时候需要调用getOffsetForName和getOffsetAddress,以此线索搜索到下图:
在ObjectsManager类的构造里看到了熟悉的操作,获取到GUObjectArray后将其保持在成员gUObjectManager.UObjectArray中。
2.5 使用GUObjectArray
搜索gUObjectManager.UObjectArray,我看到了一些循环遍历操作,也有通过index来查找对象指针的操作。
但是ObjectsManager类终归只是提供功能的模块,我现在更想找到程序运行的主流程代码。
观察ObjectsManager类的定义时,我发现了函数setSDKGenerationDone,用来设置SDK生成完成时的状态。
初始状态为“正在获取SDK”,setSDKGenerationDone会将状态设置为“正在使用SDK并查找新的ovject”
2.6 寻找dump主流程
搜索全部setSDKGenerationDone。
发现除了ObjectsManager中的声明和定义,只有一处调用了。
调用发生在DumpProgress::render()函数中,从日志信息来看,就是dump主流程了。
继续阅读代码,看到了之前见过的ObjectsManager和EngineCore
到第5步调用setSDKGenerationDone时,获取SDK就已经完成了,下面再逐步探索前4个步骤。
2.6.1 ObjectsManager::copyGObjectPtrs
函数声明提到,该函数的功能是将UObjectArray中的所有指针复制到一个大缓冲区中。
代码实现中用了一个循环,来遍历UObjectArray数组中的每一个对象指针。
2.6.2 ObjectsManager::copyUBigObjects
该函数的功能是将UObjectArray数组中每个UObject对象复制到缓冲区中
同样也是使用循环遍历,每个UObject获取到UObjectManager::UBigObject类对象中,存储在容器gUObjectManager.linkedUObjectPtrs中。
2.6.3 EngineCore::cacheFNames
该函数的功能是遍历上一步保存的所有UObject对象,缓存其名称(FName)。
object->getName()内部即为2.3中看过的函数FNameToString
关于缓存
2.6.4 EngineCore::generatePackages
该函数的功能是生成所有包。
函数比较大,就先不截图了,主要功能包括了:
a.遍历所有UObject对象
b.判断和统计UObject对象类型
c.生成包文件
3.疑问
一圈学习下来,居然没有发现GWorld的用处,不过根据原理来看,GUObjectArray是包含了GWorld,下一步实战的时候,可以将GWorld填错试试看。
4.结尾
这次学习只能说大概了解了UEDumper的工作原理,其中很多算法细节,类型结构等都还没有弄清楚,大部分还是与UE源码相关,后面将继续学习。