FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈

如何瞄准一个敌人?我们先看到敌人的位置,然后把鼠标挪过去。

换到程序这样也是一样的,程序先读取到敌人的X Y Z坐标,然后利用鼠标模拟或者直接修改准星坐标瞄准敌人。

所以只要实现了自瞄,就相当于实现了透视,只是需要开发者通过一系列算法将其绘制在屏幕上而已。而实现自瞄,对于有相关工作经验的人来说,也算不上难,一套公式,几乎“通杀”所有FPS。

那么要实现基本自瞄,需要在游戏里逆向出的数据有:

  1. 修改有效的准星数据地址或瞄准call
    作用:填入敌人所在坐标,实现自动瞄准
  2. 敌我坐标,即敌我X Z Y坐标
    作用:计算出敌我距离,敌人所在”圆的位置。

知晓了所需数据,下一步?当然是实践了:

模块动态加载

用OD附加创世某某游戏进程, 进入OD主界面以后, 我们CTRL+G 跳转到地址 00401000(系统默认通常游戏领空的起始地址)的时候发现了 错误。

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
也就是说代码段不在这了, 那跑哪去了?

为什么会有这样的错误呢? 首先我们先来看看什么是基地址 我们随便找一个其他游戏CE附加

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
如上图,基地址 00D0DF1C 实际上是可以继续拆分,我们直接写00D0DF1C其实是不对 的。

他等于 游戏主模块句柄也就是 xxxx.exe + 90DF1C, 而这个游戏主模块句柄 ,一般是固定的 , 系统为其分配成00400000。 00D0DF1C = 00400000 + 90DF1C 由于这个00400000 是不变的, 所以 00D0DF1C 也是不变的 ,我们就没有继续拆分了。

但是并不是所有游戏, 这个模块句柄 都是不变的。 也有游戏模块动态加载的,那今天这个游戏就是动态加载。

无论是代码段还是数据段都是动态加载的。 加载的地址每次变化,代码地址当然也是跟着搬家,每次变化了,加载的地址变化,模块句 柄每次 变化,未拆分的基地址当然也是变化的,很好理解。

下面来个例子 OD附加上游戏进程 我们点开E模块窗口,

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
发现游戏模块句柄不是00400000 而是12D0000

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
那么这种情况就是模块动态加载 模块句柄每次都是不一样的 我们重新启动游戏,此时 又变成了 1300000

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
我们双击进入游戏领空

由于模块是动态加载的 基地址和代码段地址,都是由模块句柄 + 偏移所得,所以每次启动游戏 他都是动态的。

例如我们随便到游戏里找一个有基地址的地址

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
该地址是 1301041 我们把他拆分成 模块句柄 +偏移 1301041 = 1300000 +1041 = 模块句柄 +1041 以后我们想直接到这个地址 就要 用 模块句柄 +1041了

同样基地址我们也进行拆分 22D92C0 = 1300000 + FD92C0 = 模块句柄 + FD92C0 我们想直接使用这个基地址就要用 模块句柄 + FD92C0了

那么模块句柄怎么获得呢? 用GetModuleHandleA这个函数就可以了 例如:

DWORD 模块句柄 = (DWORD)GetModuleHandleA(“Crossout.exe”);

用这样的方式就和固定加载00400000的游戏没有区别了 以后本游戏中所有基地址都以该方式表达。

玩转堆栈

我们想做FPS游戏的自瞄透视功能,最基础的数据就是周围所有目标的坐标值 只有确定了所有目标的位置,我们才能用算法写出自瞄和透视。

首先我们拿自身的血量当突破口,找出周围所有目标的遍历。 竞技场血量不好找,玩家乱跑乱攻击,会给我们制造很大的麻烦。 那么我们进练习场,用碰撞的方式减少自己血量。

扫描整数型自身血量不存在结果 扫描float型自身血量得到一个结果,如下:

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈

在血量属性周围观察其他属性.

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈

在血量 +1F0 +1F4 +1F8 的位置发现了 XZY

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈

如果不拿血量当突破口,先去扫描坐标 ,由于游戏没有显示坐标,未知值去扫,会有很多结 果,很难筛选,最后会很有很多类似的结果,确定用哪一个也是一件比较累的事情。 所以选择一个合适的突破口,会事半功倍。

-58的位置也发现了 UTF_8的名称
FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
确定了 属性 我们可以开始追踪了

对血量下断断到这条代码
FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
如果我们往上追寄存器会非常的繁琐 代码非常多,而且返回多次,光是追寄存器就要追踪半小时,如果追踪能力不强很容易就丢 了

那么有没有什么好办法的呢?

有的,我们可以先去尝试 堆栈中是否出现过 该数值,如果出现过,我们直接到最先出现的位置继 续追就可以了,我们现在所在的位置相当于调用的较内层,而通过这样的方法 我们就直接来到了程序最先用该数值的地方,绕过了中间追寄存器的过程。

方法如下 首先断下以后 复制要追的寄存器的值

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
然后到堆栈中搜索

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
一直 CTRL+l 查找到最后一个位置,也就是程序中最先出现的位置

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
通过上图发现是一个call 的第一个参数(当然堆栈中的返回到是会欺骗我们的) 所以我们直接跳过去 下断验证

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
发现确实不对 第一个参数不是我们要追的数值 那么说明堆栈是有问题的 怎么办呢?我们可以 到倒数第二次出现的位置追 ,总之能省多少追踪的力气省多少

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
是这个返回到的第4个参数 那么我们 过去下断验证

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
发现是正确的 来源于ESI 我们返回上层继续追踪

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
再网上翻

FPS游戏自动瞄准敌人头部?是如何实现的(一)模块动态加载与玩转堆栈
找到了数组 公式很简单
[[22F6ECC]+esi*4]

熟悉并且玩转堆栈会让逆向事半功倍.

深入学习游戏逆向分析,****学院搜索-----吉林飞郁网络培训.

购课与选课咨询飞郁客服QQ:290330101,再根据自身情况与客服建议下购买学习,并享受不定额度优惠.