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就是指向的矩阵地址。

截图

  • 为什么会 - 0x20?

因为在标准的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(本地玩家)的数据结构,找到四个或三个紧紧相邻的指针:

截图

这四个紧紧相临的指针的第一个指针就是 MeshComponentMeshArray 和绘制骨骼所需要的** 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
2
3
在UE4引擎中所有的实体对象都有他的唯一 **标识符** ,子弹也包括在其中,想要实现修改子弹坐标,我们需要在游戏世界如此多的对象中找到隶属于 **子弹的唯一标识符** 。

想要修改子弹坐标,我们还需要找到给 **坐标赋值的源代码** ,对源代码进行hook,单靠修改数值是无法修改的。所以我们还要找到它修改坐标源代码的地址。

寻找所需数据:

子弹唯一标识符:(在游戏中开枪遍历世界对象对子弹标识符进行绘制)

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