【unity逆向】一种global-metadata.dat通用dump方案
0x0 前言
0x1 il2cpp源码分析
在获取到il2cpp的源代码后,我们不难看出出,不论是哪个版本的il2cpp,对于global-metadata.dat的加载方法都是不变的
均是调用vm::MetadataLoader::LoadMetadataFile对global-metadata.dat进行加载。
我们进一步分析,分析vm::MetadataLoader::LoadMetadataFile是个什么逻辑
这个函数实际上就是将global-metadata.dat加载进内存,并返回首地址,随后,在MetadataCache::Initialize函数中把返回的地址强制转换为Il2CppGlobalMetadataHeader*类型,不难判断出:global-metadata.dat被加载进内存后,s_GlobalMetadataHeader的值就是global-metadata.dat起始地址.
0x2 在ida中对il2cpp.so进行分析
用ida打开il2cpp.so,等待加载完成,在字串中搜索“global-metadata.dat”,并跳转到其交叉引用函数上,这样一来我们直接就定位到了global_metadata.dat的加载函数,即上一段中所提及的MetadataLoader::LoadMetadataFile函数

此处我们直接祭出f5,得到如下代码
不难看出,dword_937B008即为源码中的s_GlobalMetadata,dword_937B00C即为源码中的s_GlobalMetadataHeader,sub_5EB0496函数即为MetadataLoader::LoadMetadataFile
0x3 global-metadata.dat文件头分析
我们随便打开一个global-metadata.dat文件,并跑一下模板,不难得知:global-metadata.dat文件头长度为0x110h,起始的4位为魔数(这个在内存中可能会被抹除,我们后面再说),随后的4位代表了Metadata version(这个version我们不用管它,毕竟我们要的是通杀的脚本)


中间的可以全部忽略,跳到0x108h,从这个位置开始,exportedTypeDefinitionsOffset和exportedTypeDefinitionsCount两个值与整个global-metadata.dat的文件大小息息相关,我们可以通过将两值相加得到global-metadata.dat的大小。
此处需要额外注意的是这两个值的起始位置不一定是0x108h和0x10Ch,还有可能是0x100h和0x104h,这个需要通过exportedTypeDefinitionsCount的值来判断,如果在0x10C处读取到的exportedTypeDefinitionsCount的值小于10,那么上述两个值的起始位置就应该在0x100h和0x104h。
0x4 frida脚本
由于MetadataCache::Initialize函数仅会在游戏刚进入时被调用,因此我们需要通过使用frida唤起进程的方式进行hook(你手速如果够快也可以不用这么弄)
因此我们就可以得到frida的最终运行命令

在0x2板块中,我们使用ida对libil2cpp.so进行了反汇编分析,得到sub_5EB0496即为MetadataLoader::LoadMetadataFile,所以我们直接对sub_5EB0496函数进行hook(这个值是不固定的,每个so都不一样,要具体情况具体分析)
至此,我们就可以得到脚本第一部分的代码了:

但是实际运行后,我们会发现进程在刚刚被启动的时候libil2cpp.so还并未加载,因此我们的脚本会报错,想解决这个问题只需要使用setInternal设置一下定时执行,并使用try catch块防止报错导致的脚本退出

以上就是对s_GlobalMetadataHeader的值的获取,接下来我们对global-metadata.dat的大小进行计算
在0x3板块中,我们得知想要计算global-metadata.dat的大小需要获取到exportedTypeDefinitionsOffset和exportedTypeDefinitionsCount两个值的大小,即读取s_GlobalMetadataHeader + 0x108h和s_GlobalMetadataHeader + 0x10Ch两个地址上的int值

综合以上两段代码,我们可以就得出完整的脚本,如下:
0x5 后记
其实这个脚本并不能做到完全的通杀,因为还有一部分游戏采取了按需加载,此时使用这种方法导出的global-metadata.dat是不完整的。
填个坑,前面0x3说加载到内存中会抹除魔数(AF 1B B1 FA),这其实是为了防止直接搜索到global-metadata.dat在内存中的位置,但是并没卵用,我们通过hook的防止,不搜索内存直接拿到地址并dump,通杀
如果喜欢的话投个币或者点个赞啥的都行,谢谢各位读者
参考:
1.il2cpp源代码 https://github.com/4ch12dy/il2cpp