失落的方舟-加速瞬移漏洞修复
很多游戏里面都存在加速,瞬移的BUG.
但是这样的BUG,到底怎么挖掘 和修复呢? 为什么这样的大型热门游戏, 投巨资开发,还会存在这样的bug呢?到底是什么原理呢? 是不是有很多疑问?
不要急,这是我们今天主要讨论的话题. 就拿目前全球火热的LOSTARK来做讲解
并且给出相应的修复建议.
在正式研究之前,我们先来了解下理论原理
1.游戏为什么会产生瞬移?
就是人物的坐标,服务器没有进行严格验证,说白了就是你在哪,要你告诉服务器,否则服务器也不知道
那岂不是随意修改, 改到哪里都可以
不过这么明显的BUG,现在的新游戏已经很少了,都是一些老游戏中存在.服务器只要对坐标进行一定保存和验证即可避免,并不是什么难的问题
2.那么新游戏存在不存在瞬移呢?
理论上都是存在的,服务器可以对坐标进行验证,但是并不能完全精确并且实时的去验证.
如果完全精确验证,那么只要游戏用户稍微有一点网络波动,就会不断的走路被拉回,这是很影响用户体验的对网络环境也是要求极其严格的, 如果实时验证也是会对用户体验 和 打击感等等产生很严重的影响.
所以,一般游戏对瞬移 都是有一定的容忍度的.
什么叫容忍度? 你上次的坐标是100,100. 这次发包坐标是102,102 服务器认为这是网络波动的正常误差,那么承认的坐标有效, 这个可以承认的最大距离,就是容忍度.
例如企鹅的XXZ 他的容忍度是0.997米 大约半个身位.
例如我们今天的主角 失落的方舟, 他的容忍度是220坐标单位左右, 大约2个身位
当然容忍度 还和不同服务器,不同角色,不同等级 等等也有关系,这要看服务器的判定
也就是说 可以瞬移,但是瞬移的距离有了限制.
你可能会说, 切! 瞬移这面点距离有啥用? 别急 这只是BUG 的开始,我们先要懂的基础原理
3.什么是加速?
加速很容易理解, 加快人物的移动速度即是加速
一些不安全的老游戏中, 是可以修改人物速度的, 实际上啊 和修改坐标达到瞬移没什么区别.
都是修改人物坐标而已,不过前者是一次性修改,后者呢,是正常走路的情况下,加快了原来的修改速度而已.
所以理论上,瞬移和加速 是没有本质区别, 加速 ≈ 瞬移
4.了解了这些,我们就来看看, LOSTARK 是怎么瞬移加速的吧
有的同学会说,一种思维, 既然我们可以瞬移200多, 不断的瞬移就好了! 这肯定不行啊 你瞬移服务器并不知道
当你动的时候 ,服务器还是会判定你的距离不对,立刻拉回.
说到这聪明的同学已经反应过来了,
其实很简单
我们瞬移200以后, 跟服务器通信一下啊,告诉服务器我们在这, 然后服务器一看我们和原来坐标有了200以内的波动, 承认我们当前的位置了,我们再瞬移,循环往复, 就可以瞬移前行了.
怎么和服务器通信确认坐标呢? 随便走路,寻路,就可以了,因为这些封包里是包含坐标的
所以
瞬移—>走路—->等待少许延迟让服务器确认—->瞬移—-走路—循环以上操作
如果这个瞬移的距离很大 200左右,就是一种瞬移的感觉, 如果瞬移的距离比较小,就是一种加速的效果
下面这是很温和的速度,只加速2-3倍,
(本文后面有暴力的8倍速度,哪里不懂可以公众号任鸟飞逆向讨论)
因为有瞬移,同样还有穿墙效果
LOSTARK调试的建议(该部分不需要可以不看)
这个游戏,在有的系统或则电脑上,当我们用DBG 附加并且下断断下的时候,会出现鼠标键盘卡顿的情况。
(不是轻微的卡顿,是完全没有办法控制)
即使用了驱动工具,也是一样的。
这种情况,一般是由于鼠标键盘钩子导致的,我们可以用PCH,XT等工具查看钩子并且进行还原处理,但是工具也不是百分百可以处理好的。
不过还有另外一种更简单一些的办法,那就是尝试砍掉他相应的检测线程。
这种线程可以实时检测到我们,而不是间歇性的
那么有2个特点必须满足
一是单独放到一个线程的可能大
二是执行优先级比较高和执行频率要比较快
根据这样的特点,我们到游戏中看一下线程
我们按照优先级排列一下
由于上面2点原因,这里面我们优先实时优先级的线程,如果不行再考虑高优先级的线程
这里面有3个实时线程
我们砍掉其中一个发现断下不在卡顿了
那么我们的目的就达到了,是不是很简单
当然调试能达到效果即可,不用像软件那么严谨。
朝向
朝向我们要找一下,毕竟,你瞬移不是乱瞬移,要往固定方向(例如寻路过程中的身前)
游戏逆向中,人物朝向属性一般都在人物坐标附近,就好像蓝量一般都会在血量附近一个道理!
但是这个游戏偏移有那么一丢丢大,我们在之前找到的坐标附近观察了半天没看出来。
于是选择CE扫描,
通过不断转换人物朝向,CE搜索变化的值,
找到一个0—-FFFF的值。修改可以改变人物朝向,
发现在我们之前找的坐标下面,不过偏移大一点,
表达式:
朝向 = *(DWORD*)(*(QWORD*)(对象2 + 0x90) + 0x4B4);
4B4的偏移 我们没有看到也算正常。
改变朝向,根据坐标方位分析得出结论:
X正朝向为0 和FFFF(朝向必有一个朝向是最大值和最小值重叠的)
Y正朝向为 3FFF
X负朝向 7FFE
Y负朝向 BFFD
注意:我们这里面为了锻炼 x 和 y 的理解 画出来的坐标系 不是标准坐标系
而是 纵轴X 横轴 Y
这样朝向就分析清楚了
瞬移
直接修改人物坐标就可以达到瞬移的效果,但是只能短距离瞬移,如果长距离瞬移,会被服务器修正回来。
事实上,如果不做发包动作的话,短距离瞬移也只是本地的效果,服务器是接收不到的
简单的瞬移代码如下:
void Call_瞬移(FLOAT X, FLOAT Y, FLOAT Z)
{
extern QWORD 角色基地址2;
QWORD Temp = *(QWORD*)角色基地址2;
Temp = *(QWORD*)(Temp + 0x440);
QWORD 对象2 = *(QWORD*)(Temp + 0x18);
*(FLOAT*)(*(QWORD*)(对象2 + 0x90) + 0x7C) = X;
*(FLOAT*)(*(QWORD*)(对象2 + 0x90) + 0x80) = Y;
*(FLOAT*)(*(QWORD*)(对象2 + 0x90) + 0x84) = Z;
}
void CTestDialog::OnBnClickedButton19()
{
UpdateData(TRUE);
Call_瞬移(m_edit3, m_edit4, m_edit5);
}
开始加速 和 瞬移 ,凌波微步
为了能让短距离瞬移变得有意义,短距离瞬移之后我们再走路,其他玩家会看到我们是瞬移成功的,
但是长距离瞬移确不行,说明服务器对我们的坐标是有验证的,这个容忍度我们上面说了220左右
不超过这个范围就不会被修正。
那么我们完全可以短距离瞬移,之后走路,让服务器承认我们的位置,然后再继续下次的瞬移,2次短距离瞬移的间隔如果很小,那么和长距离瞬移的结果几乎是一样的。
(如果我们连续短距离瞬移,中间不走路来让服务器确认我们的坐标有效性和直接长距离瞬移是没有区别的,还是会被修正回来的)
所以,我们要做一个短距离瞬移 + 寻路的功能,出来的效果看上去像凌波微步
但是还存在一个问题,就是,当我们瞬移的时候,不能去到地图无效点,否则还是会被修正
也就是说,我们选择的瞬移点位也必须是合理的
那么,怎么才能合理呢?
我们利用寻路,因为在寻路的时候,他会自动把人物转向到要寻路的方向,我们按照寻路的方向,小距离瞬移即可,即使到了地图边缘,那么下一次调用寻路,又会帮我们修正方向了
本着这个思路,我们需要做的事就是计算出来,我们面朝方向的一小段距离的坐标点,瞬移到这个坐标点。
这个距离我们用200,200在这个游戏不大,只有2小步那么长
那么计算方法如下:
第一象限
下图有一个错误,看看是否能够发现?(X增和Y增写反了,我们说了 这个坐标系纵轴是X)
if (A.朝向 > 0 && A.朝向 < 0x3FFF)
{
X增 = (FLOAT)(200 * sin((0x3FFF - A.朝向) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((0x3FFF - A.朝向) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X + X增, A.Y + Y增, A.Z);
}
第二象限 ,其他象限也是一样的,但是要注意,X增和Y增 不同象限可能是加也可能是减
if (A.朝向 > 0xFFFF * 3 / 4 && A.朝向 < 0xFFFF)
{
X增 = (FLOAT)(200 * sin((A.朝向 - 0xFFFF * 3 / 4) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((A.朝向 - 0xFFFF * 3 / 4) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X + X增, A.Y - Y增, A.Z);
}
第三象限
if (A.朝向 > 0xFFFF / 2 && A.朝向 < 0xFFFF * 3 / 4)
{
X增 = (FLOAT)(200 * sin((0xFFFF * 3 / 4 - A.朝向) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((0xFFFF * 3 / 4 - A.朝向) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X - X增, A.Y - Y增, A.Z);
}
第四象限
if (A.朝向 > 0x3FFF && A.朝向 < 0xFFFF / 2)
{
X增 = (FLOAT)(200 * sin((A.朝向 - 0x3FFF) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((A.朝向 - 0x3FFF) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X - X增, A.Y + Y增, A.Z);
}
上面的计算封装成子程序
void Call_向前瞬移200()
{
人物属性 A;
A.初始化();
FLOAT X增;
FLOAT Y增;
if (A.朝向 > 0 && A.朝向 < 0x3FFF)
{ //Q2217777779
X增 = (FLOAT)(200 * sin((0x3FFF - A.朝向) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((0x3FFF - A.朝向) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X + X增, A.Y + Y增, A.Z);
}
if (A.朝向 > 0xFFFF * 3 / 4 && A.朝向 < 0xFFFF)
{
X增 = (FLOAT)(200 * sin((A.朝向 - 0xFFFF * 3 / 4) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((A.朝向 - 0xFFFF * 3 / 4) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X + X增, A.Y - Y增, A.Z);
}
if (A.朝向 > 0xFFFF / 2 && A.朝向 < 0xFFFF * 3 / 4)
{
X增 = (FLOAT)(200 * sin((0xFFFF * 3 / 4 - A.朝向) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((0xFFFF * 3 / 4 - A.朝向) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X - X增, A.Y - Y增, A.Z);
}
if (A.朝向 > 0x3FFF && A.朝向 < 0xFFFF / 2)
{
X增 = (FLOAT)(200 * sin((A.朝向 - 0x3FFF) * 2 * 3.14159 / 0xFFFF));
Y增 = (FLOAT)(200 * cos((A.朝向 - 0x3FFF) * 2 * 3.14159 / 0xFFFF));
Call_瞬移(A.X - X增, A.Y + Y增, A.Z);
}
}
然后在我们的代码中调用即可
void Call_凌波微步(FLOAT X, FLOAT Y, FLOAT Z)
{
人物属性 A;
A.初始化();
int i = 0;
FLOAT 距离 = sqrt((A.X - X)*(A.X - X) + (A.Y - Y)*(A.Y - Y) + (A.Z - Z)*(A.Z - Z));
while (距离>50)
{
i++;
if (i > 100) break;
Call_向前瞬移200();
Sleep(80);
//公众号 任鸟飞逆向 2217777779
Call_寻路(X, Y, Z);
Sleep(120);
A.初始化();
距离 = sqrt((A.X - X)*(A.X - X) + (A.Y - Y)*(A.Y - Y) + (A.Z - Z)*(A.Z - Z));
}
}
在我们自己的屏幕上 是一步步瞬移过去的,但是在别的玩家屏幕看到的效果
我们是窗墙 ,飞檐走壁的
修复漏洞的建议 和方法
加速和瞬移 有点像开车, 服务器就像摄像头
虽然可以监督 和检查
但是确不能完全的实时检测,否则会严重的影响游戏体验度和流畅度
那么有什么办法,可以在服务器眼里并不是特别大的情况
又起到检测最好效果呢
我这里的建议是:
1.检测无需实时, 抽样调查,减少服务器压力, 而且每人又会不知什么情况下被检测
既起到了检测作用, 用起到了检测的隐藏作用
2.做单点检测 不可能完美, 做区间测速
排除一切 传送等因素的情况下, 不定时 做区间测速,这可以从根本上解决加速的问题,并且不需要实时检测
3.玩家举报
4.相关原理,过程代码的检测