字节暗面

玩家存档加密该做到哪一步:文件、内存与服务端校验


最近帮一个项目做客户端安全检查,查到存档这部分时发现了问题:金币、等级、背包和任务进度都明文落在本地,字段和值一眼能对上,改完再塞回去,客户端照常读取。

存档加密麻烦的点在于,它是游戏自己要反复读写的数据。代码可以混淆到让人难读,存档还得写回成下次自己能认的格式,那份数据对拿着手机的人就是敞开的。它又是磁盘上的持久状态,不像战斗结算报一次就完,攻击者可以把文件拷走,离线慢慢改,再放回去,没有时间压力。很多时候不需要逆向代码,找到那个数、改掉、存回去就行。

明文存档,改起来没门槛

先看最直接的。Unity 的 PlayerPrefs 在 Android 上就是一个 XML:

/data/data/<pkg>/shared_prefs/<pkg>.v2.playerprefs.xml

(老版本没那个 .v2)。打开全是明文键值,金币明晃晃一行 <int name="gold" value="99999" />。这不是我黑它,Unity 官方文档自己写着 PlayerPrefs 不加密、别拿来存敏感数据。背包、任务也一样:加个稀有道具就是往 JSON 数组塞一项,过个任务就是把某个 flag 从 0 改成 1。

这一档没门槛。有 root 可以直接翻;可调试包或备份配置没收紧,也可能把文件导出来。再加上一堆现成的存档编辑器 app,改完一丢就有人用,破解存档才会传得这么快。

第一反应是加密:别落明文,AES 一层,键名也别老实叫 gold

加密了文件,内存里还是明文

但只加密文件,用处比想的小。游戏要用金币,存档一加载,它在内存里就是个明文 int。GameGuardian 根本不碰你的文件,直接搜内存:输当前金币 12345 搜一遍,花掉点变 12300 再搜,几轮就锁定那个地址,改了、冻上。你那层 AES 全程没参与,因为攻击压根没走文件这条路。资源加密是同一个坑,Decrypt 写得再花,人家 hook 你的 LoadFromMemory 把明文捞走,你的解密根本没进场。

所以值在内存里也不能存明文。常见的是 ObscuredInt 那套,内存里躺的是 gold ^ key,用时才异或回来:

int _key = Rand();          // 每次运行随机
int _enc = gold ^ _key;     // 内存里是异或后的值
int Gold => _enc ^ _key;    // 读时才还原

GG 搜 12345 就搜不到了,内存里那个数是 12345 ^ key。但这只挡图省事的:GG 有针对 XOR 的 encrypted value 搜索(输两个值 a;b),还有 fuzzy 搜索。不管你混淆成什么样,它只看这数变大还是变小,顺着你花钱赚钱的增减照样把地址逼出来。

藏住值还不够,得发现被动过

到这层,完整性校验就得跟上。保存时给存档算一份 HMAC,加载时重算比对,对不上就是被改过。

string mac = HmacSha256(saveKey, saveBytes);  // 存成 {data, mac}
// 加载时重算,和存档里那份比,对不上 = 被篡改

再补一手:一个数在存档里存两份(值加反码)交叉校验,改一份、冻一份,另一份都会露。顺一句性能,别一到 autosave 就把整份存档重加密、重算 hash,存档一大低端机就卡,护太狠把游戏拖卡等于赶客。

存档放哪,密钥绑谁

存档存在哪也是笔账。往应用私有目录放,别写公共目录,普通第三方 app 就够不着了;当然也就挡这一档,root 或调试提取照样够得着。

想让改好的存档拷去别的机器用不了,就一机一把钥匙:每个安装实例单独生成密钥,塞进 Android Keystore / iOS Keychain,别让它明晃晃躺在能读的地方。账号 ID、槽位、版本号顺手丢进认证加密的关联数据(AAD),跨账号拷的、换槽位的、版本对不上的,解密时自己就露。IMEI、Android ID 这种就别拿来当密钥了,它会变、受权限限制,还连累卸载重装、换机和云恢复。

自己不想养这套加密和密钥管理,接个现成的存档加固方案也行,但要先看它管不管密钥隔离、完整性和跨设备这几样。反正不管自研还是外接,这些都还是文件层:攻击者跟到密钥调用、解密入口,或者加载完的对象,照样绕过文件本身,无非多花点时间。

谁说了算,分联网和单机

联网、带经济、能交易或上榜的,权威存档放服务端,客户端只留缓存。但“搬到服务端”还不够:服务端如果把客户端上传的存档原样入库,篡改过的金币和道具照样会被收进去。它得判断这份状态合不合理,做值域、字段关系和必要的过程校验。

纯单机没有服务端兜底,做多少主要看投入产出。前面的保护叠起来,能挡住大部分图省事的修改;真有人铁了心折腾,很难彻底拦住。只要存档能换成榜单名次、交易资产或付费资源,最终算不算,别让玩家的手机说了算。

← 返回列表