zoukankan      html  css  js  c++  java
  • (转)一篇文章读懂守望先锋外挂的技术原理

    原文链接:https://zhuanlan.zhihu.com/p/346617073

    这篇文章会从一个程序员角度,去谈守望先锋的外挂技术问题。

    守望先锋外挂泛滥这个话题热度一直居高不下,到目前游戏环境日益变差,玩家们也是人心惶惶,杯弓蛇影。但凡在日常竞技中遇到操作离谱或者高于当前分段的准度,都觉得是外挂。

    关于本文作者背景以及声明

    我学生时代曾经自己动手做过一些单机游戏作弊工具,工作后在游戏公司工作过几年。业余也做过MMORPG包括一些对网络游戏外挂的兴趣研究,其中包括守望先锋的外挂。

    有部分B站UP也试图在解释这方面的内容,但是可能专业方向不同,部分解释模棱两可,没有技术背景支撑,甚至会误导人,尽管他们都是出于善意。因为本人不擅长也没有足够的时间进行视频制作,所以想通过文字的方式试图进行补充,求同存异。

    之前我在知乎谈过关于外挂的一些回答,也被知名媒体平台约稿发布过类似的文章。我的出发点是从技术上进行科普,并讨论为什么外挂无法在技术上被禁止以及外挂到底是怎么实现,帮助大家从一个不曾接触过的角度去了解外挂。

    所以,本文有以下声明:

    本文已简化一些技术专有名词,力求能让没有技术基础的普通玩家看懂,同时也尽可能保留专业性;

    本人所实现的外挂从未参与过竞技游戏或者取得过任意一场正式游戏的胜利,仅限于训练场、靶场、以及对战AI里测试过,进行过这些测试的账号也已经不再使用;

    本文会以大家熟悉的守望先锋为例,探讨外挂的技术实现,帮助大家更好地了解关于外挂这个东西,并且强烈不建议大家通过作弊方式取得游戏胜利;

    本文不会出现市面上任何外挂的具体名称;

    这篇文章也不做任何的道德审判,也不会讨论在 FPS 中作弊的道德问题;

    这类文章争议较大,所以我会对评论区中进行控评(仅针对恶意评论);

    该文章不允许任何形式的不署名转载,转载必须注明出处和原作者;

    外挂实现了什么功能

    这段时间我做了大量功课,包括了解市面比较流行的外挂,研究他们如何实现。首先我们在守望先锋中所见到的外挂,功能无非都是辅助瞄准(aimbot)、透视(maphack)两个主要功能。更进阶一些往细里说,也有大家听说过的包括自动近战、源氏自动刀、小美残血自动冰箱、狂鼠自瞄根据目标距离可以计算抛物线的下落点自动帮你抬高准星、甚至是猎空自动闪接近战、DVA自动吞敌方大招自动矩阵保队友避免致命伤害等辅助功能。

    相信大家听说过 “内存挂” 这样的名词。当然还有的玩家可能会说出 “脚本挂”、“封包挂” 这种其实听上去比较模棱两可的说法,不过没关系,我们会一块儿说。

    外挂如何实现

    守望先锋如今比较泛滥流行,功能比较强大的外挂都属于内存挂。

    我们在童年的时候可能都玩过单机游戏,不知道大家有没有使用过类似金山游侠、CE(Cheat Engine) 这类内存修改器。其实我们在单机游戏中对数值进行读取和修改,跟如今守望先锋中的外挂的实现方向是类似的。

    为什么修改内存就能对游戏造成影响

    游戏中所有东西,在内存里都会有所体现。其中最直观的就是数值,以及复杂一点点的函数的指针。其中单机游戏感观最明显,因为只要你修改了具体某个数值它就能直接生效,比如说生命值从 300 改为 3000,你马上就能发现自己变强了。

    为什么网游几乎都无法直接修改数值

    在守望先锋中,有可能把英雄(比如法老之鹰)的生命值 200 改为 10000 吗?

    目前来说不可能。因为我们平时在对战中,你的所有数值的比对,都是在和服务端进行数据交换。当然,你在游戏屏幕能够看到 200 这个数值,说明它肯定是存在于我们内存里的,但它的作用可能仅仅是展示。

    因为我们所有中枪受到伤害、被治疗时产生的生命值变更,其实都是在服务器实现的。服务器对你的生命值计算完了之后才通过网络通信把结果告诉你,而你的游戏客户端会把这个数值存到内存里,并渲染到画面上。

    以上是守望先锋的战斗通信时序图,并且大多数实时通信的网游也是类似的实现原理。包括爆头、伤害计算等关键部分都会放在服务端进行计算,已知:

    我们几乎不太可能入侵服务器,因为有内网隔离;

    服务器一般也不会放有游戏的源代码;

    即使有代码,你也不太可能成功编译;

    成功编译了,也不可能重启所有服务;

    所以平时说的入侵服务器基本上是十分困难且几乎无法实现的,这种基本上只存在于小说里的艺术手法。

    “脚本挂”和“封包挂”是什么

    其实这两种说法都是从技术实现的角度来区分。这两种说法都来源于普通玩家,脚本挂一般可以通俗地认为是鼠标宏、或者某些一直执行轻量的机械性重复指令的程序。比如说以前 DNF 的连发工具。

    而“封包挂”,其实是一个完全不同于脚本和内存的概念。

    在以前的某些 RPG 网游中存在,通常是用来做脱机外挂,这种外挂的制作成本不算低,它的前提是你必须要有能力解析客户端和服务端之间的所有通信协议,并且通过自己编程的方式,去实现一个能和服务端正常通信的客户端程序。

    在传统的 RPG 里,很多有技术资源的代练/打金工作室,会通过制作脱机外挂,在一台电脑上跑大量的账号,一条龙地完成各种任务。相当于实现了一个文字版的网游,因为只需要担任网络收发工作而不需要用到显卡资源,所以一台普通的家用电脑跑上百个甚至上千个账号也是很正常的。

    当然,早些时候也有吃鸡的多屏雷达地图透视,原理也是通过捕获吃鸡的通信协议内容并解析,通信协议里面会下发全地图的敌人位置,资源位置和类型,载具,毒圈范围等等。然后把这些信息绘制到某个网页中,并在局域网中的另外一台机器上打开浏览器访问这个网页雷达,以达到透视(雷达地图)的目的,一旦获得了上帝视角,肯定就能做出更多的预知行为。

    这也可以被称为是从网络通信上达到作弊目的的方式之一。这也是为什么这种外挂永远不可能被检测得到。

    但在守望先锋中,这类脱机外挂意义并不大,所以也不会有人去做。而且现代的游戏的服务端实现越来越严谨,校验特别多,这类外挂基本上没有用处了。尤其是脱机外挂,顶多帮忙完成一些自动化工作,收益不高。

    透视的技术原理

    接下来,会结合前面提到的网络通信的一些基础知识,跟大家谈谈我们所看到的“透视”是怎么实现的。

    FPS 是实时性非常强的游戏,我们知道每个玩家都需要靠服务器的同步才能完成游戏,更别说守望先锋这种节奏极快、角色都能上天入地的游戏。

    所以为了满足玩家看到人马上能开枪的需要,你的客户端一定得时刻知道所有角色的位置,也包括那些在墙体后、房间里你所看不见的玩家。

    试想,如果敌人在掩体后你看不见的时候,服务器就不告诉你,会带来什么后果呢?

    首先,服务端要时时刻刻计算所有角色的视野方向,视角的遮挡关系,服务端的运算量相当大。其次是,你再也没办法听到墙体后有人走动的脚步声。对于客户端来说,服务端不告诉你,墙后就是没人的。

    而当敌人快速从某个角落窜出来时,网络通信的延时来不及把信息马上同步给你,当你收到服务端告诉你有敌人出现时,如果对方的网络比你快,你可能已经被杀掉了,想想延迟怪是什么感受。

    所以为了满足游戏所需的实时性,服务端会在每时每刻都把所有角色的坐标信息告诉对局中每个玩家。因此,你的客户端其实是知道每个玩家的所在位置的,这些位置信息都会存在客户端的内存中,只是它们不一定会被显示出来。

    那么外挂如何做到让你能看到所有敌人的位置呢?

    外挂会通过读取守望先锋的内存数据,拿到所有角色的坐标、是什么英雄、当前的生命值、甚至大招进度(其实自己是看不见敌人的大招进度的,但不知道守望先锋出于什么考虑,也下发了敌人的大招进度,可以从内存里得知)等信息。

    总而言之,凡是在你游戏画面中有所体现的元素,一定也能在你的内存中读取得到。

    我们知道,外挂的透视,可以通过方框的形式标注所有玩家的位置。那么外挂如何让我们看到方框呢?我们看到的方框似乎就画在了你的游戏画面上,看起来就像是游戏本身的特性一样。

    其实,外挂只是在你的游戏窗口上再打开了一个透明的窗口,这个窗口来源于一个和守望先锋完全不同的进程,外挂会根据时时刻刻读取出来的角色坐标,通过不断绘图的方式在透明窗口上绘制出来。

    (PS: 有些外挂能够显示模型的轮廓,我更倾向是调用了内存中某个可以一直打开轮廓高亮显示的函数,类似回放模式中的轮廓常亮,而不是在覆盖层画出来的)

    这其中也涉及世界坐标(读出来的是3D坐标,因为守望先锋是个3D的世界,有 x, y, z 轴)转换为 2D坐标(屏幕坐标,把摄像机看到的三维坐标,以2D的方式映射到屏幕上)的问题,基本上有过游戏开发经验的程序员都会知道这种简单的换算方法。

    这就是为什么外挂通常都会要求你用窗口模式或者无边框窗口模式来运行游戏的原因。因为在 Windows 下,全屏窗口是不允许有顶层窗口的,如果是全屏模式,透明的覆盖窗口就无法覆盖在上面,绘制的方框你都看不到,就没办法获得透视效果了。

    自瞄的技术原理

    自瞄,很简单啊,就是帮你把鼠标移到敌人头上开枪。没错,的确是这样的,只是从技术机理上看,它中间其实还需要做一些魔法。

    我们知道了可以读取角色坐标,而对于辅助瞄准来说,它要做的事情就是在你开枪时捕获你的键盘消息,在你触发开枪按键的同时,帮助你把准星移动到胸部、头部、或者就近的部位。

    其实,内存中不仅仅能够读取到整个角色的坐标,同样可以读取到胸部、头部的位置,因为能够拿到完整的骨骼数据。所以这也是为什么外挂能够选择爆头或者不爆头。

    帮助你矫正准星的位置,就是外挂自瞄要做的工作。

    外挂通常有个 FOV 参数,做过测绘工作或者做过3D游戏的朋友就一定不会陌生,这其实本来是形容视野的夹角,而外挂的自瞄通过这个参数来决定你的准星在什么范围内自动帮你进行修正,数值越大就意味着,即使你的准星距离敌人还有半个屏幕远,它也会自动帮你修正。

    而还有一些让你看起来更像人类瞄准的参数,比如说X, Y轴的矫正平滑度等。平时大家口中说的“陀螺”,其实就是这些参数都归零之后,对敌对角色进行超大视野瞄准开枪,快速矫正的结果。所以这种其实不是子弹跟踪,我想纯粹是因为转得太快,画面还来不及刷新,所以看到的陀螺外挂的回放视角时,这些外挂通常是对着地板开枪,但开枪的瞬间会闪烁一下的原因。

    说完了自瞄的机理,我们再来说说鼠标的控制。

    我们在玩游戏的时候,肯定会对鼠标进行控制。其实外挂也一样,它只要计算好了准星的落点,也会接管你的鼠标,帮你进行瞄准修正。但是守望先锋中的外挂,不一定是在驱动层模拟你的鼠标事件进行控制,它还实现了内存瞄准。换句话说就是通过内存 call 的方式,直接调用守望先锋里已有的方法进行瞄准。当然,两种方式只是技术实现不一样,结果还是一样的。

    所以平时我们说的“微自瞄”,其实就是带有修正的瞄准,往往是你准星越接近目标,命中率越高,对于已经有不错的瞄准基础的人来说,这种小幅度修正更加难被察觉到。也就是你开枪的数十毫秒,根据平滑度帮你尽可能向目标挪动,所以根据挪动距离和平滑度(速度)的不一样,也可能不会命中目标。

    而正是因为这种机制,如果你是从右往左快速挪动准星,而准星刚好挪动到了目标的左侧,而位于左侧的范围正好又是能够触发辅助瞄准的范围,那么辅助瞄准就会开始工作,再将你的准星往右拉回到目标的头部。这一操作被戏称为“三角形拉枪”,这种细微的调整操作人类一般是不会有的,因为人类从反应能力到控制手部进行操作的过程,都远远高于150毫秒,所以只有机器瞄准才会出现这种微妙的曲线修正。

    当然你说你手抖不小心打的,那也说得过去,但是手抖只会偶尔一次,不会经常出现。如果你一直手抖,那么建议你去看看是否患上了帕金森。

    古老的血条外挂

    其实早在 2016 年的时候,守望先锋的外挂并没有那么泛滥,也没有目前那么高级(也可能是已经有高级的了,但没有这么大范围的传播)。

    那时候守望先锋存在的外挂是一种叫做 “血条挂” 的东西。

    我们知道守望先锋在击中敌人的时候,敌人头顶会出现一个鲜红色的血条。而那时候的血条挂,并不需要绕过反作弊,也不需要读取内存,它的原理也十分简单:写程序通过对游戏画面进行不断地截图,并且找到鲜红色(固定的颜色)的血条位置,再通过一个固定的偏移量,取得血条下方的头部或者躯体的位置,再通过移动鼠标的方式去实现自瞄功能。

    这种方法简单粗暴,也是因为守望先锋本身的设计问题(击中时出现固定颜色的血条)才会被利用。当然,这种外挂的缺陷就是,你必须要打中敌人第一枪,才会有自瞄效果。

    (PS: 2016 年的时候,我得知了这种外挂的原理,抱着好玩的心态,通过朋友提供的部分开源代码,只花了半天时间就自己实现了一个简单的版本,当时还录制了两个视频传到 B 站,但审核认为争议较大不予通过,最后不了了之。)

    后来守望先锋加入了一些简单的反作弊机制,可以成功检测截图以及是否有外部的鼠标输入事件。那时候,技术比较菜的血条外挂都死于非命,而且也只有以前的老开挂用户会知道,守望先锋是有一个「黑号模式」的,当它检测到你疑似开挂的时候,你就会进入一个奇特的模式,首先是你的QQ、以及其它的普通应用,没办法再对守望先锋进行截屏了,截出来的是黑屏;其次,你看到的敌人的血条,颜色值也不再是原来的值了,而是更加深一点。但这一切,对于普通玩家而言是毫无区别的,但血条外挂却不能用了。当然,后来血条外挂也有一套方法对抗这种模式,在这里我就不细说了。

    不过通过这一点也说明,暴雪其实还是有在默默努力的,但奈何无法从根本上解决问题。

    后来,血条挂也出现了一些骚技巧,比如说我不直接截图守望先锋,但可以通过 OBS(最流行的直播软件),用 OBS 假装自己在直播,然后再从 OBS 中拿到游戏画面,再去找血条位置。这么一来,反作弊机制根本就发现不了,毕竟它可以不允许你截图,但是它不能让别人都不直播。

    截图问题解决了,核心问题就回到了鼠标的控制问题。因为血条外挂不入侵游戏进程,所以也不存在内存自瞄这么一说。这也促进了外挂进一步升级,但也大大提高了外挂的制作难度。那就是不再简单地使用系统提供的鼠标控制函数,而是在驱动层来进行鼠标控制。

    在更高权限下做文章,对守望先锋来说是远远超纲了,毕竟他还只是个在应用层的弟弟。

    因此,江湖上流传的 U 盘外挂,其实很可能就是一个 USB 设备,而这个设备其实假装自己是个鼠标,是真真正正的硬件,只不过这个硬件,还夹带了一些私货罢了。甚至你可以去购买一个价格仅为十几二十块的键鼠模拟USB芯片,加上一些开发基础,就可以轻易做到这件事。

    我只不过是多插了一个“鼠标”,跟你守望先锋又有什么关系呢?(战术后仰)

    炸房外挂技术原理

    炸房外挂其实很少遇到,但这个东西是存在的。炸房的原理也是简单粗暴,实际上就是用大量的数据,对目标服务器进行攻击(DDoS),让网络堵塞。

    所以炸房,其实是一套七伤拳,因为不存在说炸房的人自己没事,而别人有事。

    以前守望先锋炸房通常出现在一些关键对局中,比如说有一方方要输掉比赛了,企图通过炸房的方式造成网络堵塞,让玩家无法忍受而主动退出游戏。如果玩家一直不退,也会让服务端认为有未知的内部异常,触发了某种容错机制,从而强制关闭当前的对局,并且不进行分数的结算。

    这种容错机制不仅仅出现在对局拥堵的情况下,以往有一些地图越界BUG,比如说角色到了一个神奇的地方,也会触发这样的容错机制导致对局无效。

    但如果有时候遇到服务器非常卡,所有人都出现了网络延迟,一定是有人炸房吗?

    不一定。

    比如说服务器机房的网络不稳定,通信链路的故障,或者服务端软件层面的 BUG, 都可能导致类似的问题。

    如果 ping 值较高,则代表是你的网络到守望先锋接入层网络服务器之间的通信延迟高。

    但如果 ping 值正常,但游戏中大家互相还是看到彼此瞬移,丢包,技能不能正常发出,说明是到接入层的网络正常,可能是守望先锋的内网服务器的软件层面出问题或者被炸房了。

    而且需要提一点就是,如果你的对局中所有玩家都很卡,假设不是服务端软件的 BUG, 而是有人在炸房,其实也不一定炸的是你的对局。

    在游戏服务端架构里,一台物理层面上的服务器其实是可能会同时承担多个守望先锋对局的运算的。比如说英雄联盟如此,守望先锋大致上也是如此。

    举个例子:如果 A 对局在花村,B 对局在国王大道,而恰好 A, B 都分配在同一台服务器上进行计算。然后 A 对局里有个弟弟留榜渡劫局打不过了,于是打开了D盘学习资料中的神秘程序开始了炸房。炸房其实就是对某服务器进行大量的垃圾数据攻击,所以就会导致身在 B 对局中的玩家也十分无辜受到了影响。

    有人会说,网易为什么连 DDoS 都防不住。其实这个问题很难说,我想机房多多少少其实是有防护的,但既然是服务器,就一定不会拒绝合法的数据来源,只要数据合法地穿过了接入层,到了内网的守望先锋服务端,而服务端处理数据包是有一定前提的,就是一定会对数据包进行校验,如果不合法的就会丢弃。而恰好是校验的这部分计算,可能就消耗了大量的 CPU 资源,而导致合法的请求得不到及时处理,堆积在队列中,造成了对局中所有操作都变得响应迟钝。

    由于守望先锋服务端的一些自我容错机制,一旦服务端发生了大规模的响应异常,就会强制终止当前对局,不进行分数结算。

    当然,守望先锋中炸房挂其实基本上几年都见不到一次,加上现在外挂已经十分高级,演员行为成本又低又稳。打不过的直接资本操作一下,或者开个外挂赶紧下一局,远比高成本的炸房来得容易得多。

    关于反作弊

    有时候我们可能会想,既然外挂如此猖獗,那为何屡禁不止呢?

    反作弊技术原理

    如果你玩过鹅厂的PC端游,就会发现每次启动游戏之前,都会弹出一个 TenProtect(简称 TP) 的加载界面。其实这是一个高权限的游戏保护进程,它属于一种外部的保护程序,它跟游戏本身没关系,却像一个保镖一样保护着游戏进程的安全,它的定位跟你电脑上的安全软件有点类似。

    我们可以把游戏进程,以及你平时打开的普通的所有应用程序都视为小弟,你只能做自己的事情,但没办法干涉别的小弟。

    而TP就是大哥大,它可以随便杀了小弟,也能保护小弟不会被别的小弟杀掉,甚至别的小弟要看你的小弟的隐私,大哥大都可以帮你拒绝。

    这个例子可能不算十分严谨,但这里我们只需要理解程序之间有不同权限即可。

    腾讯正因为有 TP 的存在,才让它的游戏的基础安全得到了保障。首先,你没办法直接拿到游戏窗口的句柄和进程 ID 了,就没办法进行内存读写了,首先在这个基本层面上,只会用简单调试器的基础玩家就没办法进行作弊。更何况,游戏保护还会对基础的鼠标键盘有关的函数进行接管和检测,你在应用层用已知的脚本工具进行键鼠模拟,也可能是会出事的。

    当然,传说中 TP 可能会对你计算机上的其它进程或者硬盘上的文件进行扫描,保证你是在一个理想的安全环境下进行游戏的。

    所以相对而言,腾讯的游戏目前是最安全的,而 TP 也是目前世界上最优秀的反作弊程序之一,毕竟经过了国内这么多年和这么大体量的用户洗礼。

    说到这里大家可能就明白,暴雪的游戏正是因为没有类似的保护进程,才导致其本身没有很好的被大哥大保护,外挂的开发也就相对来说顺利一点,也给后期的反作弊工作带来了很大的运营难度。

    那守望先锋到底有没有反外挂机制呢?

    有的,不过都是小弟级别的反外挂,只在一些级别的反调试和键鼠消息检测上做了点工作,而对于外挂的高权限还是束手无策。

    这也是为什么守望先锋没办法很好地在技术层面检测得到外挂的原因。

    目前市面上流行的内存挂,有的用 Intel 硬件虚拟化,有的用签名驱动的方法,但无一例外,这些外挂基本上都是大哥大级别,守望先锋完全无法在技术上得知他们的存在,而他们却可以很轻易地对守望先锋的内存进行读取,甚至是对守望先锋的进行函数调用。

    守望先锋能做到的,只有在游戏内通过人工手段来鉴别外挂。而偏偏这些外挂制作精良,并且也花了极大的心思去做 Human-Like (仿人类)。

    高水平的玩家进行作弊,往往还会和正常的游戏操作混在一起,有的玩家甚至只使用透视而不使用辅助瞄准,给人工鉴别带来了极大压力和难度,不得不说长此以往一定是个巨大的挑战。

    游戏机制硬伤

    在这里我们再谈谈游戏机制的问题。

    因为守望先锋这类 FPS 游戏(也包括但不限于绝地求生、APEX 等同类型游戏),他们本身就没办法很好地杜绝外挂。

    我们上面提到过,FPS 的实时性极高,必须要无时无刻向所有玩家的客户端下发坐标等信息,因此透视无法避免。知道了角色的坐标,自瞄也无法避免,而偏偏瞄准这个操作,对于 FPS 而言是非常核心的操作,带来的收益非常高,偏偏因为游戏的技术机制问题,没办法很好地避免。

    FPS 不像我们熟知的其它类型的游戏。

    比如说像梦幻西游、剑3这些 RPG 网游,他们几乎所有的数值都在服务端储存,所有的交互行为都通过服务端来进行计算,而你客户端得到的只是个冷冰冰的数字。

    而这些游戏的外挂可以得到什么呢?

    如果是内存外挂,能够做到挂机完成某些机械性的行为,自动帮你完成任务,自动打怪等,但是这些行为的收益都不算核心收益,顶多是分担了玩家的体力,远远触及不到核心。

    后话

    这篇文章花了整整一个通宵,虽然文字不算多,但算是从我的专业角度和经验上做了个抛砖引玉。大多数玩家可能对技术不感兴趣,不过本文还是希望在科普的同时,也能让所有人提升科学意识,提高思想觉悟。

    外挂对于玩家来说一直是个沉重的话题,不愿提起,也不愿遇到。

    FPS 本身是通过射击获得快感的游戏,一旦这部分快感很轻易就得到满足,这个潘多拉的盒子就会打开。在无法被满足公平环境的同时,有越来越多的人为了能在喜爱的游戏中获得较好的体验,也会纷纷去效仿这种行为。

    诚然,人与人之间无法要求彼此高尚。但如果你心有热爱,我愿意和你们一起去了解它,守护它,并且在遇到这种不良秩序时,能大方站出来声讨、抗议以及抵制。

    我希望,人人都是守望先锋。

  • 相关阅读:
    2016 Multi-University Training Contest 1 solutions BY HIT
    Unicode 码表
    用 lambda 表达式 对 List 进行排序
    Linux的sed命令介绍
    Linux下的NTP服务搭建
    Linux网络配置(ip命令及配置文件)
    Linux的bash脚本编程(if语句和循环语句)
    Linux新手必须掌握的命令(2)
    Linux的文件查找
    bash中的变量
  • 原文地址:https://www.cnblogs.com/zhwer/p/14343349.html
Copyright © 2011-2022 走看看