CS420 附加篇笔记 P1 - 如何寻找基址、偏移、实体的地址和指针

文章目录

    • Intro
    • [Health variable](#Health variable)
    • Entity
    • [How cheat engine works](#How cheat engine works)
    • [What is an object / a class](#What is an object / a class)
    • [Static addresses](#Static addresses)
    • Pointers
    • [Relative addresses](#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 是实际生成的一个类的实例(这一部分讲的很绕,推荐结合后续理解、找其他资料或者学习一些面向对象编程知识!

cpp 复制代码
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

对于非从业者来说,学习指针的过程可能需要耗费六个月之久,这是一个抽象的概念,不断尝试!不断试错!试着找到他们之间的逻辑!多做练习多看论坛!

相关推荐
起名字真南11 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
少年负剑去11 分钟前
第十五届蓝桥杯C/C++B组题解——数字接龙
c语言·c++·蓝桥杯
cleveryuoyuo12 分钟前
AVL树的旋转
c++
tyler_download22 分钟前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~23 分钟前
qt5将程序打包并使用
开发语言·qt
hlsd#23 分钟前
go mod 依赖管理
开发语言·后端·golang
小春学渗透24 分钟前
Day107:代码审计-PHP模型开发篇&MVC层&RCE执行&文件对比法&1day分析&0day验证
开发语言·安全·web安全·php·mvc
杜杜的man27 分钟前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang
亦世凡华、27 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
神仙别闹34 分钟前
基于MFC实现的赛车游戏
c++·游戏·mfc