共计字数:2.2k 预计阅读用时:7 分钟

如何保护你的 Roblox 游戏公平/安全

注:文章仓促完成,如果有误请不吝指正。

众所周知,游戏都有可能存在漏洞,而作弊者常常出现,他们破坏游戏规则,干扰玩家体验,甚至窃取游戏代码。至此在游戏有一定社区规模后,我们就不得不考虑游戏安全和公平问题了。借由亲身体验过游戏执行器(Executor)(即可以从外部向 Roblox Player 内执行完整的核心脚本(Core Script)的等级 Lua 代码的工具)的人。我向大家介绍 Roblox 的游戏作弊社区,以及如何尽可能的保护游戏和公平。

什么是执行器(Executor)?

执行器(俗称作弊器)是一种通过访问 Roblox 进程,获取 Lua 句柄,并从外部向内执行任何完整 Lua 代码的工具,目前较为出名的执行器有 Synapse-X,Protosmasher 等,被注入的脚本通常拥有非常高的权限(等级同等 CoreScript)以及非常丰富的脚本模块,以至于脚本很难被侦测到。
但因为其执行器的特殊性,大部分黑客用其制作游戏作弊脚本。例如 ESP(玩家透视),TP(传送),Farm(自动刷取游戏币)等。

执行器的等级

在 Roblox 的黑客社区里,这类执行器的执行能力通常被划分为 7 个等级,通常代表了不同的脚本环境,1 - 2 级为 LocalScript 环境,3 - 4 级为 Roblox 核心脚本环境,5 级为 Roblox Studio 命令栏环境,而 6 级为的 Roblox Studio 的插件环境,最后一个 7 级等级为 Roblox 服务器脚本等级。(因为 2016 年 Roblox 推出的过滤启用功能,现已经没有这个等级的执行器了)(笔者入坑时,正值当时的 7 级执行器 RC7 泛滥的时候)

什么是混淆器(Obfuscator)?

混淆器和压缩器(Minifier)虽然看似相同,但是目的不一致。压缩器的目的是用于减少原代码体积,而混淆器是用于使代码转换成让普通程序员无法直接解读的形式:

一段简单的代码:

被混淆后变成了这样:

这类代码通常解读难度巨大,一般情况下很难判断代码内在执行什么,也正因为如此,部分黑客通过将脚本混淆,放在一些工具箱(ToolBox)的模型内,进而影响到玩家的正常游戏(例如被莫名其妙的跳转,要求购买通行证,甚至是要求输入账户密码)。更有甚者,在开发者的插件内植入混淆代码,从而通过 HttpService 直接从开发者处获取完整的原代码(历史上 木材大亨2(Lumber Tycoon 2) 幻影军队(Phantom Forces) 均因此被窃取原代码并在黑客论坛内流通)
目前已知的混淆器有闭源的 Synapse-Xen 和开源的 IronBrew,Github 也有不同种类的可以自行查询。

如何保证游戏公平?

虽然我们不一定能做到绝对无法破解(例如 ESP,Synapse-X 可以在 CoreGui 内输出透视结果,而局部脚本和服务器脚本均无法检测核心页面的变化),但如果能把作弊降低至辅助等级,我们的公平就能得到极大的保证了。

开启过滤启用(或关闭实验模式)

2016 年,Roblox 推出了过滤启用(Workspace.FilteringEnabled),使得客户端的绝大部分数据不会复制到服务器内,大大减少了执行器的涉及范围。如果你的游戏没有开启该设置,启用将可以抵制绝大部分的低等级执行器。

重要的事件放在服务器处理

拿 FPS 游戏举例(Phantom Forces),我们通常会使用枪械射击,这通常会需要触发事件,但某些游戏开发者会先检测是否击中玩家,然后向服务器发送该玩家的信息,这就给作弊可乘之机了,只要遍历玩家列表,逐个循环触发,就能做到全员击杀(没准队友也被杀了呢)如果手段高明一些,我们可以发送枪械的发射方向(Ray)然后让服务器计算弹道是否击中,虽然可能有些简陋,但是我们的确增加了作弊脚本的编写难度。

拿 Obby 游戏举例(举例 The Tower of Hell),玩家想瞬移到终点直接胜利,而如果玩家的移动速度是恒定不变的,我们便可以检测玩家的水平速度(垂直可能存在玩家掉落的情况),如果会超出,可能就是在作弊了。

拿 Tycoon 游戏举例(Lumber Tycoon 2),作弊者通过触发读档事件复制游戏物品,作者将保存和读取分开,而不是合并成一个处理,游戏中不得保存当前存档槽到别的存档槽上,但是借助作弊脚本即可复制存档,如果可以进行合并,也许可以解决复制方面的问题。

其他游戏可能不会出现上述情况,但是要领一定是尽可能减少客户端与服务器的交互,或者合并与客户端的访问都可以。

不用的游戏实例放在 ServerStorage 内

不光是事件,一些游戏模型如果可以就将其存储在 ServerStorage 内,绝大多数的执行器权限不足以访问 ServerStorage,所以这也可以确保游戏模型不被一下子全部窃取。

使用 ToolBox 的模型需要再三检查

正如上文说到,ToolBox 的部分模型被插入恶意代码,虽然一下子可能会让你束手无策,但是如果我们只需要一个纯模型(不需要脚本添加行为的)的话,完全可以把下下来的模型包含的脚本删除。这样就能避免恶意脚本影响玩家游玩了。

尽量不要开启或限制插件访问 HttpService

前面举了两个游戏被窃取原代码的例子,都是因为安装了留有后门的插件造成的,因此我们一定要选择可以信任的插件(最好是从开发者论坛内寻找),如果担心插件不安全,可以访问 Roblox 的安装目录下的 你的用户ID 文件夹下的 InstalledPlugins 找到插件对应的模型文件(rbxm)进行检查。

减少局部脚本的数量或者也进行混淆

我们无法确保客户端脚本(LocalScript)的完整安全,毕竟数据已经在客户端上,只要有时间都可以破除或者反编译。但是如果混淆器能正确使用,也可以保护我们的脚本不被直接解读。可以尝试制作插件,发布前将脚本进行混淆,这样能大大增加逆向工程的难度,这样即使被反编译,也需要大量的精力去逆向工程。

在表内使用元表进行访问检测

如果你的游戏价值已经大到作弊脚本已经要访问你的脚本来篡改数据的话,那么在需要的变量(多为 table)内使用元表进行检测访问便是必不可少的了。通过 __index__newindex 抓取数据被更改的情况,使用 getfenv 获取脚本环境是否是脚本自身,如果不一致那么就是作弊脚本正在访问了。
简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local scriptEnv = getfenv()
local meta = {
__index = function(t, k)
for i = 0, 10 do
if getfenv(i) ~= scriptEnv then
warn("检测到作弊脚本读取数据!") -- 做你想做的,踢出玩家也可以
return
end
end
return t[k]
end,
__newindex = function(t, k, v)
for i = 0, 10 do
if getfenv(i) ~= scriptEnv then
warn("检测到作弊脚本写入数据!") -- 做你想做的,踢出玩家也可以
return
end
end
t[k] = v
end
}
local data = setmetatable({ a = 0 }, meta)
data.a = data.a + 1

在你能确认的情况下永久封禁用户

如果能确信用户的操作属于作弊,那么可以在云端数据上记录该玩家并阻止玩家进入游戏(进入则踢出),这也是一个简单粗暴的方式,而且在国服 Roblox 内,小号的创建难度远高于国际服,所以能加大脚本作者编写作弊脚本的难度。

结尾

我们不能完全阻止玩家做出不公平的事情,道高一尺魔高一丈,即便是像微软苹果这样的公司也会有泄露商业机密的问题。我们能做的就是尽可能增加难度,让作弊成本大于游戏价值

最后祝大家做出自己梦想的游戏,Powering Imagination