UE4引擎分析获取UWord,GName,GetName,GObjectArray

UE4引擎分析获取UWord,GName,GetName,GObjectArrayUE4 引擎具有很多固定特征和特性所以我们在逆向 UE4 引擎开发的游戏的时候 可以利用这些特征和特性 当然 正常思路逆向也是没问题的 例如我们的 ttw 课程全数据逆向 你就当多了一个针对于该引擎逆向

大家好,欢迎来到IT知识分享网。

UE4引擎具有很多固定特征和特性

所以我们在逆向UE4引擎开发的游戏的时候,可以利用这些特征和特性.

当然,正常思路逆向也是没问题的(例如我们的ttw课程全数据逆向),你就当多了一个针对于该引擎逆向的快捷方式 或则多了一个思路即可.

正常逆向+引擎分析,使逆向结果全面且快速.

了解一下我们的学习顺序,

先熟悉UE4特性,找到UWord,GName,GetName,GObject等关键数据(入门阶段不使用IDA,但是效率更快)

然后把这些数据跟正常逆向的关系对应清晰,以提高对UE4的熟悉

再学习UE4正向开发和源码,从根本上了解该引擎

最后完整性dump游戏数据,边角数据用逆向方式补全即可

1.查看游戏的引擎版本

首先我们来查看游戏引擎版本

游戏启动程序所在目录如下:(任何UE4游戏都是类似目录)

UE4引擎分析获取UWord,GName,GetName,GObjectArray

我们可以右键属性查看游戏版本,如果长期分析经验以后,可以根据版本做出更多的判断

实际情况还是我们跟着最新版本即可,老版本等于淘汰.

下面这个4.26.2.0 已经是目前的新版本了

UE4引擎分析获取UWord,GName,GetName,GObjectArray

同时需要注意,附加进程也是

UE4引擎分析获取UWord,GName,GetName,GObjectArray

2.UWorld

首先我们先来获取一下UE4引擎中的UWorld.

UWorld其实就是世界数组基地址 .

世界数组基地址下面挂着一个包含所有对象的数组,这在我们讲FPS专题的时候已经讲解过了,忘了的同学可以翻回FPS转体复习一下.

根据ue4引擎世界对象数组的特点,我们可以采取以下方式来进行扫描,

例如 打枪数组对象数量 +1或则+2,拿出手雷 +1或者+2,也就是说出现新物品+不定数量,还有什么增加方式,大家可以找到以后多观察一下.

例如手雷增加,原本手雷不在模型上显示,打出的子弹也是有对象的,所以子弹打中某个碰撞体也会增加。

RPG也是相同,对象只会增加不会减少这是他的一个很容易被利用的特点,所以我们就利用这个道理进行扫描.

进入游戏,选择一个小地图,方便我们可以搜少点的值

搜索0-1000

像障碍物或则目标开枪 搜索增加的数值 增加可能是1可能是2

最终可以锁定地址

到OD中下断追表达式:

r14+B0

UE4引擎分析获取UWord,GName,GetName,GObjectArray

往上继续追 得到表达式 r15+B0

UE4引擎分析获取UWord,GName,GetName,GObjectArray

追到函数头部 得到表达式 [rcx+30]+B0

UE4引擎分析获取UWord,GName,GetName,GObjectArray

返回,发现 [rax+30]+B0 此时RAX 来源于一个call

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这地方有三种方法

第一种直接调call 取得的返回值就是 UWORD 里的值

第二种方法直接CE搜索就可以搜索到UWORD的基地址

UE4引擎分析获取UWord,GName,GetName,GObjectArray

UWorld = deathlystillnessgame-win64-shipping.exe+460F4F0

第三种也可以继续逆向来源

call 内来源

UE4引擎分析获取UWord,GName,GetName,GObjectArray

继续进call, 由于是虚表可以进入很多位置,算法也都不同

其中有一个是我们追熟悉的位置

UE4引擎分析获取UWord,GName,GetName,GObjectArray

再进call

UE4引擎分析获取UWord,GName,GetName,GObjectArray

好像发现了什么吗?

就是天堂W里类似取对象的call,可以参考天堂W,他的过程类似于一个加密过程,忘记了 可以回去看下天堂W课程

其实这里追不到UWORD的基地址,但是我们得到的加密表达式 是可以执行UWORD里的的,一样是可以当UWORD来使用.

相比之下,低版本的UE4的UWORD就比较好找了

例如

低版本吃鸡模拟器的世界数组数量如下:

[[“BattleRoyaleTrainer-Win64-Shipping.exe”+2AF0FB8] +138 ]+ b8

[[“BattleRoyaleTrainer-Win64-Shipping.exe”+2AF0FB8 ] +30 ]+ b8

“BattleRoyaleTrainer-Win64-Shipping.exe”+2AF0FB8就是UWORD

经过了正常逆向,我们看看还有什么快捷方式来搜索到UWORD

就是引擎特征码了.

我们用XDBG 搜索所有模块字符串,需要等待一会,速度会比较慢,用IDA搜索这个速度更慢

xdbg搜索字符串: SeamlessTravel FlushLevelStreaming

UE4引擎分析获取UWord,GName,GetName,GObjectArray

剩余唯一一个结果

UE4引擎分析获取UWord,GName,GetName,GObjectArray

用IDA搜索这个速度慢,而且IDA搜索到的位置可能会不全,可以用xdbg搜索字符串,用IDA查看伪代码

跳转到这个地址向上翻可以得到btr edx,0x7,再向上遇到的第一个基地址,就是UWorld

UE4引擎分析获取UWord,GName,GetName,GObjectArray

UWorld = deathlystillnessgame-win64-shipping.exe+460F4F0

UE4引擎分析获取UWord,GName,GetName,GObjectArray

跟我们正常逆向的结果一样的

当然,正常逆的方式是比较通用的,特征的方式很容易在几个版本之后失效,当然失效以后,我们可以在通过正向开发的方式找到新的特征不是吗?

有了UWORD ,实际上我们就能遍历到周围环境所有对象.

3.GName

什么是GName?GName保存着UE4整个世界对象的名字

世界对象下只有Key(可以理解成名称ID),没有直接的名字的,所以想要获取名字必须要先搞定GName

说的直白点,GName是存放游戏里面所有名称字符串的基地址

分析清晰这些字符串的结构

然后通过GetName函数(也就是从Key到字符串的转换)调用取得名称字符串

名称字符串内存中的格式一般如下:

None

ByteProperty

前两个 一般是None 和ByteProperty

CE扫描ByteProperty, 以后版本不是 ByteProperty怎么办? 可以正向编译一个最新版本,看看里面任意一个相对比较特殊的字符串即可,我们不是要一个精确的位置

一个大概的位置即可

UE4引擎分析获取UWord,GName,GetName,GObjectArray

所有地址拉下来

CTRL+b 挨个查看,找到连续字符串,并且开头第一个是None,当然目前所有版本都是这样,改动我们可以人为识别

UE4引擎分析获取UWord,GName,GetName,GObjectArray

观察结构 就是 2字节+一个字符串 再2字节 再一个字符串 以此类推(后面我们会知道这个2字节就是加密长度)

那么ByteProperty字符串 – 8 的位置应该就是结构头部

CE直接搜索 2024E3C0008 – 8 = 2024E3C0000

UE4引擎分析获取UWord,GName,GetName,GObjectArray

直接得到基地址 , 再 -10就是 GName

GName = “DeathlyStillnessGame-Win64-Shipping.exe”+44BDB80

这里直接说他是GName视乎有些无赖,其实就是无赖… 那么我们用正常逆向的方法来追到GName以及GetName算法

4.GetName

我们对字符串下访问断,例如ByteProperty

小退或则开始游戏会断下

UE4引擎分析获取UWord,GName,GetName,GObjectArray

字符串下断

 | CC | int3 | | CC | int3 | | CC | int3 | | 48:895C24 08 | mov qword ptr ss:[rsp+0x8],rbx | | 4D:8BD0 | mov r10,r8 | | 4C:8BCA | mov r9,rdx | rcx | 4C:8BD9 | mov r11,rcx | rcx -rdx + r9 | 4D:85C0 | test r8,r8 | | 74 23 | je deathlystillnessgame-win64-shipping.7FF6F18A3EF6 | | 4C:2BDA | sub r11,rdx | r11+ r9 = r11-rdx + r9 | 48:8D1D D3DC6F02 | lea rbx,qword ptr ds:[0x7FF6F3FA1BB0] | | 0F1F00 | nop dword ptr ds:[rax],eax | | 43:0FB6040B | movzx eax,byte ptr ds:[r11+r9] | r11+r9 r9是堆栈地址 R11只是堆栈和真实地址的偏移 | 45:0FB601 | movzx r8d,byte ptr ds:[r9] | 断下的位置 | 4D:8D49 01 | lea r9,qword ptr ds:[r9+0x1] | | 41:3AC0 | cmp al,r8b | | 75 0C | jne deathlystillnessgame-win64-shipping.7FF6F18A3EFE | | 84C0 | test al,al | | 75 2C | jne deathlystillnessgame-win64-shipping.7FF6F18A3F22 | | 33C0 | xor eax,eax | | 48:8B5C24 08 | mov rbx,qword ptr ss:[rsp+0x8] | | C3 | ret | | 41:0FBED0 | movsx edx,r8b | | 0FBEC8 | movsx ecx,al | | 0BD1 | or edx,ecx | | F7C2 80FFFFFF | test edx,0xFFFFFF80 | | 75 21 | jne deathlystillnessgame-win64-shipping.7FF6F18A3F30 | 

返回追rcx

追字符串是什么表达式指向的, 发现整个表达式都是由ID(也可以叫Key)决定的

全部分析流程如下:

 | 48:895C24 20 | mov qword ptr ss:[rsp+0x20],rbx | | 55 | push rbp | | 56 | push rsi | | 57 | push rdi | | 41:56 | push r14 | | 41:57 | push r15 | | 48:8D6C24 C9 | lea rbp,qword ptr ss:[rsp-0x37] | | 48:81EC A0000000 | sub rsp,0xA0 | | 48:8B05 E | mov rax,qword ptr ds:[0x7FF6F4F2B7B8] | | 48:33C4 | xor rax,rsp | | 48:8945 2F | mov qword ptr ss:[rbp+0x2F],rax | | 41:0F1000 | movups xmm0,xmmword ptr ds:[r8] | | 4C:8BF2 | mov r14,rdx | | 48:8BD9 | mov rbx,rcx | rcx==rbx==GName 返回可得 | 0F10C8 | movups xmm1,xmm0 | | 0F2945 D7 | movaps xmmword ptr ss:[rbp-0x29],xmm0 | | 48:8B55 D7 | mov rdx,qword ptr ss:[rbp-0x29] | | 66:0F73D9 08 | psrldq xmm1,0x8 | | 6648:0F7EC8 | movq rax,xmm1 | | 6641:0F7EC8 | movd r8d,xmm1 | | 48:C1E8 20 | shr rax,0x20 | | 0F1145 07 | movups xmmword ptr ss:[rbp+0x7],xmm0 | | 84C0 | test al,al | | 75 0B | jne deathlystillnessgame-win64-shipping.7FF6F19BA711 | 0 | 48:8D4D E7 | lea rcx,qword ptr ss:[rbp-0x19] | | E8 F11DFFFF | call deathlystillnessgame-win64-shipping.7FF6F19AC50 | | EB 09 | jmp deathlystillnessgame-win64-shipping.7FF6F19BA71A | 1 | 48:8D4D D7 | lea rcx,qword ptr ss:[rbp-0x29] | | E8 D61EFFFF | call deathlystillnessgame-win64-shipping.7FF6F19AC5F | | 0F1000 | movups xmm0,xmmword ptr ds:[rax] | | C645 2B 00 | mov byte ptr ss:[rbp+0x2B],0x0 | 00007FF6F4FEDB80 | 66:0F7EC7 | movd edi,xmm0 | rbx==Gname Gname+10040+0*40 | 0F1145 17 | movups xmmword ptr ss:[rbp+0x17],xmm0 | [[rbx+10040+040+10]+n*4] | 48:81C7 0 | add rdi,0x401 | [[rbx+(rdi+401)*40+10]+n*4] | 48:C1E7 06 | shl rdi,0x6 | [[rbx+rdi*40+10]+n*4] | 48:03FB | add rdi,rbx | [[rdi+rbx+10]+n*4] | 48:8BCF | mov rcx,rdi | | FF15 E0D04202 | call qword ptr ds:[<&RtlAcquireSRWLockExclusive>] | | 8B5D 1B | mov ebx,dword ptr ss:[rbp+0x1B] | | 48:8B47 10 | mov rax,qword ptr ds:[rdi+0x10] | rax = [[rdi+10]+n*4] | 44:8B7F 0C | mov r15d,dword ptr ds:[rdi+0xC] | | 41:23DF | and ebx,r15d | | 48:8D3498 | lea rsi,qword ptr ds:[rax+rbx4] | | 8B0498 | mov eax,dword ptr ds:[rax+rbx4] | 数组中取ID 给eax 这里rbx是多少不用管,因为我们要遍历全部 | 85C0 | test eax,eax | | 0F84 DE000000 | je deathlystillnessgame-win64-shipping.7FF6F19BA83B | 不跳>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | 0F1F00 | nop dword ptr ds:[rax],eax | | 8BC8 | mov ecx,eax | | 81E1 000000E0 | and ecx,0xE0000000 | | 3B4D 1F | cmp ecx,dword ptr ss:[rbp+0x1F] | | 0F85 9D000000 | jne deathlystillnessgame-win64-shipping.7FF6F19BA80E | 不跳>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | 25 FFFFFF1F | and eax,0x1FFFFFFF | and 1FFFFFFF | 8BD0 | mov edx,eax | eax>>0x10 第几页 | 0FB7C8 | movzx ecx,ax | ax 字符串偏移 | C1EA 10 | shr edx,0x10 | edx>>0x10 第几页 | 8955 C7 | mov dword ptr ss:[rbp-0x39],edx | | 894D CB | mov dword ptr ss:[rbp-0x35],ecx |实际是这 ecx字符串偏移 | 48:8B45 C7 | mov rax,qword ptr ss:[rbp-0x39] |来源于这 | 48:8B4F 18 | mov rcx,qword ptr ds:[rdi+0x18] | | 48:C1E8 20 | shr rax,0x20 | (rax>>0x20)*2 字符串字符串数*2 ==前面字符串长度 | 44:8D0400 | lea r8d,qword ptr ds:[rax+rax] | r8 ==前面字符串长度 | 4C:0344D1 10 | add r8,qword ptr ds:[rcx+rdx8+0x10] | 字符串==[rcx+rdx*8+10]+r8(前面字节长度)+2(头部2字节) | 41:0FB710 | movzx edx,word ptr ds:[r8] | 头部两字节 | 66:3B55 23 | cmp dx,word ptr ss:[rbp+0x23] | | 75 6B | jne deathlystillnessgame-win64-shipping.7FF6F19BA80E | 不跳>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | 0FB7C2 | movzx eax,dx | 头部两字节给了 eax | 49:8D48 02 | lea rcx,qword ptr ds:[r8+0x2] | 字符串== r8+2 跳过 头部2字节 因为我们之前知道 字符串前面有2字节长度 | C1E8 06 | shr eax,0x6 | 头部两字节>>6 解密长度 >>6以后我们知道哦 原来是字符串长度 不难观察哦 | F6C2 01 | test dl,0x1 | | 74 11 | je deathlystillnessgame-win64-shipping.7FF6F19BA7C3 | 来源都是rcx 字符串== rcx | 8945 FF | mov dword ptr ss:[rbp-0x1],eax | | 48:8D45 F7 | lea rax,qword ptr ss:[rbp-0x9] | | 48:894D F7 | mov qword ptr ss:[rbp-0x9],rcx | rcx 来源一 | C645 03 01 | mov byte ptr ss:[rbp+0x3],0x1 | | EB 0F | jmp deathlystillnessgame-win64-shipping.7FF6F19BA7D2 | | 8945 DF | mov dword ptr ss:[rbp-0x21],eax | | 48:8D45 D7 | lea rax,qword ptr ss:[rbp-0x29] | | 48:894D D7 | mov qword ptr ss:[rbp-0x29],rcx | rcx 来源二 | C645 E3 00 | mov byte ptr ss:[rbp-0x1D],0x0 | | 0F1000 | movups xmm0,xmmword ptr ds:[rax] | 字符串==[rax] | 48:8B55 07 | mov rdx,qword ptr ss:[rbp+0x7] | | 0F2945 E7 | movaps xmmword ptr ss:[rbp-0x19],xmm0 | 字符串==XMM0 | 48:8B4D E7 | mov rcx,qword ptr ss:[rbp-0x19] | 字符串==[rbp-19] | 66:0F73D8 08 | psrldq xmm0,0x8 | | 66:0F7EC0 | movd eax,xmm0 | | 4C:63C0 | movsxd r8,eax | | 48:8B45 0F | mov rax,qword ptr ss:[rbp+0xF] | | 48:C1E8 20 | shr rax,0x20 | | 84C0 | test al,al | | 75 07 | jne deathlystillnessgame-win64-shipping.7FF6F19BA800 | | E8 C296EEFF | call deathlystillnessgame-win64-shipping.7FF6F18A3EC | 返回call====上面是从这里返回的 | EB 05 | jmp deathlystillnessgame-win64-shipping.7FF6F19BA805 | | E8 BB97EEFF | call deathlystillnessgame-win64-shipping.7FF6F18A3FC | | 85C0 | test eax,eax | | 0F94C0 | sete al | 

返回是GName

00007FF69D0ECD66 | 803D B7D76203 00 | cmp byte ptr ds:[0x7FF6A071A524],0x0 | 00007FF69D0ECD6D | 74 09 | je deathlystillnessgame-win64-shipping.7FF69D0ECD78 | 00007FF69D0ECD6F | 48:8D05 0A0E6403 | lea rax,qword ptr ds:[0x7FF6A072DB80] | Gname 在这里 00007FF69D0ECD76 | EB 13 | jmp deathlystillnessgame-win64-shipping.7FF69D0ECD8B | 00007FF69D0ECD78 | 48:8D0D 010E6403 | lea rcx,qword ptr ds:[0x7FF6A072DB80] | 00007FF69D0ECD7F | E8 6C050000 | call deathlystillnessgame-win64-shipping.7FF69D0ED2F0 | 00007FF69D0ECD84 | C605 99D76203 01 | mov byte ptr ds:[0x7FF6A071A524],0x1 | 00007FF69D0ECD8B | 0F1003 | movups xmm0,xmmword ptr ds:[rbx] | 00007FF69D0ECD8E | 4C:8D4424 20 | lea r8,qword ptr ss:[rsp+0x20] | 00007FF69D0ECD93 | 48:8BC8 | mov rcx,rax | 00007FF69D0ECD96 | 48:8D5424 30 | lea rdx,qword ptr ss:[rsp+0x30] | 00007FF69D0ECD9B | 0F 20 | movaps xmmword ptr ss:[rsp+0x20],xmm0 | 00007FF69D0ECDA0 | E8 0BD90000 | call deathlystillnessgame-win64-shipping.7FF69D0FA6B0 | 返回call 2 00007FF69D0ECDA5 | 48:8B5C24 58 | mov rbx,qword ptr ss:[rsp+0x58] | 00007FF69D0ECDAA | 8B08 | mov ecx,dword ptr ds:[rax] | 00007FF69D0ECDAC | 48:8BC7 | mov rax,rdi | 00007FF69D0ECDAF | 890F | mov dword ptr ds:[rdi],ecx |

整理公式:

GName = “DeathlyStillnessGame-Win64-Shipping.exe”+44BDB80

GName+10040+n*40 Key结构体数组开始指针

n== 0 到 F 也可以根据 对象+18 里的值是否等于 GName 判断

$-18 0000000000000000

$-10 0000000000000000

$-8 0000000000000000

$ ==> 0000000000000000====从这开始

$+8 00001FFF0000142A

$+10 00000

$+18 00007FF6F4FEDB80

$+20 000000A

$+28 0000000000000000

$+30 0000000000000000

$+38 0000000000000000

$+40 0000000000000000====第2个对象

$+48 00001FFF000013E0

$+50 0000029726CE0000

$+58 00007FF6F4FEDB80

$+60 0000002B000013E0

$+68 0000000000000000

$+70 0000000000000000

$+78 0000000000000000

$+80 0000000000000000====第3个对象

$+88 00001FFF00001407

$+90 0000029726D90000

$+98 00007FF6F4FEDB80

$+A0 0000002E00001407

$+A8 0000000000000000

$+B0 0000000000000000

$+B8 0000000000000000

每个对象 +10 进入 存放了1FFF个4字节的ID

ID == [[GName+10040+n*40+10]+i*4]

最上面

UE4引擎分析获取UWord,GName,GetName,GObjectArray

拉到最下面了

UE4引擎分析获取UWord,GName,GetName,GObjectArray

[00007FF6F4FEDB80+10040+n*40+10]+ i*4 (n==0 到F i ==0到 1FFE) 这样可以取得游戏中的全部ID

取得所有ID,注意判断是否为0 ,0其实对应的就是None

得到ID以后:

第几页 = (ID and 0x1FFFFFFF)>>0x10

字符串偏移 =WORD (ID and 0x1FFFFFFF)

长度== WORD PTR :[[Gname+第几页*8+10]+字符串偏移*2 ] >> 6

内容地址 = [Gname+第几页*8+10]+字符串偏移*2 +2(头部2字节)

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这样就可以遍历出全部字符串了

UE4引擎分析获取UWord,GName,GetName,GObjectArray

一共8W+ ,还是非常多的,运行不卡,输出会比较卡,怕卡的同学可以把字符串全部相加以后 一起输出.

ps:可以观察一下

头部两字节/ 0x40 (>>6) 就是真实长度, 这种可能有不同的加密方法,通过上面的方法都是可以逆向出来的

UE4引擎分析获取UWord,GName,GetName,GObjectArray

GetName 其他的方法

当然找GetName我们也可以用IDA 或则 XDBG 搜索ByteProperty

IDA搜索 ByteProperty,搜索到多个

IDA ctlr+X 转到引用,发现 连续字符串的就是我们要的

UE4引擎分析获取UWord,GName,GetName,GObjectArray

XDBG也一样

UE4引擎分析获取UWord,GName,GetName,GObjectArray

跳过去 跟IDA是一样的

UE4引擎分析获取UWord,GName,GetName,GObjectArray

同时告诉你们个秘密,还记得我们之前分析的位置吗

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这个头部下断返回的是GName

如果我们在头部查找引用

UE4引擎分析获取UWord,GName,GetName,GObjectArray

会发现很多个引用,但是其实 除了我们返回GName的位置

其他都是一个地方

UE4引擎分析获取UWord,GName,GetName,GObjectArray

也就是 XDBG 和IDA 搜索字符串的位置

IDA中我们点F5

然后拉到函数头部 点X 查看调用

UE4引擎分析获取UWord,GName,GetName,GObjectArray

其中一个是getname,挨个分析下参数即可,当然XDBG也可以

具体算法和我们上面分析的差不多,不再赘述

这里注意:

我们通过这样方式找到的GetName函数只能静态观察,因为,他是访问不断的

因为他只有在游戏初始化的时候调用分配一次

而逆向的算法和他其实是一个,是可以随时断下查看的, 也就是说动态调试的方式是不能主动断到GetName的,但是可以断到算法

5.GObjectArray

GObject 是保存着世界的对象地址

我们先利用特征找到

xdbg扫描字符串CanvasObject

得到一个结果,跳转到该条代码,并向上翻找带有sar的代码

UE4引擎分析获取UWord,GName,GetName,GObjectArray

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这一条下面的基地址就是Gobject

Gobject=deathlystillnessgame-win64-shipping.exe+44D6128

发现没发现 这个基地址 贼眼熟

他就是之前 获取对象call,里面的基地址,这个数组套数组的基地址就是 GObject

UE4引擎分析获取UWord,GName,GetName,GObjectArray

6.旧版本UE4案例吃鸡模拟器—-三件套分析

游戏进程在…./Binaries/Win64内

UE4引擎分析获取UWord,GName,GetName,GObjectArray

UWorld

xdbg搜索字符串: SeamlessTravel FlushLevelStreaming

用IDA搜索这个速度慢,而且IDA搜索到的位置可能会不全,可以用xdbg搜索字符串,用IDA查看伪代码

搜索字符串的过程比较缓慢,注意用xdbg找完UWorld不要把搜索到的字符串关掉,后面可能还需要找其他的数据

xdbg搜索得到一个结果

UE4引擎分析获取UWord,GName,GetName,GObjectArray

跳转到这个地址向上翻可以得到btr edx,0x7,再向上遇到的第一个基地址,就是UWorld

UE4引擎分析获取UWord,GName,GetName,GObjectArray

UWorld = battleroyaletrainer-win64-shipping.exe+2AF0FB8

UWorld的找发是UE4通用的,新老版本一样

GName

找GName的关键词是ByteProperty

找Gname的目的是为了获取UE内的所有字符串及其对应的ID,ByteProperty通常是所有字符串中的第2个,也就是说其ID是1,ID为0的为None,当然这个ID并不绝对,可能会有改变

用CE扫描ByteProperty

UE4引擎分析获取UWord,GName,GetName,GObjectArray

在内存对每一个结果逐个进行观察,找到连续字符串

UE4引擎分析获取UWord,GName,GetName,GObjectArray

很幸运的是第一个结果就是这样的,而后面的挨个看了下没有这类结构的

其实我们用xdbg观察这个结构会更清晰

UE4引擎分析获取UWord,GName,GetName,GObjectArray

我们会发现,每一个字符串前面会空出0x10字节,并且这0x10字节的第一个DWORD数值是0,2,4,6,8……这种,那么我们可以猜到1E01C就是这个结果的头部,这时我们可以用CE对其进行扫描然后得到2个地址

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这里我们选择第一个地址,由于我们扫描的none的地址,所以直接扫描两次,可以得到Gname

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这两个地址都可以用,但是为了稳妥,我们用访问断的方式再追一次,

对名字下访问,然后关闭游戏时会访问这个名字,

UE4引擎分析获取UWord,GName,GetName,GObjectArray

返回追rcx

UE4引擎分析获取UWord,GName,GetName,GObjectArray

最终的来源时上面的CALL

UE4引擎分析获取UWord,GName,GetName,GObjectArray

在CALL里同样可以得到Gname

UE4引擎分析获取UWord,GName,GetName,GObjectArray

这种方法的好处是可以看到字符串的具体公式,并找到Gname的位置

出现数组的这一层,如果再向外返回,就可以来到getname的位置,getname需要传入2个参数

第一个参数是一个指向ID的指针,对于这种没有加密的老版本来说,ID传0-3FFF即可

第二个参数是一个空结构体,返回的字符串会写到这个结构体里

其实最好的方式并不是调用这个函数,因为这个函数只是为了取name字符串用,我们只要知道取name用的是什么样子的ID,然后用相应的算法和规则将所有字符串和ID一起遍历,并输出出来即可,并不一定需要调用函数,自己写的函数效率可能会高一些

Gname = battleroyaletrainer-win64-shipping.exe+2AD75C8

GObjectArray

在字符串中扫描Unexpected concurency while adding new object

这句话的含义是添加新对象时出现意外,而这句话的上面正是添加对象的判断代码,

UE4引擎分析获取UWord,GName,GetName,GObjectArray

随意找一条代码查看

UE4引擎分析获取UWord,GName,GetName,GObjectArray

仔细分析,可以发现上面的rdi+10是一个数组的起始地址,而下面一条就是添加对象的判断,在头部点击右键,查看引用可以很容易得到rdi的来源

UE4引擎分析获取UWord,GName,GetName,GObjectArray

当然,我们也可以用IDA查看伪代码

UE4引擎分析获取UWord,GName,GetName,GObjectArray

a1是函数的第一个参数,在函数处查看交叉引用,发现第一个参数就是基地址

UE4引擎分析获取UWord,GName,GetName,GObjectArray

142AD9F20-=0x2AD9F20

GObject

=battleroyaletrainer-win64-shipping.exe+0x2AD9F20+10

=battleroyaletrainer-win64-shipping.exe+0x2AD9F30

7.DUMP

先简单说下DUMP

使用IDA静态分析前 ,先用xdbg 把游戏dump 一下

步骤如下:

UE4引擎分析获取UWord,GName,GetName,GObjectArray

选择进程

UE4引擎分析获取UWord,GName,GetName,GObjectArray

点DUMP

UE4引擎分析获取UWord,GName,GetName,GObjectArray

最后会有一个错的,delete即可

然后打开IDA

dump 文件直接拖进 IDA ,出现对话框,直接取消取消即可.

然后我们就可以shift+F12 分析字符串了,当然这里分析的速度会比XDBG慢一些.

本章节内容就到这里,下一章我们继续研究DUMP,欢迎大家关注公众:任鸟飞逆向,共同学习讨论

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/126581.html

(0)
上一篇 2025-09-17 18:15
下一篇 2025-09-17 18:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信