目录
一、基础理论知识
二、实践是检验真理的唯一标准
1、恢复钩子
2、发包的灵活处理以及分析
3、调用
三、总结
分析对象:天堂M 傳說
编程工具:Visual Studio 2019
工具:CheatEngine7.0 (该游戏有调试器检测,OD附加后电脑直接进入卡死状态)、火绒剑
既然是吃药call,那么我们需要先思考一下这个call的一些信息,比如说他的参数个数,触发时机等等,这样的话才能方便我们去逆向找出这个call。
首先我们明确两点:1、这个游戏的是一个网络游戏,那么就说明他和服务器存在着数据的交互,用来保证数据的正确性(封包的检测),举个例子:我们要做某个动作,那么此时客户端(就是我们正在玩的游戏)就会向服务器发送一个封包数据,然后服务器对这个封包进行一个验证,该动作是否合法或者可以操作,然后服务器再发送一个结果封包到客户端,客户端接收到封包后进行处理。如果合法,那么进行这个操作,如果不合法就阻止或者非法提示(封号)。2、我们既然是要找吃药call,那么肯定是在我们吃药的时候才会触发的游戏内部call,因为游戏不可能说在我们做其他动作的时候游戏会调用的吃药call(该情况可以存在,但是没有必要)。明确了上面两点后,我们呢就有了一些找call的思路。
思路一:通过发包。因为在我们吃药的时候,客户端肯定会发送一个封包到服务器,用来验证这个动作是否可行,然后服务器反馈到客户端。我们就可以直接在相关发包的函数头下一个断点,然后吃药,断下的时候,我们就可以通过不断的返回上一层来定位到吃药call。
思路二:通过物品的属性去逆向查找。因为药品是背包的物品,那么我们吃药这个动作也相当于一个使用背包物品的动作,此时,游戏需要知道我们使用了背包的哪个物品。然后再根据这个物品去调用他的相关函数。所以说,在我们找吃药call的时候需要注意一些的参数(也就是push的值),因为他极大的可能会存着药品的某个属性(比如:地址、ID、数量)。因此我们就可以通过这个特性直接在药品的相关属性下一个访问断点,通过调试器给我们断下的地方去定位到一些关键代码段,然后再进行逆向反推,这样的方法也是可以找到吃药call的。
笔者在本篇文章中采用的是思路一,原因是思路二看起来虽然道理很简单,但是真要去逆向反推的话其过程是相当繁琐和困难的,因为游戏公司不可能给你轻轻松松的找到call,他们肯定会进行一些代码处理和加密,让逆向的难度大大增加。其困难的程度由该call的火爆性决定(如果一个call被众多外挂所利用,那么游戏公司会对其进行一些处理,例如:加密,检测)。话不多说,咱们开始搞事!
咱们既然是通过发包下手,那么简单来介绍一下常见的发包函数: send,sendto,WSASend,这些呢基本都是现在比较常见的发包函数,游戏也基本上也是通过这三个发包函数中的其中一个去与服务器交互的。Emmm,既然我们知道,那么游戏公司也知道,所以咱们相对于某些游戏来说,直接在这些函数头部下断是会吃瘪的,需要去瞎搞更深底层的WSPSend。因为本篇文章所采用的游戏对其的处理并不怎么高手,这里呢我就不过多讨论。
这里我用的是火绒剑(官方认可工具,强大又安全)。咱们打开游戏后先来看一下这游戏干了什么事。打开火绒剑扫描钩子。
通过扫描结果我们会发现这个游戏下了很多的钩子,其中有一个就是发包函数send的钩子,我们双击看一下。
发现他的头变成了jmp,而且目的地址为游戏地址。为了确保调试正常,我们待会把他下的所有钩子全部恢复。这里注意:钩子的恢复必须要在游戏内,如果恢复钩子过早那么游戏会提示一些错误。简单说就是你得在游戏界面为以下时才能恢复。
这个游戏用的是send发包,因为三大发包函数里只有这个下断后会断下。我们用CE附加游戏后到send头部尝试下断后做动作看看
我们会发现,一旦我们下断点后,切回游戏的一瞬间,断点就被断下,无论我们重复多少次都是如此。而我们当前想要是,只有在游戏角色做某出些动作时才断下。这个就与我们想做的事产生了一些矛盾,那怎么办呢?
我们先思考一下为什么在我们切回游戏的时候,断点会断下?断点一旦被断下就说明了有封包发送到服务器,可是客户端(游戏)为什么在我们切回游戏的时候要发送封包呢?其实在切回游戏后,游戏会瞬间发送一个封包到服务器是有一定作用的,这个作用类似于游戏拉回操作。用FPS来说的话就是防止瞬移,有些FPS能实现网截瞬移就是因为游戏并不会对服务器发送一个校验的封包(有的直接通过第三方工具拦截封包实现瞬移),如果说游戏不断的向服务器发送一个请求正确位置的封包,那么服务器接收到后就会返回一个正确位置的封包,然后游戏会进行一个判断,如果当前位置不正确就直接拉回。大致原理就是这个意思。明白这个之后我们来看一下怎么去解决这个问题。稍微学过socket编程的都知道,send函数他有一个参数是包长,我们贴上MSDN的定义:
在游戏中,不同的动作他的包长也会发生变化的,因为动作不同,包数据也不同。(当然不排除一些包长一样的动作),那么我们就可以通过这个包长去下一个条件断点,只有当包长是吃药时的包长才让断点断下。那么问题来了,我们怎么知道他这个包长是多少,上面也说了,我们一下断点切回游戏,游戏就会发送一个类似校验的封包,使游戏断下,这样的话我们也没法观察吃药时的封包包长。但是只要思想不滑坡,办法总比困难多。咱们直接来一个HOOK获取包长就行了。我们在send头部下断让其断下,然后返回到外层。
(为什么不在send头部下断点,因为CE好像不支持[esp+xx] == xx 这样类型的断点,反正我没下成功),通过send的定义,包长是第三个参数,那么对应的就是eax是我们的包长,我们可以直接在push eax 下面一句进行一个hook。我们首先分配一个空白地址用来保存我们的eax,我申请的地址为0x03830000,我们直接用CE自带的代码注入HOOK,
HOOK后,咱们把刚刚申请到的地址放到列表看看。
我这里以16进制显示,我们发现现在我们可以看到包长了。那么咱们去吃药,发现吃药的时候包长也是0xE,那好办,下一个条件断点。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课