Unreal Engine 4 从入门到入狱
Unreal Engine 4 从入门到入狱
什么是Uworld?
Uworld 地址( 世界地址 ) -> 包含 Actor ( 对象数组 )
1 | -> 包含 **Count** ( 对象数量 ) |
Count:对应 Actor 对象数组中的对象计数
Actor:
- 是包含Uworld世界中所有实体对象的对象数组(比如游戏中的一辆载具,或者是一只丧失)
- 对象数组中每个对象的间隔: 0x8 0x10 0x18 0x20( 根据游戏的不同每个对象的间隔有时也不同 )
关系: Count - 8 = Actor( 游戏中可能有多个Count 有的数值大 有的数值小)
如何使用CE寻找Uworld
利用Count( 数量 )反向寻找Uworld
1 | 在UE4游戏中,一般一颗子弹也算一个实体对象,所以我们可以利用这个特性。先大致估一个Count的数值范围(如 1 - 3000),使用CE搜介于两者之间的4字节类型。 |
利用Actor = Count - 8 的关系确定找到的地址是否为 Count
这里找到了一个地址为 293F37B4860 的地址
选中这个地址 Ctrl+B浏览内存 在内存浏览窗口内右键把显示类型改成8字节16进制
这里的57A就是十六进制的1402对应上上图
我们找到 293F37B4860 的上一个地址293F37B4858按空格进入
可以看到这种排列整齐结构相同的地址列,所以我们可以确定此地址就是Count,地址293F37B4858就是Actor( Actor = Count - 8 )
我们继续右键Count地址,点击”找出是什么访问了这个地址”
点击显示反汇编程序 根据汇编代码我们可以看到
rax+000000A0 就是我们的Count,而 rax 又等于 rcx+30 所以我们要搞清楚rcx是多少。
我们可以在下面两处代码处分别下端运行 看看寄存器里的rax 或 rcx是否是静态地址
SCUM.exe+3F6A139 - 48 8B 41 30 - mov rax,[rcx+30]
SCUM.exe+3F6A142 - 83 B8 A0000000 00 - cmp dword ptr [rax+000000A0],00
经过多次运行,我们发现分别在上述两行代码处下端运行rax 或 rcx都不会变化,所以我们
可以直接在CE中搜索rcx的值,找到对应该值得静态地址
==6D52D40== 就是Uwolrd的静态偏移
如何使用CE寻找Gname
什么是Gname?
1 | Gname是用于UE4中储存所有实体对象类名名称的名称表,每个实体对象中都有一个ID来对应Gname中相应的类名名称。 |
如何寻找4.23版本一下Gname(旧版)
待记录。
如何寻找4.23版本以上Gname(新版)
利用CE查找关键字符串:ByteProperty
找到类似这样,地址 - 8 后地址后四位等于 0000 的地址
我们继续搜索这个 - 8 后的地址 29329150000会出现一个静态地址
这个静态地址(SCUM.exe+6BD0550)所指向的地址 - 10(HEX)就是Gname
所以==6BD0540==就是Gname的静态偏移。
如何使用IDA寻找Uworld
打开游戏后,使用“Scylla_v0.9.8”转储该游戏的dump文件
用IDA打开刚才转储的SCUM_DUMP.exe文件进行分析(过程稍慢,耐心等待)
分析完后,我们需要记录一个值(Imagebase),这个值就相当于CE中的SCUM.exe ( 基地址 )。
Shift+F12分析字符串,等待IDA处理完毕,
然后我们按Ctrl+F搜索关键字符串:SeamlessTravel FlushLevelStreaming
双击进去然后Ctrl+X交叉引用
用这个地址减去我们之前记录的基地址就可以得到当前版本Uworld的静态偏移。
如何使用IDA寻找Gname
通过关键字符串: ByteProperty 搜索
Ctrl+X交叉引用
得到一个比较规整的类似CE搜索该字符出现的比较规律的排列
我们F5反汇编此段函数,然后拉到该函数的头部,为了更好的找到这个函数头,我们把他的头部修改为FnamePool_fun.
然后返回IDA View-A视窗拉到这个函数头部随便找个函数交叉引用进去F5反汇编
上图这个地址就是Gname。
我们可以减去我们之前记录的基地址,就可以的到Gname的静态偏移。
如何使用IDA寻找GameInstance.Ulevel.Actor
如何使用IDA寻找Ulevel
我们通过关键字符串: 找到Uworld地址
按X,在引用里寻找两个一样的 Call 指令
双击进入CALL
0x30便是Ulevel的静态偏移。
如何使用IDA寻找GameInstance
通过搜索字符串: InWorld->GetGameInstance() is null 双击进去
然后交叉引用 Ctrl+X
如何使用CE寻找LocalPlayer.PlayerController
LocalPlayer(本地播放器).PlayerController(玩家控制器).Apawn(本人)都包含在GameInstance里
在所有标准的UE4引擎中,PlayerController的偏移都为 0x38->0x0->0x30
所以LocalPlayer(本地播放器的偏移为 0x38->0x0),我们可以通过其他方法验证
因为SDK里 LocalPlayer(本地播放器)的变量是由数组方式呈现的,每个数组都有最大
数量和当前数量,因为我们本地玩家只有一个所以最小数量是1,而4就是定义数组时他的
最大数量,所以0x38就是localplayer的指针。
接下来我们在Localplayer中寻找Apawn(本地玩家):
我们要找到两 - 三个相差不远,地址后四位相同的指针( 一般从0x100偏移处往后找)
这三个相同的指针都是Apawn,我们随意用哪个偏移都可以。
如何利用 Apawn(本地玩家)寻找共用坐标偏移:
在标准的UE4引擎中,公用坐标的偏移一般在 0x100 - 0x160 之间。
我们在0x100 - 0x160之间先寻找到一个数组指针,特征类似这样:
在这个数组指针的下方就是我们的公用坐标(RootComponent)偏移:0x130
在公用坐标中一般有3组类似坐标:
可以用做我们验证此处是否为公用坐标。
小结:我们通过GameInstance找到了如下关系数据:
GameInstance->ULocalPlayer->LocalPlayer->PlayerController->Apawn->RootComponent->Vector(坐标)
如何利用通用特征码寻找矩阵偏移(两种方法)
矩阵特征码:
我们找到带有矩阵特征的地址:(下图为矩阵特征,Ctrl+B后需要向下翻找)
我们将此地址(2270C3AA280)加入到地址列表中
方法一(自动扫描):
我们右键此地址点击 “对这个地址进行指针扫描”
注意:将这个扫描的ptr缓存文件保存在不能含有中文的路径中,否则扫描结果不显示。
扫描后,第一个偏移为 0x20 的地址就是矩阵地址。
方法二(手动跟踪):
我们右键此地址 “ 找出是什么访问了这个地址 ”
点击 显示反汇编程序
我们在rbx+280处下断运行,观察rbx是否变化
不变化,复制rbx的值,取消刚才断点继续运行,在CE中搜索刚才复制的值,我们得到11个结果
在这些地址中找到有访问的地址将其加入到地址列表中,一般都为这样
然后我们将这个地址 -0x20 然后继续搜索这个值
得到一个静态地址,这个地址+0x20+0x180就是指向的矩阵地址。
因为在标准的UE4引擎中这个 0X20 是固定的,
老版本的矩阵地址偏移一般为: 0x20->0x270
新版本的矩阵地址偏移一般为: 0x20->0x280
如何利用PlayerController(玩家实例)寻找鼠标X、Y?
我们直接对PlayerController(玩家实例)进行结构分析,找到指向APawn的三个相同的指针地址
继续晃动鼠标,我们发现只有0x28C的值类似鼠标坐标,我们将其添加到地址列表中,此时这个地址的值为-121.06。Ctrl+B浏览内存发现他-4的地址还有一个类似的值,我们也将其添加到地址列表中。
这两个地址其实就是我们的鼠标地址,0x288为Y,0x28C为X 。
在一般游戏中较小的偏移一般为X,较大的偏移一般为Y,但在UE4中正好相反。
如何利用PlayerController(玩家实例)寻找相机?
我们还是找到指向APawn(本地玩家)的三个相同的指针地址
我们在CameraController中找到一组类似坐标的值(其中有很多0.xxx的数都不是,继续往下找)
我们发现一组数据,摇晃鼠标数值会随之浮动,所以这个就是CameraPos(相机坐标)
我们通过下图对比发现,其中有两个坐标和我们鼠标XY的值有时相同
这两个值所对应的地址就是CameraRot,我们可以利用这个地址进行相机转换。
所以0xEB8就是我们的CameraFov。
如何利用Apawn(本地玩家)寻找MeshArray(骨骼数组)?
什么是MeshArray(骨骼数组)?
1 | 在虚幻4游戏世界中骨骼点都是以数组的方式呈现,骨骼数组是实体对象的一部分,或者说是一个组件,所以MeshArray(骨骼数组)是包含在APawn(本地玩家)里的。根据这个特性我们就可以寻找到MeshArray和其他绘制骨骼所需要的指针。 |
如何寻找 MeshArray(骨骼数组)?
我们首先分析 Apawn(本地玩家)的数据结构,找到四个或三个紧紧相邻的指针:
这四个紧紧相临的指针的第一个指针就是 MeshComponent,MeshArray 和绘制骨骼所需要的** ComponentToWorld **就包含在里面。
找到 **MeshComPonent **以后,接下来我们寻找 **MeshArray **,类似下图结构的就是 **MeshArray **:
展开这两个指针,在游戏中走动这两个指针里的数据都在变化,这两个用哪个都可以。
如何寻找 ComponentToWorld?
我们翻到 MeshComponent 指针的头部,从头开始找两组类似坐标的数据:
第一组:
第二组(ComponentToWorld就包含在里面):
如何寻找 MeshNameArray(骨骼名称数组)?
我们返回到** MeshArray (骨骼数组)**的位置:
找到第一个骨骼数组往上的第一个指针:
我们进入 StaticMesh(静态骨骼指针):
我们找到两组包含数量和定义数量和我们骨骼数组相同的两个数组,
第一个数组就是 MeshNameArray(骨骼名称数组)
MeshNameArray(骨骼名称数组)内部排列:
如何寻找 Objectid 以及 PlayerState ?(补充)
什么是Objectid?
**Objectid **是用于储存对应实体对象类名ID的一个变量,可以配合Gname的算法得到这个实体对象的类名。
如何寻找Objectid?
我们随便找一个实体对象指针,这里我们用APawn (本人)指针来演示:
在标准的UE4引擎中,实体对象指针 + 0x18 就是我们的Objectid
如何寻找 PlayerState ?
PlayerState 在结构中位于 MeshComponent 的上面,我们可以利用 MeshComponent 寻找:
我们还是分析Apawn的结构,在APawn中找到 MeshComponent ,MeshComponent 的上方有两个紧紧相邻的指针
离 MeshComponent 最近的那个指针就是 PlayerState 。
有时CE分析会出现这种结果:
我们就需要更改地址显示的数据类型来查看是否是指针
这两个指针的下面都有一个字符串形式的数据。且字符串下面的4字节数据都为2。(可以多找几个UE4游戏判断通用特征,积累实战经验。)
如何寻找 PlayerName(玩家的自定义名称) ?
PlayerName 被包含在 PlayerState 里,其他 Teamid、KillCount也包含在其中。
我们先找 PlayerName:
进入 PlayerState 指针,找到如下图所示排列的指针 (最多翻500偏移即可找到)
一个一个进去看指针的头部有没有Unicode String 类型的 数据 也就是字符串数据。我这里用的是Apawn分析的,所以这个字符串应该是我自己的名字,建议用APawn来分析。
如何寻找 人物穿墙 灵魂出窍 以及 几种加速?
人物穿墙 和 灵魂出窍 都比较好找 都在APawn头部
全局加速:
局部加速:
在APawn里找到那四个紧紧相临的四个指针,
第一个是MeshComponent(骨骼组件)第二个就是MoveComponent(移动组件)进入移动组件
靠近头部,很多指针相邻的下面
制作特征码快速获得所需偏移!
Uworld:
48 89 0D ?? ?? ?? ?? 4D 85 F6 74 ?? 49 [+3]
Gname:
48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 8B 54 ?? ?? 4C 8B C0
Matrix:
41 8B ?? ?? 83 F8 ?? 74 ?? 48 8B 15 ?? ?? ?? ?? 0F 1F 84 ?? ?? ?? ?? ?? 48 63 C8 48 8D
Ulevel:
48 8B ?? ?? 0F B6 EA 48 85 C9 74 ?? 41 0F [+3]
Count:
48 8B 41 ?? 48 85 C0 74 ?? 83 B8 ?? ?? ?? ?? ?? 74 ?? B0
Actor:
49 8B 85 ?? ?? ?? ?? 4A 89 ?? ?? 4D 63 BD ?? ?? ?? ?? 41 8D
GameInstance:
48 8b 86 ?? ?? ?? ?? 48 85 C0 48 8D ?? ?? ?? ?? ?? 48 8B
ULocalPlayer:
48 8B ?? ?? 48 8B ?? ?? 48 85 D2 74 ?? 48 8B 42 ?? 48 85 C0
Apawn:
48 8B 8E ?? ?? ?? ?? 48 8B 9C ?? ?? ?? ?? ?? 48 85 C9 75
RootComponent:
49 8B 9D ?? ?? ?? ?? 48 85 DB 74 ?? 0F 10
Vector3D:
41 0F 10 8A ?? ?? ?? ?? 0F ?? 4B ?? 41
PlayerState:
48 8B 9B ?? ?? ?? ?? B8 ?? ?? ?? ?? 48 8B
MeshComponent:
48 8B 8F ?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? 48 85 C0 74 ?? 48
使用Hook实现子弹追踪(受子弹下坠影响)!
SCUM 子弹追踪特征码:
41 0F 28 D0 E8 ?? ?? ?? ?? F2 (失效)
41 0F 28 ?? E8 ?? ?? ?? ?? 44 0F 28 ?? ?? ?? F2 0F 10
特征:
- 有两个相同的Call
- Call下面有一个 Je 跳转
- 在第二个Call处下断,开枪会断住
追踪地址(Hook地址):SCUM.exe+1D81103
追踪地址反汇编代码:
SCUM.exe+1D810D1 - 0F28 D0 - movaps xmm2,xmm0
SCUM.exe+1D810D4 - E8 171B60FF - call SCUM.exe+1382BF0
SCUM.exe+1D810D9 - F3 44 0F59 1D 9E8F2C03 - mulss xmm11,[SCUM.exe+504A080]
SCUM.exe+1D810E2 - 4C 8D 4C 24 70 - lea r9,[rsp+70]
SCUM.exe+1D810E7 - 48 8D 54 24 30 - lea rdx,[rsp+30]
SCUM.exe+1D810EC - 48 8D 4C 24 40 - lea rcx,[rsp+40]
SCUM.exe+1D810F1 - 41 0F28 D3 - movaps xmm2,xmm11
SCUM.exe+1D810F5 - E8 F61A60FF - call SCUM.exe+1382BF0
SCUM.exe+1D810FA - 44 0F28 9C 24 00020000 - movaps xmm11,[rsp+00000200]
SCUM.exe+1D81103 - F2 0F10 38 - movsd xmm7,[rax]
SCUM.exe+1D81107 - F3 44 0F10 40 08 - movss xmm8,[rax+08]
SCUM.exe+1D8110D - 0F28 F7 - movaps xmm6,xmm7
SCUM.exe+1D81110 - 0FC6 F6 55 - shufps xmm6,xmm6,55
利用Hook在CE中实现简单的子弹追踪:
空白特征:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
我们首先在游戏中找一段空白的地址:SCUM.exe+6100820
然后在空白地址写入我们Hook的汇编代码:
字节代码(Hook):
66 83 3D 38 00 00 00 00 74 16 F2 0F 10 3D 3E 00 00 00 F3 44 0F 10 05 3D 00 00 00 E9 CD 08 C8 FB F2 0F 10 38 F3 44 0F 10 40 08 EB EF
回到我们的Hook地址:
将此汇编修改为:
Hook地址汇编:E9 18 F7 37 04 90 90 90 90 90
将下方Je 汇编代码,修改为Jne。
关键:
设置空白地址内存页属性为(可执行/可读/可写)!!!!
因为我们在Hook中做了一个比较,所以我们有一个开关的功能。
总结:
- 空白地址:SCUM.exe+6100820
- 空白地址汇编:66 83 3D 38 00 00 00 00 74 16 F2 0F 10 3D 3E 00 00 00 F3 44 0F 10 05 3D 00 00 00 E9 CD 08 C8 FB F2 0F 10 38 F3 44 0F 10 40 08 EB EF
- Hook地址:SCUM.exe+1D81103
- Hook地址修改汇编:E9 18 F7 37 04
- Je跳转地址:SCUM.exe+1D8113D
- Je地址汇编:75 71
- 开关地址:SCUM.exe+6100860 (四字节)
- 追踪.x : SCUM.exe+6100870 (单浮点)
- 追踪.y : SCUM.exe+6100874 (单浮点)
- 追踪.z : SCUM.exe+6100878 (单浮点)
我们往追踪地址传入我们想让他击打的坐标,就可以完成追踪。
使用Hook实现魔术子弹(瞬击,穿墙)!
原理:
使用Hook手段修改出膛子弹的坐标,让子弹直接飞到目标脸上。
1 | 在UE4引擎中所有的实体对象都有他的唯一 **标识符** ,子弹也包括在其中,想要实现修改子弹坐标,我们需要在游戏世界如此多的对象中找到隶属于 **子弹的唯一标识符** 。 |
寻找所需数据:
子弹唯一标识符:(在游戏中开枪遍历世界对象对子弹标识符进行绘制)
HEX : 95710A (经多次测试,每种口径的子弹ID都为 0x95710A)
源代码:访问 RootComponent 下第三组坐标偏移的地址。
右键找出是什么改写了这个地址:
因为这次我们需要比较类名,所以写的复杂一点,但也大差不差。
原始代码:
SCUM.exe+3A1B4C7 - 33 D2 - xor edx,edx
SCUM.exe+3A1B4C9 - EB 1C - jmp SCUM.exe+3A1B4E7
SCUM.exe+3A1B4CB - 0F11 BF C0010000 - movups [rdi+000001C0],xmm7
SCUM.exe+3A1B4D2 - 45 8B C6 - mov r8d,r14d
SCUM.exe+3A1B4D5 - 44 0F11 A7 D0010000 - movups [rdi+000001D0],xmm12
SCUM.exe+3A1B4DD - B2 01 - mov dl,01
SCUM.exe+3A1B4DF - 44 0F11 B7 E0010000 - movups [rdi+000001E0],xmm14
SCUM.exe+3A1B4E7 - 48 8B CF - m ov rcx,rdi
老方法找一个空白地址写入我们的Hook代码:
空白地址:SCUM.exe+6100900
Hook地址:SCUM.exe+3A1B4D5 - 44 0F11 A7 D0010000 - movups [rdi+000001D0],xmm12
我们还是要写一个开关,让他需要的时候再执行
总结数据:
修改后:
SCUM.exe+3A1B4C7 - 33 D2 - xor edx,edx
SCUM.exe+3A1B4C9 - EB 1C - jmp SCUM.exe+3A1B4E7
SCUM.exe+3A1B4CB - 0F11 BF C0010000 - movups [rdi+000001C0],xmm7
SCUM.exe+3A1B4D2 - 45 8B C6 - mov r8d,r14d
SCUM.exe+3A1B4D5 - E9 26546E02 - jmp SCUM.exe+6100900
SCUM.exe+3A1B4DA - 90 - nop
SCUM.exe+3A1B4DB - 90 - nop
SCUM.exe+3A1B4DC - 90 - nop
SCUM.exe+3A1B4DD - B2 01 - mov dl,01
SCUM.exe+3A1B4DF - 44 0F11 B7 E0010000 - movups [rdi+000001E0],xmm14
SCUM.exe+3A1B4E7 - 48 8B CF - mov rcx,rdi
Hook段汇编:
SCUM.exe+6100900 - 66 83 3D 48000000 00 - cmp word ptr [SCUM.exe+6100950],00
SCUM.exe+6100908 - 74 09 - je SCUM.exe+6100913
SCUM.exe+610090A - 81 7F 18 0A719500 - cmp [rdi+18],0095710A
SCUM.exe+6100911 - 74 17 - je SCUM.exe+610092A
SCUM.exe+6100913 - 44 0F11 A7 D0010000 - movups [rdi+000001D0],xmm12
SCUM.exe+610091B - B2 01 - mov dl,01
SCUM.exe+610091D - 44 0F11 B7 E0010000 - movups [rdi+000001E0],xmm14
SCUM.exe+6100925 - E9 BDAB91FD - jmp SCUM.exe+3A1B4E7
SCUM.exe+610092A - 44 0F10 25 2E000000 - movups xmm12,[SCUM.exe+6100960]
SCUM.exe+6100932 - 44 0F11 A7 D0010000 - movups [rdi+000001D0],xmm12
SCUM.exe+610093A - 8B 0D 28000000 - mov ecx,[SCUM.exe+6100968]
SCUM.exe+6100940 - 89 8F D8010000 - mov [rdi+000001D8],ecx
SCUM.exe+6100946 - EB DD - jmp SCUM.exe+6100925
- 空白地址:SCUM.exe+6100900
- Hook汇编:66 83 3D 48 00 00 00 00 74 09 81 7F 18 0A 71 95 00 74 17 44 0F 11 A7 D0 01 00 00 B2 01 44 0F 11 B7 E0 01 00 00 E9 BD AB 91 FD 44 0F 10 25 2E 00 00 00 44 0F 11 A7 D0 01 00 00 8B 0D 28 00 00 00 89 8F D8 01 00 00 EB DD
- Hook汇编:66 83 3D 48 00 00 00 00 74 09 81 7F 18 D1 6E 95 00 74 17 44 0F 11 A7 D0 01 00 00 B2 01 44 0F 11 B7 E0 01 00 00 E9 10 E6 91 FD 44 0F 10 25 2E 00 00 00 44 0F 11 A7 D0 01 00 00 8B 0D 28 00 00 00 89 8F D8 01 00 00 EB DD
- Hook地址:SCUM.exe+3A1B4D5
- Hook地址汇编:E9 26 54 6E 02 90 90 90
- 开关地址:SCUM.exe+6100950 (4字节)
- 坐标.x : SCUM.exe+6100960
- 坐标.y : SCUM.exe+6100964
- 坐标.z : SCUM.exe+6100968