封包 “不得不说的秘密”(1) 任鸟飞 任鸟飞逆向 时间如梭,岁月似箭啊!~ 转眼又到一年之中最难熬的6 、7月份,最近几个月沉迷游戏虚度光阴 惭愧啊! 原理扯淡: 多说无益,我们直接进入主题。 在过去的几篇笔记里,我们通过利用系统发包函数找Call入手,逐渐理解与实现了如何通过自己发送封包实现功能 并且 避过游戏检测的方法。 理论上其他所有功能都应该通过自己发送封包实现,但是凡事都有例外—— 寻路Call 寻路Call的重要性无需多言,无论是自动打怪 还是 主线脚本 寻路Call都是移动角色的最佳选择。 那为什么不能我们自己去发包实现寻路功能呢?老办法hook明文封包,尝试捕捉一下寻路的封包。(HOOK输出明文包 上篇笔记已分享) 然后机会发现非常奇怪的事情,它并不像我们之前捕捉的吃药包那样 吃一个药 一个包,发一个包 吃一个药,而它则是在寻路的过程中 不间断有规律的发送 包长为0x20的封包,而且看起来它们都没啥区别。 其实它们都不是寻路包,或者说根本没有所谓的寻路封包。 因为比起吃药Call 或者说 释放技能Call,这种正常的功能Call 寻路Call说它是一种功能 更不如说它是一套算法,原因在在于寻路Call本身并不发包! 既然它不发包,那怎么实现功能的?难道服务器上没有角色位置的坐标么?那我们不是可以实现很多变态? 要想了解寻路Call,我们首先需要了解走路Call,顾名思义走路Call 就是游戏里用来移动角色的功能Call,一般点击自己附近的地板 游戏就会调用走路Call使人物直线移动到点击的目的地。 比起寻路Call 走路Call就是个铁憨憨——只能走直线,如果目的地于自身中间有一块障碍物....就会... 角色就会憨在原地不动,如果是点击小地图 调用寻路Call 角色则会绕过石头到达目的地。(当然不一定点击地板就是调用走路Call,小地图就是寻路Call,主要看游戏是怎么设计的,为了更好的体验可能都是寻路Call,我们需要根据实际情况去揣测它) 那寻路Call 咋这么能呢?它怎么实现曲线移动的?首先,我们需要了解一些小学数学知识——曲线可以看作是由无数条直线组成的,或者说可以拆解成无数条直线! 而寻路Call就是一个 根据地图障碍物与目的地坐标 计算出一条路线,这条路线尽可能由较少的直线组成 且 较短; 然后把每条直线尾端坐标保存在一个数组中,依次调用走路Call传入这些坐标,就达成了绕过障碍物 角色曲线移动的效果。 现在我们为什么要去寻找寻路Call的原因已经明了了,原来我们之前捕捉到的那些包长0x20的封包 全部是走路包,我们大可分析走路包 然后自己发送实现走路功能,可大部分情况下只能直线移动的走路功能 根本无法满足我们的需求,而我们自己实现寻路的话 还需要逆向地图中的障碍物 并实现寻路算法 这未免有些困难,得不偿失 还不如直接调用寻路Call。 说起找Call 你肯定想起了之前分享的如何断点游戏发包 定位功能Call的笔记了,但是寻路Call本身并不发包,再想使用这个快捷的方法可能就不快捷了,所以今天来认识另一种方法—— 关键数据定位功能Call。 何为关键数据? 一个实现功能的函数,里面肯定会访问一些游戏数据(毕竟程序就是数据+算法嘛)同时既然有访问 那么在数据上下访问断点就可以断到对应的函数里面,而我们根据功能Call(函数)的作用,去猜测它一定或者说可能使用到数据或参数——这就是我们要找的关键数据! 那寻路需要什么数据?很好猜,寻路嘛 无非就是移动角色到指定目的地,那目的地坐标它肯定是需要的吧? 不如它怎么知道要到哪去? 那么如何找到这个目的地坐标数据呢?找数据突破口 通常使用CE工具来扫描数值非常的方便。 卵蛋实战: 打开我们的老朋友 版幻想神域2,服务器游戏都在咱自己电脑上 不会有更新给我们造成困难非常方便练习。 以上游戏我们先随便点一次小地图进行一次寻路: 也许在寻路过程中我们不知道目的地在哪,但是等到寻路结束之后我们肯定知道了,因为我们就站在目的地上。 打开CE以X为标准搜索348 - 368之间的单浮点数,一般来说坐标一般都是浮点数 而寻路这种函数一般不会非常精确 可能到一定范围就停下了,所以我们放宽一些范围搜索。 12万多个可能,不要紧 我们继续重复刚才的过程。同时点小地图开始寻路目的地坐标变化,可以搜索变化的;寻路中或者到达目的地未继续寻路,可以搜索未变化的,以此加快筛选速度。 十几秒的事情,我们最后发现还剩下5个左右的数值 实在筛选不掉了,只要一寻路它们马上一起变化 而且都是一样的数值,分不清那个才是真的 没其他办法了 一个一个下断试试呗~ 打开OD附加游戏(下断反汇编分析个人比较喜欢用OD,CE完全也可以看个人喜好),先从第一个开始测试吧,Command里DD它,下硬件访问DWORD断点。 游戏里面寻一下路,马上就断下了。 哪我们要找的Call在哪呢?既然这是关键数据那么本层就应该是寻路Call内部,我们Ctrl+F9返回上一层分析即可。(返回之前别忘记把硬件断点给删除了) 按照道理来说它应该就是我们要找的寻路Call了,但是要问真假试试便知,使用老伙计代码注入器——我们注入一段汇编代码调用一下这个Call试试。(详细的使用方法可以看看之前的几篇笔记,这里就不过多介绍和演示了) 可惜的是注入进去之后...角色它不动,不过小地图上却出现了一个寻路位置的点,我想这应该不是寻路Call,而是在小地图上画点的Call。 那么下一个吧,和上面同意的操作一样——下断、Ctrl+F9返回上层调用处。 这次我们运气就比较不错了,注入调用代码后 角色马上就开始寻路了。 没错这个Call确实可以使用,但是一般不建议在代码封装使用它。因为游戏里面应该还有几个它的封装函数,更加封装的原则越外层的函数 往往越简单越容易调用,这样我们调用也好简单一些,同时调用最外层的参数会更接近游戏自然调用一些,理论上更不容易吃满检测。 很简单几个Ctrl+F9就找到最外层了(具体方法可以参考系列第一篇笔记,利用发包找Call的那篇) (怎么分析函数调用之前笔记已有介绍,相同操作不再重复仅提思路) 这个Call确实比之前那个简单多了,开头3个push参数固定常量我们不用管,第4个push我们可以看到是一个堆栈地址([ebp-0x1C]) 进去一看是目的地坐标结构体,第5个push 断下看它的值一直是2 我想它应该是一个地图ID,毕竟寻路需要根据地图障碍物计算路线,这种小值还不是常量一般都是ID或者操作方式之类的。最后它调用了一个函数以获取ecx的值,下断查看这个函数没有参数 可以不用理会。 参数分析完成了,接下来就可以把它封装到我们自己的代码里面。 代码参考: bool T人物属性::F寻路(float x, float y) { this->GetData(); DWORD ndx = x; DWORD ndy = y; DWORD nd结构[4] = {0,0,ndx ,ndy}; nd结构[0] = *(DWORD*)&x; nd结构[1] = *(DWORD*)&y; DWORD nd结构W = (DWORD)nd结构; __try { //逆向发现地图ID挂在人物对象下 DWORD nd地图ID = *(DWORD*)(this->nd对象 + 0xC); nd地图ID = *(DWORD*)(nd地图ID + 0x3A4); __asm { push 0x1 push 0x0 push 0x0 push nd结构W push nd地图ID mov ecx, dword ptr ds : [0xF84B74] mov eax, 0x006D8340 call eax mov ecx, eax mov eax, 0x006F0D60 call eax } } __except (1) { F输出调试信息("幻想神域 bool T人物属性::F寻路(float x, float y)异常 \r\n"); return false; } return true; } 逆向交流qun:453769015
联系我时,请说是在 挂海论坛 上看到的,谢谢! |