ue4游戏逆向之GName内存解析(4.23版本及其以上)

发布时间 2023-08-14 20:19:29作者: 怎么可以吃突突

4.23版本及其以上与低版本的GName解析有较大差别。

解析GName

4.23版本及其以上的GName保存在内存池NamePoolData中,NamePoolData实际就是一个静态全局数组,保存的就是FNamePool对象。

FNamePool对象的第一个成员变量是一个FNameEntryAllocator对象,在类FNameEntryAllocatorBlocks成员中保存了8192个内存块的地址,每一个内存块都包含了若干个FNameEntry类型的对象。
需要注意的是类FNameEntryAllocator的第一个成员是一个锁Lock,android平台对应的是pthread_rwlock_t类型,windows平台对应的是SRWLOCK类型,对于32位的android平台而言Blocks的偏移为GName + 0x30

64位andoird平台Blocks的偏移为GName + 0x48,其FNamePool内存池的内存布局如下图所示

那么如何索引到对应的名称呢,查看FName::GetComparisonNameEntry函数,GetComparisonIndex会获取到FNameFNameEntryId ComparisonIndex

然后会将FNameEntryId ComparisonIndex类型转化为FNameEntryHandle类型,其中ComparisonIndex的高16位为Block,低16位为Offset

最后调用FNameEntryAllocator::Resolve函数,利用BlockBlocks中索引到对应的内存块之后,再加上对应的内存块内偏移Offset就得到了对应的FNameEntry对象。因为Blocks大小只有2^13(8192),所以Block只有低13位有效。

总结通过FNameFNameEntryId ComparisonIndexFNamePool (GName)中索引到FNameEntry的过程如下图所示。

遍历所有Actor的名称

frida脚本遍历(32位)ue4.23高版本的actors数组并获取对应的名称。

function hook_ue4(){
    var libUE4_module = Module.findBaseAddress("libUE4.so")
    console.log("libUE4_module is :", libUE4_module)

    var GWorld_Offset = 0xD21931C
    var GName_Offset = 0xD0E2380

    var GName = libUE4_module.add(GName_Offset);
    var GWorld = libUE4_module.add(GWorld_Offset).readPointer()
   
    var Level_Offset = 0x20
    var Level = GWorld.add(Level_Offset).readPointer()
    console.log("Level :", Level)

    var Actors_Offset = 0x70
    var Actors = Level.add(Actors_Offset).readPointer()
    console.log("Actors Array :", Actors)

    var Actors_Num = Level.add(Actors_Offset).add(4).readU32()
    console.log("Actors_num :", Actors_Num)

    var Actors_Max = Level.add(Actors_Offset).add(8).readU32()
    console.log("Actors_Max :", Actors_Max)

    for(var index = 0; index < Actors_Num; index++){
        var actor = Actors.add(index * 4).readPointer()
        //console.log("actor", actor)
        //通过角色actor获取其成员变量FName
        var FName_Offset = 0x10
        var FName = actor.add(FName_Offset);

        var FNameEntryAllocator = GName
        var Blocks_Offset = 0x30
        var Blocks = FNameEntryAllocator.add(Blocks_Offset)
        //手动解析FNamePool
        var ComparisonIndex = FName.add(0).readU32()

        var FNameBlockOffsetBits = 16
        var FNameBlockOffsets = 65536
        var Block = ComparisonIndex >> FNameBlockOffsetBits
        var Offset = ComparisonIndex & (FNameBlockOffsets - 1)

        var FNameEntry = Blocks.add(Block * 4).readPointer().add(Offset * 2)
        var FNameEntryHeader = FNameEntry.readU16()

        var isWide = FNameEntryHeader & 1
        var Len = FNameEntryHeader >> 6
        if(0 == isWide){
            console.log("actor : ", actor, " ", FNameEntry.add(2).readCString(Len))
        } 
    }
}

最后的结果如下

参考:
https://zhuanlan.zhihu.com/p/599751716