ue4游戏逆向之GName内存解析(4.23版本及其以上)
4.23版本及其以上与低版本的GName解析有较大差别。
解析GName#
4.23版本及其以上的GName
保存在内存池NamePoolData
中,NamePoolData
实际就是一个静态全局数组,保存的就是FNamePool
对象。
FNamePool
对象的第一个成员变量是一个FNameEntryAllocator
对象,在类FNameEntryAllocator
的Blocks
成员中保存了8192
个内存块的地址,每一个内存块都包含了若干个FNameEntry
类型的对象。
需要注意的是类FNameEntryAllocator
的第一个成员是一个锁Lock
,android平台对应的是pthread_rwlock_t
类型,windows平台对应的是SRWLOCK
类型,对于32位的android平台而言Blocks
的偏移为GName + 0x30
。
64位andoird平台Blocks
的偏移为GName + 0x40
,其FNamePool
内存池的内存布局如下图所示
那么如何索引到对应的名称呢,查看FName::GetComparisonNameEntry
函数,GetComparisonIndex
会获取到FName
的FNameEntryId ComparisonIndex
,
然后会将FNameEntryId ComparisonIndex
类型转化为FNameEntryHandle
类型,其中ComparisonIndex
的高16位为Block
,低16位为Offset
。
最后调用FNameEntryAllocator::Resolve
函数,利用Block
从Blocks
中索引到对应的内存块之后,再加上对应的内存块内偏移Offset
就得到了对应的FNameEntry
对象。因为Blocks
大小只有2^13(8192)
,所以Block
只有低13位有效。
总结通过FName
的FNameEntryId ComparisonIndex
在FNamePool (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))
}
}
}
最后的结果如下
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】