996传奇客户端 C++ 反编译还原工程 — 阶段 1+2+3 全部交付 日期: 2026-09-21 起, 2026-09-23 阶段 3 完成
将 9.6MB stripped arm64 二进制的 C++ 自研层 (mmoclient, 33 类 418 方法) 还原为 9 个可运行的 Python 模块 + 10 份伪 C + 1 个完整接口契约 + 158 项回归测试。
涵盖网络协议(头编解码/XXTEA/RSA 接口)、脚本密文、地图格式(.map 文件字节级 roundtrip)、寻路(A* 8 方向)、热更下载、批量文本渲染六大子系统。
| 模块 | 行数 | 来源 | 验证 | 用途 |
|---|---|---|---|---|
hdr_codec.py |
74 | encode_header @0x10004b4e4 | 50 组随机 roundtrip | 24B 头 ↔ 32B 编码 |
xxtea.py |
193 | LuaBridgeCtl::SetXXTEAKeyGM | 第三方 xxtea 6.2.0 cross-verify | 脚本密文加解密 |
frame.py |
226 | netClient::SendNetMessage | 与 protocol_bot 字节级一致 | 36B 头 + body 帧装拆 |
rsa.py |
151 | LuaBridgeCtl::RsaEncryptPub/DecryptPri | 接口预留 (密钥待提取) | RSA 客户端本地接口 |
msg_codec.py |
232 | 上述聚合 | 33 测试 | 协议编解码统一 API |
map_parser.py |
299 | tileSceneManager:LoadMapData | 7/7 .map 字节级 roundtrip | WGS Mir2 地图解析 |
path_find.py |
311 | aiManager:FindPath + Lua A* | 41 测试, 长直道 189 格代价 1890 | A* 寻路 (8 方向) |
http_downloader.py |
321 | httpDownloader:AsyncDownloadEx | httpbin.org 1024B 实下载 | 热更/资源下载 |
label_batch.py |
142 | LabelBatchNode 22 tolua 方法 | 接口自测 | 批量文本渲染 (数据层) |
| 文件 | 二进制地址 | 函数 |
|---|---|---|
tolua_SendMsg_binding.c |
0x10007cd54 | tolua 绑定层 |
LuaNetworkController_SendMsg.c |
0x10005f8cc | 消息组装 |
netClient_SendNetMessage.c |
0x10003d408 | 加密+入队 |
encode_header.c / decode_header.c |
0x10004b4e4/94 | 头编解码 |
netClient_recv_loop.c / recv_dispatch.c |
0x100023df8 / 0x10003cd30 | 收包+分发 |
xor_deobfuscate.c |
0x100043b34 | XOR 0x10 字符串解混淆 |
init_keys_and_sign.c |
0x100043bdc | XXTEA 密钥初始化 (555 行) |
LuaBridgeCtl_SetXXTEAKeyGM_impl.c |
0x100044b88 | GM 动态换 key |
mmoclient_interfaces.h — 33 类 418 方法 .pkg 风格声明,带分组注释和 tolua 方法名
| 文件 | 覆盖 |
|---|---|
README.md |
总体说明 + 三档产物 + 用法示例 |
map_format.md |
.map 文件格式逆向报告 (含 0.map 额外区部分反推) |
rsa_investigation.md |
RSA 探索全程记录: 7 次搜索失败 → r2 aaa 突破 → 空壳 stub 定论 |
box_data_passing.md |
996盒子→游戏数据传递: env.json 双通道 + GN/NG 桥 + CS20 去向 |
SUMMARY.md (本文件) |
阶段性成果总览 |
| 文件 | 测试项 | 范围 |
|---|---|---|
tests/test_msg_codec.py |
33 | hdr/xxtea/frame/msg_codec/rsa 协议层 |
tests/test_map_parser.py |
38 | map_parser 全部 7 个 .map 样本 |
tests/test_path_find.py |
41 | A* 寻路 + 5 个 e2e 地图寻路 |
| (原有 28 项无回归) | 28 | 协议桥/调度器/网络桩 |
| 合计 | 158 (含 extra_areas 8 项 + header 语义 10 项) |
| 子系统 | 接口契约 | 伪 C | 可运行实现 | 字节级 roundtrip | 真实 e2e |
|---|---|---|---|---|---|
| 网络协议 (头) | ✓ | ✓ | ✓ | ✓ | ✓ (pcap 兼容) |
| 网络协议 (帧) | ✓ | ✓ | ✓ | ✓ | ✓ (3 个调用方字节级一致) |
| XXTEA | ✓ | ✓ | ✓ | ✓ | ✓ (第三方包 cross-verify) |
| RSA | ✓ | — | △ (接口预留) | — | — |
| 地图格式 | ✓ | — | ✓ | ✓ | ✓ (7/7 文件) |
| 寻路 | ✓ | — | ✓ | — | ✓ (189 格长直道) |
| 热更下载 | ✓ | — | ✓ | — | ✓ (httpbin 1024B) |
| 批量文本 | ✓ | — | ✓ (数据层) | — | △ (需 cocos2d-x 渲染) |
✓ = 完整还原 / △ = 部分还原 / — = 未涉及
明文字节 b → ENC_TABLE[b] ^ 9 → 8bit 窗口每次吐 6bit → +0x3C
24B 明文 → 32B 密文 (全 0x3C..0x7B 可打印)
ENC_TABLE: 256B, __DATA @0x1008ae5f4
8B 块, 16B key, 32 轮, big-endian, delta=0x9E3779B9
padding 0x00 (与 lua lockbox/cipher/tea.lua 一致)
注: 已修正 xxtea-c 参考的 z 未初始化 bug, 严格按 Needham-Wheeler 论文
52B header (width/height/title/raw_tail) + (w*h × 12B cells)
cell 12B: bg_img(2) + mid_img(2) + obj_img(2) + ani_idx(2) + door_flag(1) + light(1) + area(1) + ani_tick(1)
扩展格式 (title_len=15, 仅 0.map): header + 980000B 2B/cell 稀疏索引 + cells
额外区: 87% 零, 5% 0x8000 标志, 8% 真实 tile 索引 (slot 编码未解)
8 方向, 直线 10 / 对角 14, 启发式 = 欧氏距离²
二叉堆优先队列, 对角穿越自动阻止 (防穿墙)
寻路范围限制 48×32 (与 Lua updateLimits 一致, 长距离需分段)
urllib + threading 模拟异步
限速: 令牌桶 (throttle)
进度: callback(downloaded, total)
SHA1 校验 (可选)
完整热更流程: download_manifest() + download_update_files(only_changed)
# 阶段 1 算法库 (33) + 阶段 2 地图解析 (38+8 extra_areas+10 header语义) + 阶段 3 寻路 (41) + 原有 (28) = 158 总
cd /path/to/传奇
.venv/bin/python -m pytest tests/ -m "not live" -v
# 实时下载测试
python3 cpp_reversed/http_downloader.py
# 期望: saved 1024B to /tmp/httpdl_test/test.bin
# 字节级 roundtrip 验证
python3 -c "
import sys; sys.path.insert(0, 'cpp_reversed')
from map_parser import load
m = load('996传奇盒子资源包/03-游戏客户端/jycq_206923/scene/map/3.map')
out = m.to_bytes()
orig = open('996传奇盒子资源包/03-游戏客户端/jycq_206923/scene/map/3.map', 'rb').read()
print(f'roundtrip: {out == orig} ({len(out)}B)')
"
| 缺口 | 难度 | 解法 |
|---|---|---|
✅ 已定论 (2026-09-23): r2 aaa 完整分析证实 RsaEncryptPub/RsaDecryptPri 是空壳 stub (返回空串), 密钥不存在于二进制。证据链见 rsa_investigation.md |
||
| 0.map 额外 980000B 区 slot 编码 | 中 | ⚡ 静态分析已到边界 (2026-09-23 二轮): 7 项一组宏图块结构 + V/0x8000 交替已实证; LoadMapData 调用链挖透, 完整槽映射在运行时渲染路径, 需抓内存对照。详见 map_format.md §四 |
✅ 已解 (2026-09-23): 7 样本逐字节统计 + 10 项语义测试锁定, 含字节序勘误 (update 时间戳 BE→LE), 详见 map_format.md §二 |
||
| mapData2DRectGrid/Tile3/LightBuffer | 中 | 与运行时内存表示有关,需场景运行时抓 dump |
| ObjC 壳层 (SDKController) | 中 | 属另一文档范围,需 arm64 ObjC ABI 工具 |
msg_codec.py 做抓包分析 — 它 1 行能解出 msgId/recog/paramsmap_parser.py 解析地图 — 7 个样本可立即分析path_find.find_path() 替代手写 A*cpp_reversed/rsa.py 的 INSTALL_HINT 块map_format.md §四 "待精读" 段*.c 文件都对应一个 Python 模块最后修改: 2026-09-23 17:09