【免费下载链接】lunar-tear
Private server research project for a certain discontinued mobile game
停服手游的客户端里通常藏着一份"主数据"(Master Data):角色、武器、抽卡池、任务、商店……所有配置都打包在一个加密压缩的二进制文件里。Lunar Tear 是一个针对某款已停服手游的私服研究项目,它的核心工作之一就是逆向并解密主数据 .bin.e 文件,用 AES-CBC 解密 + msgpack 解析 + LZ4 解压三步打通完整链路。本文带你完整拆解这条数据链路的每一步,以及解密后的数据如何被服务器热加载。
一份 .bin.e 是什么?
游戏客户端首次启动时会向 CDN 请求下载主数据文件。Lunar Tear 的 CDN 服务(octo-cdn)就原样提供这个文件,代码位于 octo.go:
- 文件固定存放在
assets/release/20240404193219.bin.e(文件名中的数字是版本号yyyymmddHHMMSS) - URL 里的
{version}段只作为客户端缓存键,磁盘上永远只有这一个.bin.e - 服务器端则直接从该路径读取并解密,见 main.go
也就是说:.bin.e=加密 + 压缩 + 序列化三件套叠加的"套娃"文件。
解密流水线总览
整个逆向过程可以浓缩成一条流水线(全部实现在 memorydb.go):
.bin.e 密文 │ ① AES-128-CBC 解密 + PKCS7 去填充 ▼ 明文 │ ② 解析 msgpack 目录头(表名 → [偏移, 长度]) ▼ 目录 TOC + 数据区 data blob │ ③ 每张表:msgpack ext(code=99) → LZ4 解压,或直接解析普通数组 ▼ []struct 强类型 Go 结构体下面逐步拆解。
第一步:AES-CBC 解密
打开 memorydb.go 就能看到逆向得到的"钥匙",直接以十六进制常量写死在代码里:
aesKeyHex = "36436230313332314545356536624265" // 16 字节 → AES-128 aesIVHex = "45666341656634434165356536446141" // 16 字节 IV解密逻辑(memorydb.go)要点:
- 模式:AES-128-CBC,
cipher.NewCBCDecrypter(block, iv) - 校验:密文长度必须是 16 字节的整数倍(CBC 分块大小)
- 去填充:解密后执行 PKCS7 Unpadding(pkcs7Unpad),校验末尾填充字节全部合法,防止"差一位"的脏数据静默通过
💡 新手提示:AES-CBC 是"分组密码 + 链式加密"。解密只需要密钥和 IV,但 IV 错了会导致第一块解密结果全乱——这也是逆向时最先验证的参数。
第二步:解析 msgpack 目录头
AES 解密出来的并不是直接的数据表,而是一个msgpack 编码的 map,充当"目录"(TOC):
{ "m_weapon": [偏移, 长度], "m_gacha_medal": [偏移, 长度], "m_config": [偏移, 长度], ... }解析实现在 parseHeader:
- 先用
msgpack.NewDecoder把第一个 msgpack 对象解成map[string]interface{} - 每个 value 必须是
[offset, length]二元数组 - 通过"解密前总长 − 解码器剩余字节数"算出目录本身占了多少字节,剩余部分就是数据区 data blob
之后每张表只需按dataBlob[off : off+length]切片取出原始字节,并校验off + length不越界(memorydb.go)。
第三步:LZ4 解压(msgpack ext 类型)
每张表有两种可能的编码形态,decompressAndUnmarshal 会先"偷看"(peek)首字节再分流:
| 形态 | 特征 | 处理 |
|---|---|---|
| LZ4 压缩表 | 首个对象是 msgpack ext,类型码99(0x63) | 先解 LZ4 块,再解内层 msgpack |
| 明文表 | 普通 msgpack 数组 | 直接 Unmarshal |
LZ4 ext 内部还有一层小"表头"(readLZ4ExtHeader):第一个字节标记解压后尺寸的编码方式:
0xd2/0xce:大端 int32 / uint32 尺寸(5 字节头)<= 0x7f:正 fixint,尺寸就在这一字节里
拿到尺寸后分配缓冲区,调用lz4.UncompressBlock解压,最后msgpack.Unmarshal到强类型切片。
第四步:从字节到强类型 Catalog
解密后的原始字节最终要变成 Go 结构体。表结构由schemas.json生成,见 entities.go(文件头注明Code generated by scripts/gen_entities.py):
// EntityMAbility is table key "m_ability". type EntityMAbility struct { AbilityId int32 AbilityLevelGroupId int32 }各业务模块用泛型工具函数按"表名"取数,例如抽卡模块(gacha.go):
utils.ReadTableEntityMGachaMedal utils.ReadTableEntityMMomBanner入口只有一个:utils.ReadTable →memorydb.ReadTableT。
解密之后:热加载与版本广播
这是 Lunar Tear 比普通"离线解密脚本"更进一步的地方——解密结果被包装成可原子热切换的运行时快照:
- runtime/holder.go:
Holder持有atomic.Pointer[Catalogs],Reload()时重新memorydb.Init整个文件、从零重建全部 Catalog,再一次性原子发布。所有 gRPC 服务每次 RPC 开头调Get(),永远看到一致快照。 - admin.go:管理 Webhook
POST /api/admin/master-data/reload(默认仅监听127.0.0.1:8082),需LUNAR_ADMIN_TOKEN环境变量设置后才会绑定——"fail-closed"设计,未配置 token 时端口根本不开放,且 token 比较使用常量时间算法防时序侧信道。 - 版本字符串联动:data.go 中
GetLatestMasterDataVersion把文件的mtime拼进版本号。Reload()会os.Chtimes刷新 mtime,客户端下一次轮询就会发现"版本变了",主动从 CDN 重新下载.bin.e(CDN 侧正是 serveDatabaseBinE 提供文件)。
于是形成闭环:改文件 → 调 Webhook → 内存原子换新 → 版本号变化 → 客户端自动拉取新数据,全程无需重启服务器。
涉及模块速查表
| 环节 | 文件 | 说明 |
|---|---|---|
| AES 密钥/IV、解密主流程 | memorydb.go | Init/decrypt/parseHeader |
| LZ4 ext 解压 | memorydb.go | decompressAndUnmarshal |
| 表结构定义(生成代码) | entities.go | 由schemas.json生成 |
| 读表入口 | utils.go | ReadTableT |
| 热加载 Holder | holder.go | 原子快照切换 |
| 管理 Webhook | admin.go | token 门控的热重载端点 |
| 版本号广播 | data.go | mtime 拼入版本字符串 |
| CDN 分发 .bin.e | octo.go | serveDatabaseBinE |
总结
逆向停服手游主数据的完整链路,本质上就是回答三个问题:
- 怎么解密?→ 找到 AES-128-CBC 的密钥与 IV(16 字节十六进制常量),PKCS7 去填充
- 怎么定位数据?→ 解密出的 msgpack map 就是目录,
[偏移, 长度]直接切片 - 怎么读出来?→ 识别 msgpack ext 类型码
99即 LZ4 压缩,解压后再解 msgpack 到强类型结构
Lunar Tear 的价值在于把这条逆向链路工程化了:生成式实体代码、泛型读表接口、原子热加载、token 门控的管理端点和 mtime 版本广播,让"解密 .bin.e"从一次性实验变成了可持续维护的私服基础设施。如果你正在做类似项目,不妨对照 memorydb.go 的四个函数逐段阅读——那大概就是全文最短的"主数据解密教程"。
【免费下载链接】lunar-tear
Private server research project for a certain discontinued mobile game
相关推荐
逆向人教社官网数据:FreePEP 如何用 AES-128-CBC 解密前端 JS 中的 780 本教材目录
逆向人教社官网数据:FreePEP 如何用 AES 128 CBC 解密前端 JS 中的 780 本教材目录 人教社电子教材下载器 FreePEP 的核心能力,
拆解TheFeed的Feed协议:AES-256加密数据如何装进DNS TXT响应(逐字节解析)
拆解TheFeed的Feed协议:AES 256加密数据如何装进DNS TXT响应(逐字节解析) TheFeed 是一个基于 DNS 的加密订阅阅读器(DNS
如何用Il2CppDumper破解Unity游戏逆向难题?3个逆向场景全解析
如何用Il2CppDumper破解Unity游戏逆向难题?3个逆向场景全解析 引言:Unity逆向工程师的三大困境 在Unity游戏逆向领域,开发者们常常面临着
逆向工程开发工具游戏开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考