游戏接入加固后,仍可能遇到两类问题:安装包里的文件难以直接分析了,运行中的血量却依然被修改;首包中的资源做了加密,新版本下发的 AB 包却暴露了未上线内容。
这些情况说明,手游加固需要检查具体保护对象,以及保护在什么阶段生效。代码、资源、内存数据和运行过程面临的风险不同,不能只看安装包是否经过加密处理。
本文以 Android 原生模块和 Unity IL2CPP 项目为例,梳理手游加固的保护范围与检查要点。
一、先把保护对象对应到风险
| 保护对象 | 常见分析或篡改方式 | 主要保护方向 |
|---|---|---|
| 原生代码、metadata | 借助符号、字符串及类型信息定位逻辑 | 模块加固、代码混淆、字符串与 metadata 保护 |
| AssetBundle、配置与脚本 | 提取素材、分析规则、替换文件 | 资源加密、完整性保护与热更新保护 |
| 运行中的敏感数据 | 搜索数值变化、定位并修改数据 | 内存数据加密、混淆与校验 |
| 游戏执行过程 | 调试、Hook、注入及重打包 | 运行时检测、环境检测与完整性校验 |
这几类保护有交集。例如资源解密后需要被游戏使用,代码保护也要面对运行时分析。因此,检查加固范围时,既要看文件,也要看加载和执行过程。
二、代码保护:原生模块与 metadata 需要配合
1. SO 加固保护的是哪些内容
Android 游戏里的 .so 是原生模块,其中可能包含引擎代码、业务逻辑或安全代码。攻击者分析时,往往先借助函数名称、可读字符串和调用关系,寻找战斗结算、购买校验等目标。
手游代码保护可通过代码混淆、字符串保护和原生模块加固,减少直接可用的分析线索,提高定位和修改关键逻辑的难度。
需要注意,保护了某一个 SO,不代表游戏的所有关键逻辑都已覆盖。项目可能同时包含多个原生模块,以及独立的配置和脚本。确定范围前,应先确认关键业务实际分布在哪里。
2. IL2CPP 项目为什么还要关注 metadata
Unity IL2CPP 会将托管程序集转换为 C++,再编译为目标平台的原生代码。1 这改变了代码的呈现形式,但项目仍然需要评估代码中的分析线索。
global-metadata.dat 中的类型、方法和字段信息,可以帮助分析者关联原生代码。代码与 metadata 如果暴露出清晰的对应关系,就会降低定位目标逻辑的难度。
因此,Unity IL2CPP 手游加固应同时考虑原生代码和 metadata,减少两者之间容易被利用的关联信息。不能仅凭文件是否改名,或某个工具是否报错,就判断保护已经充分生效。
三、资源保护:覆盖首包、热更新和本地缓存
资源保护主要关注模型、贴图、音频,以及配置和脚本等内容。素材被提取可能造成盗用或提前泄露,配置和脚本暴露也可能让玩法规则更容易被分析。
Unity 的 AssetBundle 是常见资源载体。这里的"脚本资源"需要区分:AB 包可以承载供解释器读取的脚本文本等资源,但不意味着它能直接包含并加载新的 C# 程序集代码。
1. 加密与完整性校验分别处理什么
资源加密提高内容被直接读取和提取的难度。完整性校验用于检查资源是否符合预期,识别损坏或篡改。两项措施需要配合。
Unity 提供的 CRC 可用于检查 AB 内容是否与预期一致;文档还明确指出,构建生成的 AssetBundle Hash 可作为版本值,但不适合用于文件损坏检测。2
如果需要抵御恶意替换,还应考虑校验依据是否可信。攻击者若能同时替换文件和它的预期校验值,单纯比较两者无法确认资源来自开发方。资源校验设计要结合可信来源、签名或消息认证等机制,按实际分发流程确定。
2. 热更新资源也要进入保护流程
只加密首包中的 AB,后续资源以明文下发,新角色和活动素材仍可能被提取。手游加固需要覆盖资源的持续更新过程:
- 构建与发布:新生成的 AB、配置及脚本资源是否经过保护。
- 下载与缓存:资源落地后是否仍按预期保存,是否出现额外明文副本。
- 加载与使用:是否验证内容,并在校验失败时停止使用异常资源。
校验时机要结合缓存是否可能被修改来设计,同时评估额外 I/O、解密与校验开销。不能默认"下载时通过校验",就能覆盖此后发生的本地替换。
四、内存保护:处理游戏运行后的敏感数据
文件保护生效后,游戏仍需在运行时处理血量、坐标、技能冷却等数据。修改器可以根据数值变化缩小搜索范围,再尝试定位和修改目标。
内存保护可通过敏感数值加密、混淆和校验,提高定位与修改的难度,并识别部分异常变化。重点应放在与玩法和收益直接相关的数据,而不是不加区分地处理所有变量。
例如技能冷却值,需要结合技能释放过程检查变化是否合理;奖励相关状态,还要与实际完成条件对应。孤立地保护一个数值,仍可能遗漏影响它的其他逻辑。
另外,攻击者不一定直接修改该数值,也可能通过 Hook 改变计算或校验函数的返回值。因此,内存数据保护还需要运行时检测配合。
五、运行时保护:关注调试、Hook、注入与重打包
运行时保护检查的是游戏启动后受到的干预。OWASP 将反调试、反篡改及运行时自保护列为提高移动应用抗逆向与抗篡改能力的措施。3
在手游中,可以按下面几个方向检查覆盖范围:
| 风险 | 检查方向 |
|---|---|
| 调试与动态分析 | 是否存在跟踪关键逻辑、干预执行过程的行为 |
| Hook 与异常注入 | 函数执行和已加载模块是否出现异常变化 |
| 运行环境异常 | 是否存在影响安全判断的环境风险信号 |
| 包体篡改与重打包 | 已发布代码、包体身份及关键内容是否符合预期 |
检测到异常后的处理,也需要结合场景设计。环境风险信号、运行时篡改证据和业务结果异常,含义并不完全相同,应综合判断,避免把单一信号直接等同于作弊结论。
六、关键业务结果仍需服务端复核
手游加固提高客户端被分析和修改的成本,但不能让客户端上报的数据自动变得可信。
联网游戏中的奖励领取、购买确认和排行榜成绩,需要服务端结合业务状态复核。例如奖励是否满足领取条件、是否已经发放,购买是否有效,成绩是否符合规则。
客户端负责增加干预成本并提供风险信息,服务端负责判断关键业务结果。设计方案时,应明确两边分别承担什么,避免将客户端校验当作最终依据。
七、FairGuard 手游加固如何覆盖上述对象
针对这些保护对象,FairGuard 根据游戏引擎、发行平台和玩法配置加固与反外挂方案。
在静态层面,通过原生模块加固、代码混淆和字符串保护,提高关键逻辑被分析与修改的难度;Unity IL2CPP 项目同时考虑代码和 metadata,减少定位线索。资源侧提供 AssetBundle、配置与脚本的加密和完整性保护,并支持热更新资源保护。
在动态层面,针对血量、坐标、技能冷却等敏感数据进行内存保护,配合运行环境检测、反调试、反注入和包体完整性校验,识别运行过程中的异常干预。
接入时,应先列出项目需要保护的模块、资源和敏感数据,再确认对应措施是否覆盖。后续引擎升级、资源更新或发行包变化时,也要复核保护范围,避免新内容落在原有流程之外。