Frost Valley 逆向总览
Unity IL2CPP 合成 + 章节式家园修复游戏。本页汇总 7 份专题笔记的主线:启动流程、玩法循环、配置数值、加密协议,以及还没解决的问题。全部来自静态分析,没有运行过游戏。
- 包名
- com.liteorange.hometopia
- 版本
- 1.12.0 (1120000)
- 引擎
- Unity 2022.3.62f2c1
- 脚本后端
- IL2CPP · metadata v31.1
- SDK 平台
- 途游 Tuyoo · AppLovin MAX
- SDK
- min 24 · target 36
做到了哪一步
| 阶段 | 方法 | 结果 | 状态 |
|---|---|---|---|
| C# 结构恢复 | Cpp2IL 导出签名与 ISIL | 135,486 个方法 · 11,232 个类型文件 | 完成 |
| 子系统梳理 | 5 个专题并行分析 | 笔记 01–05 | 完成 |
| 配置表解码 | M1 XOR → LZ4 → MessagePack,按 C# Key 还原字段名 | 291 / 291 张表,0 个未映射字段 | 完成 |
| 关键函数反编译 | Ghidra 12.1.4,套用 13.1 万个方法名和 2.1 万个字符串字面量 | 笔记 07:加密、扣费、发奖、访客判定 | 完成 |
| 全量伪 C | 2.88 万个游戏函数批量反编译,按精确边界和 noreturn 修正 | 28,787 个函数 · 失败 1 个 · 77MB | 完成 |
| 运行时验证 | Frida / 真机存档 | 还没做 | 未开始 |
从 Java 层到业务代码
业务代码拆在约 40 个自研程序集里。Assembly-CSharp 只放了 Bootstrap 等少数类。蓝框是游戏自研部分。
12 个启动模块
MergeLauncher.AddLaunchModules @0x30eb3fc 注册 12 个模块,Launcher 按依赖做拓扑排序,就绪的模块并发初始化。下面每行是一个依赖层级。
- DebugEnvChoose release 包中为空实现
- BasicMgr 服务器时间同步
- UserInfo · GdprLauncher · YooAsset 本地存储与用户信息 / GDPR 与 TCF 同意 / 资源包
- YooGa · ThirdSdk 途游 SDK 初始化、登录、GameServer / Firebase
- Config 291 张表:CDN
latest.txt+cfg_N.bytes,失败用内置message_bytes;AB 覆盖 - UserGroup 按充值和激励视频频次分群
- IAP ShopService 与恢复购买
- MAX 广告 SDK 与 CMP
- OtherMgr UI、货币、语言、卡册、邮件等业务服务,注册 33 个活动
全部成功后加载 GameScene。TestHome.RunStartupAsync 关闭加载界面:第一章前三个建造点没完成就直接进剧情,否则进家园。家园和合成在同一个场景内切换。
体力 → 合成 → 金币 → 建造 → 等级
- 7 × 9 棋盘,物品 = 链 + 等级
- 双击生成器耗体力出棋子
- 同链同级合成后升一级,并解开相邻障碍
- 托盘策略填单,有每日上限
- 金币 = 订单内棋子
Price之和,无倍率 - 同时发活动积分和卡包
- 91 个故事 ID、190 个场景
- 建造点花金币,然后播动画和对话
- 等级只靠建造提升
- 33 个活动,28 天循环
- 122 个 IAP SKU
- 激励视频按用户分群调整
体力与产出
| 项目 | 值 | 来源表 |
|---|---|---|
| 体力上限 / 恢复 | 100 · 每 120 秒 +1 | MergeEnergyConfig_Main |
| 体力倍率解锁 | ×2 16 级 · ×4 30 级 · ×8 30 级(需 400 体力) | MergeEnergyConfig_EnergyBoost |
| 生成器 | 大多每次 1 体力,有次数上限和冷却 | MergeChessConfig_MergeChessGeneratorInfo |
| 剧情建造 | 1,163 个建造点 · 共 371,722 金币 | StoryStrategy_LTProgress |
| 等级 | 200 级;剧情全部建完约到 85 级 | MergeLevelConfig_LevelCostReward |
| 钻石档位 | $1.99 – $99.99(gem100 – gem7450) | IAPInfo_PurchaseInfo |
| 广告 | 插屏和开屏在默认配置里关闭;分群 1/2 关掉全部激励位 | AdConfig_* |
线上值可能被 CDN 下发的配置或 AB 实验覆盖,下载到线上配置后可以用同一个解码脚本处理。
五种处理算法和两个协议
| 对象 | 算法 | 密钥来源 | 证据 |
|---|---|---|---|
| M1 Handler | 循环 XOR:out[i] = in[i] ^ key[i % n] | 调用方传入 | ProcessDataSafe @0x2e98f2c 观察 |
| M2 Handler | AES-128-CBC / PKCS7,密文前 16 字节是 IV | SHA256(key)[0:16] | GenerateAesKey @0x2e993f4 观察 |
| 内置配置 | M1 → Lz4BlockArray → MessagePack | RemoteConfigMeta.Extra | 291 张表 MD5 校验通过 观察 |
| 访客配置 | v_cfg 用 M2,v_rule 用 M1 | Extra / 字面量 "v_rule" | 两者都已解开 观察 |
| 本地存档 | MessagePack LogData,Content 用 M1 | 新档 Flag=1, Extra="None" | 需用真机存档验证 推断 |
| GameServer | AES-256-CBC,IV = key[0:16];16 字节随机前缀 + 大端长度 + JSON,按 32 字节补齐后 Base64 | metadata 中两个 32 字节数组逐字节异或 | 笔记 07 §3;请求没有签名 观察 |
| YooAsset bundle | 只在文件头加了 12 字节 0x00 偏移,没有加密 | 无 | 三个包抽样都是 12×00 + UnityFS 观察 |
部分设备会拿到另一套配置
VisitorHandler 按 v_rule 的 11 条规则依次判定,第一条命中即生效:白名单判为正常用户,黑名单判为访客,全都不命中判为正常用户。结果会持久化,只算一次。
| 规则 | 类型 | 默认启用 |
|---|---|---|
| device_id 白名单(179 个)· IP 白名单(4 个)· 设备名白名单 · debug 包 | white | 是 |
| device_id / IP 黑名单(当前为空) | black | 是 |
| 中国区域 · 中文系统语言 | black | 是 |
| 无 SIM · 非 Google Play 安装 · 已 root | black | 否 |
被判为访客后,会用 v_cfg 给 8 张表打稀疏补丁,并跳过远端配置和 AB。列表表按主键替换对应行,Main 表只覆盖 MainMap 里列出的字段。实际效果:关闭 FlamingFrenzy 活动、资产气泡激励位、推送、评分弹窗、剧情对话和立绘;开启序章;第 3–7 级的金币和经验门槛调高(例如第 6 级金币从 244 提到 480)。活动总开关和首启视频不变。推断 这是给审核人员或中国区设备看的简化版本。
多个系统共用的部分
| 机制 | 实现 |
|---|---|
| 弹窗与流程 | UIMgr 弹窗栈加上 FlowMgr:首页约 26 个单元,合成局内约 50 个,按优先级串行执行 |
| 资源 | YooAsset 三个包。剧情包只内置第一章,其余约 1.79GB;活动包 1,091 个 bundle,内置 303 个,开启前 48 小时内预下载 |
| 云存档 | GameServer tuyoo_client:user/bind、hall、fkworld get/set_game_data |
| 掉落调控 | Koilab FV:ONNX 双头模型给"留存"和"看广告"打分,决定生成器下一个产出(包内默认关闭) |
| 埋点 | Reporter 走途游 GA 主通道,另有 AppsFlyer、Firebase、Koi 阈值事件 |
| 混淆 | Obfuz 运行时已装,但名称都保留,业务代码里没有加密调用 |
值得关注的点
内购只在服务端校验
客户端验签失败只上报,照样本地发货。恢复购买走游戏服务器邮件,不查询 Google。
包里写死了密钥
AB SecretKey、FV AccessKey(HMAC)、MAX sdk key 和 GameServer AES 密钥都在包里。笔记只记录前几个字节。
用机器学习调控掉落
生成器的产出由模型按留存和看广告倾向打分决定,lucky 目标率 0.02,支持本地和云端推理。
活动完全本地运行
活动代码里没有网络请求,排行榜和竞速对手都是本地机器人。
审核态配置
中国区或中文设备默认判为访客,会关掉一个活动、广告气泡、推送、评分和剧情对话,早期升级也更慢。
还需要验证的
| 问题 | 状态 | 下一步 |
|---|---|---|
| 线上配置与 AB 实际取值 | 未解决 | 拿到 CDN 下发的 cfg_N.bytes 后用 decode_config.py 解码 |
| 存档是否就是 XOR "None" | 推断 | 用真机 All.main.bin 验证 |
| 服务端收据校验 | 未解决 | 静态分析看不到,需要观察网络流量 |
| Koilab FV 线上开关与 WebSocket 协议 | 部分 | 包内默认关闭,需要 AB 数据 |
文件在哪里
| 内容 | 路径(相对 merge2/) |
|---|---|
| 专题笔记 00–07 | notes/ |
| C# 签名 / ISIL | extracted/cs/ · extracted/isil/ |
| 配置 JSON(291 张表) | extracted/config/tables/ |
| 访客规则与配置 | extracted/config/v_rule.decoded.json · v_cfg.tables.json |
| Ghidra 工程(已套符号) | ghidra_proj/ |
| 伪 C(游戏自研程序集) | extracted/pseudo_c/ |
| 脚本 | scripts/cfg/ · scripts/ghidra/ · scripts/strlit/ |