文章目录
- Intro
- Health variable
- Entity
- How cheat engine works
- What is an object / a class
- Static addresses
- Pointers
- Relative addresses
- Summary
Intro
这一篇进入了进阶内容,讲的内容也变得即有广泛又有深入,推荐有一定基础和实践或者编程经验的观看!
通常来说,进入游戏的第一件事往往是找 生命值,你可以从生命值地址追溯到本地玩家对象并获取其地址
Health variable
首先确认我们的生命值,搜索 4bytes 的精确数值 Exact value,随后通过挨打/穿脱装备来改变生命值!
如果生命值是精确数值的话,仅改变一次后,即可找到2个数值,通过经验判断一个是生命值最大值,一个是当前生命值,我们再穿一件装备看一下哪个数值在跳就可以知道留哪个了
如果你在操作的是单机游戏,直接修改或者冻结是更不错的办法!需要注意的是,有可能数值仅用于 GUI 显示,即你获得的地址是 GUI 从玩家对象获取的备份数据地址,而不是 玩家对象的真实数据,一般这种情况筛选重找就好了
Entity
一般来说我们都要找到一个 Object 对象,而生命值 Health 存储在里面
例如 Player 就是 C 语言里面定义出来的一个类,在这情况下也就是是一个 struct 结构体。所以我们的 Local Player 也就是一个实体对象 Entity Object
所以现在生命值极有可能在实体对象内部,一般来说汇编代码知道如何访问 Health 地址,因为汇编指令需要从 Player 处出发开始寻址,找到 [PlayerAddr + 0B0] 这样的地方来获取生命值
How cheat engine works
这一部分推荐多看原视频,比较玄学,有一点在讲内功的感觉
编译器要想办法实现这个过程,对我们来说不必关注里面的细节,只需要找到是什么在 read/write 这个地址即可,也就是CS420课程中学到的 Find out what accesses/writes to this address,一般来说 accesses 包含了 read & write,但 write 只有 write,需要根据情况来判断使用
通过 找出是什么访问了这个地址,有时这个过程不会这么轻松,你要通过 writes 和 accesses 一起找,熟能生巧!而且有的时候你不能直接一眼判断出他就是生命值偏移,很多时候这是不可靠的,需要借助后续的技巧来判断,假设,验证
有一个情况是:有可能所有对象都是 GameObject,包括矿石,家具,栅栏,GameObject 衍生出了 Player,那么你想找 XYZ 坐标,很有可能就无法通过 Player 结构体找到了!这需要你保持一些直觉性,对数据的敏锐性,和重复大量的实验和试错,很多时候这些东西是没办法讲述教学的
这是写入的情况:
这是读取的情况:
我们来试着理解这个案例:[eax+40] 获取了生命值,然后把他读取到了一些地方,比如 UI,技能计算线程等等,那么这个 eax 地址的数据或许只是一个 数据引用锚点,开放给 UI 数据通信之类的,这种帧刷新的,往往就不是实际有用的线索
再看看上面的写入,经过尝试,发现 eax 就是实际的 Player 对象基址,即为 317161D78
What is an object / a class
object 就是编程时你设置的 class 的实例,class 包含了一个类型的数据,而 object 是实际生成的一个类的实例(这一部分讲的很绕,推荐结合后续理解、找其他资料或者学习一些面向对象编程知识!
PlayerEntity obj = new PlayerEntity();
还是 CS420 里面讲过的,我们可以用 内存视图 Memory view 里面的 Tool 来解析结构体 Struct,这个功能非常好用:
输入地址,添加新的结构,这就得到了我们想要的结构分析。他不总是正确的,只是作为一个大致的猜测和指导,你可以通过这个找到很多好的信息,比如:如果你找到了3个连续的 Float,那基本上就是找到 XYZ 坐标了!如果你跳一下发现有东西变了,那就是 Z 坐标(我这里就是上面说的阴间情况,Player 数据和 GameObject 数据是分开的)
这样一来,我们就知道了 entity object 的基址 Base address,还有 health 的偏移 Offset
Static addresses
下一步是要找到静态地址,静态地址始终与模块相关,而模块总是加载到同一位置
所以,我们想找到指向实体的指针对象,指针只是一个 包含另一个地址 的地址
Pointers
现在我们进行新的扫描,这里要使用 Hex 十六进制,因为我们要粘贴十六进制数据进来了
扫描出来的结果都是包含地址或者实体对象的变量,在视频作者的演示中,他这里已经是最终的指向 Player 对象的模块
而有时有可能扫描出来这样的效果,根据经验,显然这就是我们上面所说的:GameObject 中存储的指向 PlayerState 的指针!
此时根据新的地址进行结构体分析,就可以找到 XYZ 坐标了,这里展示了 Tool 不能良好处理的地方:
此时他们被解析成 4 bytes,显然这个值不会是 int 类型,回顾一下之前了解到的 byte 信息:
float 和 int 都是 4bytes,我们点击 4 Bytes 把数据的解析方式改成 Float 类型
这就得到了正确的 XYZ 坐标信息!
当你发现一些绿色的地址,打开以后发现内部是这样的
其中这个 ac_client.exe 就是模块,模块加上偏移,最终指向的代码块是固定不变的,每次重启游戏都不会变化
可以按下 Ctrl + H 或者通过 Memory View -> Tools -> Dissect PE headers 内存查看器来剖析 PE 标头
这些都是加载到内存里的不同模块,当你点击 INFO 信息时可以查看里面的具体信息
Relative addresses
上面的 PE 标头将会在后续其他课程中介绍,回到刚刚我们找到的地址处
这些找到的 绿色的地址 是固定在模块的某个位置上的,而 黑色的地址 也就是最常见到的动态地址,他们可能是通过 C 语言的 new 或者 malloc 分配得到的,所以他们会随着虚拟内存初始化进行加载
因此查找指针的目标就是向后追踪,直到找到一个静态地址,现在我们只是一个 1-level pointer 即 1级指针
在这里不是简单的把 ac_client.exe + 10F4F4 相加,而是通过相加之后得到的地址进行解引用,最终得到了 00A3A7D0 这个指针路径上的中间量
通过这样的多级指针,我们最终就寻址到了玩家生命值处!
Summary
对于非从业者来说,学习指针的过程可能需要耗费六个月之久,这是一个抽象的概念,不断尝试!不断试错!试着找到他们之间的逻辑!多做练习多看论坛!