一、代码加固
为提高微信小游戏代码的安全性,平台针对小游戏游戏场景提供了定制化的深度保护功能,可实现对小游戏前端代码混淆,以防止代码暴露,提高攻击者阅读、二次开发成本,一定程度上也能提高黑产制作外挂的成本;还为代码增加了水印能力,防止核心代码被搬运或二次上线到其它平台。
“游戏深度保护” 插件只需在微信开发者工具中直接进行安装即可,操作便捷,无需额外耗时。建议涉及小游戏中的核心算法逻辑、版权内容等需要安全保护的内容,都可以进行深度保护。本插件由小游戏平台提供,不会泄露原代码。开发者也应注意自身开发规范的严谨性,保证游戏的代码质量,保护游戏用户账号安全。
以下是插件各项能力以及使用介绍。
1.1 代码混淆
代码混淆是利用抽象语法树(AST)解析,对代码进行功能等价的转换。主要做两件事情:名字擦除、结构打乱。
名字擦除是指将代码中的变量名、函数名、类名进行重命名,将属性名、常量值等隐藏起来。经过名字擦除的代码,搜索不到原代码中的名字。可以有效地规避攻击者做关键字搜索,定位代码逻辑。
结构打乱是指将代码的控制流等价转换,比如对象定义、类的定义、以及块内的代码,进行打乱重排。经过结构打乱后的代码,很难分析它的执行流程,可以有效地防止攻击者读懂代码或预测逻辑。
以下分别是示例代码和经过本插件混淆后的代码。可以看到,原代码中的变量、属性、字面量等信息皆被擦除或隐藏,原代码结构也被打乱,很难分析执行顺序。


1.2 代码水印
代码水印是在代码混淆时,放置到代码中一段隐蔽且唯一的信息。它具备以下特点:
- 隐蔽性:水印本身也是代码,放在混淆代码中,参与运行,具备比较强的隐蔽性。
- 等价性:水印不会影响原代码逻辑。
- 唯一性:水印是根据微信小游戏的身份、加水印时间等信息经过加密生成的,是唯一的。
水印在一定程度上可以证明开发者与游戏的关联关系,从而用于辅助判断代码与开发者的归属关系。当开发者怀疑加固的代码被搬运到其它平台时,开发者可以寻求平台帮助提取水印以用于辅助判断。
1.3 体积&性能
根据大盘已有加固案例:平均体积膨胀在 30% 以内。如果原代码本身比较小,膨胀比率可能略大一些。原代码比较大时(>1M),膨胀比例会接近于前述值;性能、cpu、内存几乎不变。
如果原代码已经是压缩甚至是混淆过的代码,那么膨胀率可能会比较高。建议开发者直接使用源代码进行加固。
1.4 如何使用
申请白名单,下载并安装 1.06.2401020 或以上版本的开发者工具、安装 “游戏深度保护” 插件即可使用。详细说明见:游戏深度保护 | 微信开放社区(文章置顶评论有申请白名单方式)。
二、内容安全升级开放
基于游戏领域海量数据积累和多年行业经验,研发游戏垂类的智能模型和定制策略方案,高效过滤游戏领域常见的谩骂、低俗、营销广告等内容及各种文字对抗变种。
2.1 单日限额提升
当前很多小游戏单日UGC文本数已经超过接口msgSecCheck的200w/day的限额,特别是小游戏周末高峰做活动期间UGC量级可能突增好几倍,导致小游戏的晚高峰UGC内容无法过内容安全审核,存在非常大的安全风险。
因此平台单独提供一个扩大限额(1000w/day)的新接口供超额的小游戏开发者使用,新接口和老接口(小程序和小游戏共用的接口msgSecCheck)所有协议完全一样,开发者使用切换时只需要更改调用链接即可,切换后不用再请求老接口。同时新接口还有三个升级优化点:
- 新接口平均耗时降低300ms,提升聊天互动场景的用户体验
- 取消”用户未在近两小时访问小游戏”的请求限制
- 取消”不合法的openid”用户请求限制,也就是非微信端用户的UGC内容也可以访问
- 为了防止刷量和攻击,新接口使用需要开发者联系小助手进行申请。
- 目前开放了文本接口,图片新接口预计2024年内开放。 (取消限频,取消用户限制,增加低俗软色情能力)。
小游戏文本内容安全新接口:https://api.weixin.qq.com/wxa/game/content_spam/msg_sec_check?access_token=ACCESS_TOKEN
仅url路径不同,其他输入输出参数可参考老接口msgSecCheck文档
2.2 游戏垂类专属
除了红线类(敏感、色情、违法等)内容的基础识别能力模型之外,游戏业务目前更多更常见黄线类的辱骂攻击,低俗挑逗,灰产广告引流三大类。特别是在游戏公屏群聊,私聊互动,社区帖子评论场景,昵称签名等场景下会存在大量谩骂、低俗内容,这些都非常容易损害正常用户体验和小游戏的口碑,还会存在玩家投诉和监管的压力。此外在小游戏中营销广告引流内容批量刷屏时,也会损害小游戏开发者利益。
基于多年游戏行业的数据经验积累,基于大数据训练更适合游戏的文本模型和定制安全策略,目前具备以下内容识别能力:
- 辱骂攻击,低俗挑逗,软色情文本
- 变种辱骂低俗文本:包括同音、谐音、同义词、emoji、拼音、缩写等组合对抗文本
- 群聊和评论场景拆字叠楼对抗文本 (非实时)
- 灰黑产广告引流内容
- 文本语种类型识别
- 文本无意义内容
同时针对黑灰产广告引流内容,每天还会从发表内容,发表频次,发表行为等多维度发现可疑内容,并经过人工审核确认后加词和加黑种子快速与黑灰产对抗。此外还会根据日常发现的灰产引流样本来定期更新文本营销引流模型,持续拦截新的灰黑产引流变种内容。
- 上述游戏垂类的黄线类内容识别能力已在老接口msgSecCheck上线生效,默认生效辱骂、低俗、广告引流。
2.3 策略可定制化
当前小游戏种类,产品形态,数量都非常多样性,因此通用的内容识别能力和模型保障在小游戏整体数据上的效果。针对一些特殊的、个性化的小游戏内容安全需求,可以切换小游戏内容安全新接口的方式接入,在黄线类内容的识别能力、策略上可以进行定制化调整和运营。
例如,某小游戏开发者可能想要将一些无意义、灌水刷经验的评论内容进行沉底,避免影响小游戏内社区的UGC内容质量,这时候就可申请配置”无意义文本模型”。或者开发者不想要管控一些日常的广告内容(可能存在一些活动广告),可以申请取消”文本营销引流”相关模型策略。更多定制需求可以联系小助手。
- 需要切换新接口才能使用策略定制化能力,并且可定制范围只限于黄线内容。
三、小游戏对抗外挂实践
3.1 认识小游戏外挂
不管是app游戏,还是微信小游戏,都很容易面临外挂的攻击,外挂对游戏造成的影响主要有:
- 用户投诉
- 影响游戏公平性(如排行榜)
- 游戏正常收益受损
就目前而言,小游戏外挂可以分为两类,协议挂和内存挂,本文将分别介绍这两类外挂,以及一些对抗方式。
3.1.1 协议挂
黑产利用小游戏客户端JS代码泄露+中间人攻击抓包的方式,获得小游戏与开发者后台之间通信的所有细节,从而实现篡改游戏远端存储数据或者实现脱机的一种外挂,常用手法:
- 反编译获取小游戏源代码逆向
- 中间人攻击抓包分析参数
部分偏离线玩法的小游戏提供了存档功能,由于代码未混淆,且部分游戏后台未对用户数据做校验,游戏内的存档数据可以被任意修改,并重新签名覆盖,如下图,游戏的存档数据为一串json字符串,通过逆向游戏代码,提取出加密和解密函数,可以任意修改游戏数据后覆盖存档。

3.1.2 内存挂
黑产利用内存搜索工具,定位游戏内存数值并直接修改的一种外挂,常用工具:
- PC端:CheatEngine
- 安卓端:GG修改器
以飞机大战举例,修改游戏内数值:

3.2 外挂对抗
3.2.1 协议挂对抗实践
协议挂能够成立有两个前提:
- 攻击者反编译获取小游戏代码逆向逻辑
- 攻击者利用中间人攻击抓包分析游戏与后台的协议
其中反编译获取小游戏代码逻辑,可以使用第一章中的 游戏深度保护插件 来对抗。针对抓包,则有三种方案可以进行对抗,对抗思路如图:

3.2.1.1 HTTPS加密网络通道
示例代码如下,开发者可以选择在游戏关键逻辑处,比如用户数据存档,游戏内活动等重要功能时对传输的数据加密,避免被攻击者直接阅读明文窃取重要信息。
const somedata = 'xxxxx'
const userCryptoManager = wx.getUserCryptoManager()
userCryptoManager.getLatestUserKey({
success({encryptKey, iv, version, expireTime}) {
const encryptedData = someAESEncryptMethod(encryptKey, iv, somedata)
wx.request({
data: encryptedData,
success(res) {
const decryptedData = someAESDEcryptMethod(encryptKey, iv, res.data)
console.log(decryptedData)
}
})
}
})
在开发者后台则通过后台接口来获取解密的密钥。
curl -X POST "https://api.weixin.qq.com/wxa/business/getuserencryptkey?access_token=ACCESS_TOKEN&openid=OPENID&signature=SIGNATURE&sig_method=hmac_sha256"
3.2.1.2 TCP协议
如果有进一步的要求,可直接在TCP层面实现,参考TCPSocket | 微信开放文档
3.2.1.3 Donuts网关方案
Donuts 提供了微信私有链路服务,可用于提升小游戏关键网络请求的安全性。
const gateway = wx.cloud.services.Gateway({
// 接入域名,可以在网关控制台中获取
domain: 'xxxxxxx.sh.wxcloudrun.com',
})
// 赋值到 cloud 对象上,方便后续调用
wx.cloud.gateway = gateway
wx.cloud.gateway.call({
// 去除域名后的接口路径,中文需要转译
path: `/api/save`,
// 请求方法
method: 'POST',
// 数据
data: 'data'
}).then(result => {
console.log('微信网关访问结果:', result)
})
3.2.2 内存挂对抗
内存挂本质上是一些数值被外部修改器给修改了,为什么会被修改呢?因为在v8引擎中,整数的数值是通过SMI结构来存储的,其最后一位数值是用来作为标记的,标识是否为堆栈对象或者Smi。因此,游戏的内存数值与游戏展示出来的数值存在两倍的关系,即:内存内数值 = 游戏内数值*2,不少外部经验贴也是这么写的:

如图,某游戏内的数值变化代码如下,对象里的数值可以直接被内存搜索修改

3.2.2.1 数值篡改感知
基于此,黑产制作了多款如秒杀、加血、加伤害等的外挂,其本质都是搜索关键数值,并直接修改,但是黑产的外挂工具并不能实现精确定位,毕竟大部分外挂使用者不懂得如何通过一步步的操作来筛选无用的内存值。因此黑产工具有一个特点,就是只搜一个或多个关键数值,然后一次性全部修改。
因此开发者可以考虑,针对关键的数值做双写校验逻辑,即同时写值和写入校验(如hash值)。然后定时、或者在关键业务逻辑处校验:一旦数值与校验值不匹配,则可以认为游戏被外挂攻击了,做进一步动作。
/////////////////////////////// 感知篡改需要用到的 hash 函数和 检查函数
function MyHash(val){
return val + 10;
}
function CheckCoin(obj){
if(obj.coin_hash !== MyHash(obj.coin)){
console.info("检测到外挂")
// 继续做其它事:退出游戏,或者上报等
}
}
///////////////////////////// 以下是原业务逻辑代码区
let obj = {}
// 写关键数值时, 也同步写一个校验值
obj.coin = 10;
obj.coin_hash = MyHash(obj.coin) // 写校验值
// 感知内存篡改方式1,定时每五秒检查一次
setInterval(CheckCoin, 5000, obj)
// 感知内存篡改方式2,在关键业务逻辑处检查
function uploadCoin(obj){
// 在上传云端金币时,先检查一次
CheckCoin(obj);
// 校查无问题,再做剩余逻辑
}

3.2.2.2 数值隐藏
Proxy 是 es6 引入的特性,其关键性质是,对一个代理对象上的属性读取或写入的动作,都可以被拦截转发到自定义逻辑。借此可以实现数据在内存中的隐藏, 从而隔断搜索,我们来看一个最简单的模型,即一个对象 obj,和对应的金币属性coin,示例代码如下:
let obj = {}
let p_obj = new Proxy(obj, {
get(target, key, receiver) {
// 如果为数值,则返回减7
if (typeof target[key] === 'number') {
return target[key] - 7
}
return Reflect.get(target, key ,receiver)
},
set(target, key, value, receiver) {
// 如果为数值,则写入加7
if (typeof value === 'number') {
value += 7
}
return Reflect.set(target, key, value, receiver)
}
});
// 后续所有的操作都针对代理对象
p_obj.coin = 10
// 不影响原有的逻辑
console.assert(p_obj.coin === 10, 'p_obj.coin === 10')
通过给原有的对象套上一层代理,从而实现游戏运行时,展示在界面上的数值,与实际存储在内存中的数值是不一样的。这就达到了阻断搜索的目标。注意 Proxy 是 es6 的特性,如果要兼容 es5,那么需要用 defineProperty 来等价实现,或者使用 es6 转 es5 工具支持。

3.3 其他需要注意的安全问题
3.3.1 游戏内禁止内置GM逻辑
游戏正式版发布的代码后禁止含有gm开关等逻辑,黑产可修改代码并重新打包替换,如下图是某游戏代码内置了gm的测试逻辑,导致被黑产利用:

3.3.2 保护游戏的自定义登录态
- 重复使用wx.login code且未校验绑定关系
小游戏开发者应正确地使用 code,指引。如下图是某游戏存在的问题:
- 对外暴露了一个接口,可重复使用wx.login的code
- 开发者后台未实际校验code与openid的对应关系
开发者第一次使用wx.login获取到的code登录,获得用户的openid,第二次重复使用code+openid查询用户自定义登录态
但实际测试发现code可固定为黑产用户A的,同时可结合任意用户B的openid查询到用户B的自定义登录态(猜测开发者后台并未校验code和openid的绑定关系)
黑产可借此制作协议挂,实现修改任意用户的数据


- 登录态接口禁止泄露
禁止后台直接暴露可通过游戏内uid获取自定义登录态的接口,黑产可遍历uid修改任意用户数据
- 禁止将openid直接当作自定义登录态
同上,黑产可遍历openid来修改任意用户的数据
3.3.3 邀请好友功能
游戏内邀请好友获得奖励的功能容易被外部黑产利用,如图:

针对上述黑产场景,可以考虑
- 风险用户扫描,针对有可能存在恶意注册、营销作弊的黑产问题
参考安全风控接口指南 | 微信开放文档,针对风险等级>=3的用户,可以采取一些措施避免
- 开发者后台需要区分微信/其他渠道进入
若没有区分渠道,黑产可通过分享卡片获得分享参数,绕开微信场景,伪造其他场景渠道进入