Cocos Creator 2.4构建产物还原:从APK/微信小游戏包恢复源工程 上个月我帮一个朋友恢复他丢失的源工程情况是这样的他手头只有一个打好的微信小游戏包加一个安卓的APK安装包整个Cocos Creator 2.4的项目源文件因为硬盘故障全没了。我把这两个产物拆开看了半天最后花了大概一天半把场景、预制体、脚本代码和资源目录全部还原出来重新在Creator里打开项目能正常跑起来继续改需求。回来之后我把这套流程整理成了一个命令行工具这篇文就详细聊聊它的设计思路、实操步骤和踩过的坑。先说清楚这个工具解决的是什么问题它面向Cocos Creator 2.4.x构建产物微信小游戏、安卓APK、iOS安装包、Web版本均可做的是把构建产物里残留的脚本代码、场景序列化数据、资源引用关系重新组装成一个可被Creator识别和打开的标准工程。这里有个前提说在前面还原自己的项目资产、做学习研究是完全没有问题的但拿去还原别人的商业游戏用于二次分发既不合规也不是工具的设计目的。下面进入正题。1. 为什么需要这样一把还原钥匙构建产物是最后的资产1.1 我遇到的真实困境源工程没了只剩一堆构建物很多团队做Cocos Creator项目时源工程管理并不规范。我见过的情况包括外包公司交付后原始工程没有同步、团队解散后代码仓库权限丢失、换电脑时Git记录损坏、甚至有人只把发布包发到客户那边源工程在某个角落再也找不到了。对Cocos Creator项目来说源工程丢失最头疼的点不在于美术资源——资源一般还有原始图片和音频文件真正难恢复的是三样东西场景/预制体的节点结构、脚本组件的挂载关系、脚本代码本身。普通玩家看一个APK或者小游戏包里面的内容就是一堆压缩混淆过的JS文件和二进制资源觉得这跟源码完全是两回事。但做过逆向的人都知道Cocos引擎的构建产物在还原这件事上条件其实比很多人想象的友好。1.2 2.4 构建产物里天然带着半套工程为什么说友好关键在于Cocos Creator的构建链路上两个核心事实第一脚本并没有被编译成二进制字节码。TypeScript脚本在构建时被编译成JavaScript然后通过webpack等工具打包合并成game.js微信小游戏端或类似的核心JS文件。既然是JS就保留了变量名、函数体、类定义只是经过压缩混淆之后可读性变差。这和C编译成机器码、C#编译成IL后的还原难度完全不是一个量级。第二场景和预制体本质上是一份JSON序列化数据。Cocos Creator 2.4的场景文件(.scene)、预制体文件(.prefab)在编辑器里虽然是二进制格式存储序列化后的JSON数组但在构建产物里它们的结构信息会以更直接的JSON形式出现在assets目录下。节点层级、组件属性、资源引用全都在只是需要通过映射关系把压缩uuid还原成完整uuid再对应到真实的资源文件路径。所以只要把构建产物里的三类文件理顺settings.js元数据、game.js脚本代码、场景/预制体JSON结构数据还原一个可编辑的工程是完全可行的。1.3 工具定位于恢复而非破解这款一键解析工具的核心目标是信息重组把一份被打散的信息重新拼装起来。整个还原过程不涉及绕过任何加密算法也不试图摘除任何防护机制纯粹是把Cocos引擎已经公开的文件格式规范反向使用一遍。我理解很多读者对逆向两个字有天然的兴奋感但说句实在话这个工具最大的价值场景就是你自己的项目炸了。把它想成数据恢复工具比破解工具更准确。工具在还原过程中也会自动丢弃一些不适合还原的内容比如第三方付费插件混淆后的代码、明显有版权保护的资源包这些我会在后面的合规边界里细说。2. 还原链路上的三个关键关口要写一个能用的还原工具第一步不是写代码而是吃透Cocos Creator 2.4构建产物的文件格式。我把整个还原链路拆成三个关口任何一个理解不到位输出工程就是坏的。2.1 settings.js整个工程的文件系统元数据在微信小游戏构建产物里src/settings.js是第一个要打开的文件。它本质上是一个给引擎运行时加载用的全局配置对象但里面存着还原工程最需要的两张表。第一张是mounts数组。每个bundle对应一条mount记录包含bundle的id、根路径等信息。这告诉你工程里分了哪些bundle主包在哪里resources目录是否独立成包。第二张是uuidMap对象。这是最关键的字典key是压缩后的uuidvalue是完整uuid。Cocos在序列化数据里为了减小体积会把36位标准uuid压缩成很短的字符串常见规则是去掉横线后取前5位配合引擎内部的查表能力还原场景JSON里自定义脚本组件的__type__用的就是这个压缩值。没有这张表你根本不知道某个组件到底对应的是哪个脚本。举个例子settings.js里的核心结构长这样window.__settings { platform: wechatgame, version: 2.4.10, engine: 2.4.10, uuidMap: { a1b2c: d94e8a5d-3d29-4edc-ba2e-5f8c6aa51f62, // ... }, mounts: [ { id: main, path: main, root: }, { id: resources, path: resources, root: resources } ], // ... };如果把还原工程比作拼图settings.js就是拼图盒背面的完整图案。工具启动后第一步永远是解析它把uuidMap载入内存后续所有场景解析都靠这张表做翻译。2.2 game.js被webpack打包压缩后的脚本集合在微信小游戏产物里game.js是把所有自定义脚本和引擎启动逻辑打包后的产物。Charset是纯JavaScript但经过webpack处理之后每个模块被包在一个函数作用域里变量名被压缩成短名类名通常还是保留着的因为Cocos的组件系统需要通过类名和装饰器注册但整体可读性很差。还原工具在脚本这一关要做的不只是把代码拿出来而是要做三件事美化beautify把压缩成一行或几行的代码通过格式化工具还原成缩进清晰的代码这步没有技术难点但能极大提升后续阅读效率。提取类定义与组件注册名Cocos 2.4中自定义组件使用cc._decorator或ccclass装饰器注册构建后代码里会留下cc._RF.push({}, uuid, ClassName, undefined)这样的注册痕迹。通过扫描这些注册调用可以把脚本uuid、类名、代码片段三者锁定。关联project.js里的scriptList构建产物里还有一份src/project.js里面通常带scriptList它是脚本uuid与类名的官方对照表。结合这份对照表给每个脚本文件命名、确定它在assets目录里的相对路径脚本还原的根基就稳了。这一步的坑在于不是每个类名都能恢复。如果项目里用了构建时的JS混淆插件或开发时脚本名和ccclass类名不一致部分组件会变成无法识别的匿名类工具只能按UnresolvedScript_uuid这样的占位名处理。2.3 场景与预制体JSON节点组件序列化结构Cocos Creator 2.4的场景文件在编辑器里保存为二进制JSON数组第一个元素是场景资源对象后面的元素是节点、组件、数据对象对象之间的引用靠{__id__: N}指向数组下标。构建产物里这些场景数据以解压后的assets资源出现结构基本一致。看一个简化版的场景片段[ { __type__: cc.SceneAsset, _name: Main, scene: { __id__: 1 } }, { __type__: cc.Scene, _name: Main, _children: [ { __id__: 2 } ] }, { __type__: cc.Node, _name: Canvas, _components: [ { __id__: 3 } ], _children: [ { __id__: 4 } ], _parent: { __id__: 1 } }, { __type__: a1b2c, _node: { __id__: 2 }, _enabled: true, _name: PlayerController } ]注意这里__type__为a1b2c的组件a1b2c就是压缩uuid。有了settings.js的uuidMap工具可以把这个值换回完整uuid再通过scriptList找到类名最后还原出这个组件对应的脚本文件内容。场景/预制体的还原是整套工具里逻辑最重的部分因为Cocos的序列化格式里还存在大量内置组件类型cc.Node、cc.Sprite、cc.Animation等它们不需要脚本映射但属性的字段名和默认值需要和Creator的import导入逻辑完全对齐否则场景打开后会出现资源丢失或属性异常。3. 工具核心模块拆解把上面的原理落地成工具我按职责拆成了五个模块。这里每个模块都说下设计思路和关键代码逻辑不贴完整工程重点讲清楚为什么这么写。3.1 入口模块解包与产物识别入口模块负责把不同来源的构建产物统一成一个临时目录。微信小游戏产物本身是个文件夹直接用APK产物需要先解包Android端的APK本质是个zip我用apktool解包也可以直接unzip拿到assets目录iOS的ipa同理网页版产物直接给目录路径即可。解包完成后的第一件事是识别引擎版本。这一步决定了后续所有格式解析规则。Cocos Creator 2.x各个小版本之间序列化格式有细微差异我用settings.js里的engine字段判断如果是2.4.x就按2.4规则解析版本不在支持范围内直接退出并给出提示。这个模块还有一个容易被忽略的职责过滤无用文件。构建产物里往往有大量引擎内置资源、自动生成的配置还原工程不需要把全部东西抄回来工具会按白名单机制只保留脚本、场景、预制体、动画、图集、音频、字体等核心资源同时保留project.json和settings.js作为校验依据。3.2 元数据与UUID引擎所有需要翻译的地方都会查这个模块。它在初始化时读入settings.js的uuidMap和scriptList然后对外提供三个方法completeUuid(shortUuid)压缩uuid转完整uuidshortUuid(completeUuid)完整uuid转压缩uuid生成meta和写回调脚本时要用scriptNameByUuid(uuid)通过uuid拿类名拿不到就返回占位名生成.meta文件是元数据模块的另一项核心工作。Cocos Creator工程的每个资源旁边都有同名.meta文件里面最重要的字段是uuid。还原出的工程要让Creator认得必须给每个还原出的资源分配稳定uuid。工具的处理策略是能查表还原的就用查到的完整uuid查不到的按规则新生成一个并在输出报告里标注。3.3 脚本还原模块脚本还原模块读入game.js先用js-beautify做代码美化再通过正则与AST扫描做两件事提取cc._RF.push里的uuid、类名、路径信息定位每个类的完整代码块并切割出来。切割逻辑我做了两版。第一版用正则暴力匹配速度快但对装饰器较多、代码嵌套深的类容易切错第二版改用babel解析AST按FunctionDeclaration和ClassDeclaration的边界切分准确率高很多代价是处理大文件时内存占用高。最终工具里默认走AST路线失败回退到正则。这一步的实测经验微信小游戏包的game.js体量通常在1MB到5MB之间AST解析大约几秒到十几秒属于可接受范围。如果你在处理超大项目时感觉慢可以先粗切再精扫不要一上来就全量AST。脚本还原后工具按scriptList里的路径信息把每个脚本写入assets/脚本目录/类名.ts如果原本就是TS开发的或.js。写入时会在文件头部加一行注释标明原始构建来源和还原时间方便日后追溯。3.4 场景与预制体重建模块这是整个工具里最容易出错的地方。场景文件还原不是简单地把JSON拷贝一份而是要做三类转换引用转换把{__id__: N}的数组下标引用保留下来.scene格式本身就靠下标引用可以不变但需要按Creator导入时的规则重排对象顺序否则编辑器会报文件格式错误。压缩uuid替换把自定义脚本组件的__type__从压缩uuid替换成类名。这一步做完的场景才能在编辑器里正确关联脚本并显示组件名。Creator的序列化数据对脚本组件__type__在保存时也会写成类名所以替换为类名反而更接近真正的源工程格式。资源引用路径修正场景里的Texture、SpriteFrame、Animation等资源序列化数据里引用的是uuid或压缩uuid。工具会把这些引用改写成Creator工程资源系统中的路径引用并确保对应资源已经拷贝到还原工程的assets目录下。转换完成后文件按assets/场景目录/场景名.scene的规则写入。Prefab的处理逻辑基本相同统一走同一套转换管线。4. 实操全流程从构建产物到可打开的Creator工程理论讲完下面上实操。以一个微信小游戏构建产物为例完整跑一遍还原流程。4.1 准备阶段需要准备的工具与环境还原工具我选择了Node.js实现理由很简单要解析的game.js、settings.js都是JSNode生态里解析、美化的库都是现成的。你本地需要准备Node.js 14以上版本建议16AST解析对内存有要求解包工具APK用apktool或直接unzipipa用unzipCocos Creator 2.4.x编辑器用于打开还原后的工程操作前先确认你的产物目录结构。微信小游戏构建产物一般长这样wechatgame/ ├── game.js ├── game.json ├── project.config.json ├── src/ │ ├── settings.js │ ├── project.js │ └── assets/ └── assets/ └── ...如果你的产物是APK先解包apktool d game.apk -o game_apk # 或者直接解压只取assets unzip game.apk -d game_unzip解包后进入assets目录你看到的src和assets就是构建产物的核心内容。4.2 一键解析命令与产出物工具安装好后运行方式很简单# 针对微信小游戏产物目录 cocos-restore -i ./wechatgame -o ./restored_project # 针对已解包的APK assets目录 cocos-restore -i ./game_apk/assets -o ./restored_project命令执行过程中工具会在终端打印解析日志[INFO] 引擎版本: 2.4.10 [INFO] 解析 settings.js: 发现 1280 个 uuid 映射 [INFO] 解析 project.js: 脚本总数 86 [INFO] 切割 game.js: 提取脚本类 73 个 [INFO] 还原场景: Main.scene (节点 204 个) [INFO] 还原预制体: Player.prefab (节点 36 个) [INFO] 还原预制体: Bullet.prefab (节点 8 个) [WARN] 3 个脚本未找到对应类名, 已生成占位文件输出目录restored_project结构如下restored_project/ ├── assets/ │ ├── scenes/ │ │ ├── Main.scene │ │ └── Main.scene.meta │ ├── scripts/ │ │ ├── PlayerController.ts │ │ ├── PlayerController.ts.meta │ │ ├── BulletManager.ts │ │ └── ... │ ├── textures/ │ │ ├── bg.png │ │ ├── bg.png.meta │ │ └── ... │ └── resources/ └── project.json这一步值得注意还原出的工程目前只是文件结构正确还没有通过Creator的导入验证下一节会讲打开时遇到的状况。4.3 在Creator 2.4里打开并修复报错打开编辑器后用导入项目选择还原目录。第一次打开几乎必然有报错别慌大部分报错是资源导入顺序导致的。我的经验是按快门保存 重新打开一两轮Editor会把.meta重新索引一遍很多问题会自动消失。常见的几类报错和处理方式脚本类名缺失导致的组件丢失。表现是场景打开后某些节点缺少原先的脚本组件Console里提示Unknown script。这时去assets/scripts下看占位脚本确认是不是工具没能匹配上的那几个类。如果是开发时的类名和文件名不一致导致的识别失败手动改一下占位脚本里的类名在场景里重新挂载组件即可。资源引用断裂。场景里某些图片显示为紫块。通常是纹理的uuid在构建产物里被重新压缩过工具查表时没找到对应完整uuid。解决方式是把assets/scripts里的meta和资源meta一起删除让Creator重新生成然后手动把场景里的SpriteFrame引用拖回去。这种问题多集中在图集类资源。脚本编译报错。还原出的TS脚本可能因为原项目的tsconfig配置、命名空间设置差异在编辑器里编译不过。先查看project.json里的语言版本和构建设置按源项目情况修正。如果原项目是纯JS开发基本不会遇到这个问题。修复完成的标准是场景能正常打开节点树完整脚本组件挂载正确点击预览能跑起来。到这里一套可用的源工程就恢复了。5. 还原质量的三个层级和常见坑不是所有还原都能一次到位。根据还原结果的完整度我把它分成三个层级方便你评估自己项目的还原预期。5.1 层级划分可打开、可运行、可二次开发层级判断标准通常能达到的条件可打开Creator能导入工程场景正常打开无致命报错产物完整settings.js完好场景文件未被篡改可运行编辑器预览出完整游戏交互正常脚本识别率高资源引用完整插件脚本不依赖特殊运行时可二次开发能在还原工程基础上直接改需求、加功能、重新发布脚本类名全部恢复代码可读性高编辑器组件挂载无异常我做过的还原项目里大约八成能达到可运行层级其中一半能顺利进入可二次开发。剩下的情况多半是构建时开启了代码混淆或使用了定制构建插件导致脚本类和场景组件的关联断链。5.2 高频问题清单与处理方案下面这张表是从多次还原实践中总结的高频问题基本覆盖了九成以上的异常情况。问题现象根因处理方案场景里自定义组件全部丢失自定义脚本被构建混淆uuidMap无法匹配打开project.js手动对齐scriptList按组件出现位置补挂脚本文件还原但代码顺序错乱大文件AST切割超时降级到正则边界切错调整内存限制强制走AST或按类名手动从game.js提取图集资源全部失败合并图集在构建时被压缩成大图plist工具没找到plist映射补充图集plist解析逻辑将大图裁回碎图并生成meta场景里节点顺序错乱序列化数组重排规则写错检查数组第一项索引偏移按Creator编辑器实际导入规则重排插件脚本还原后无法运行第三方插件代码依赖编辑器API脱离编辑器后失效工具只提取文件不重组逻辑插件需从官方渠道重装5.3 插件脚本、AssetBundle与分包的特殊处理插件脚本比如原生SDK适配、广告组件在构建产物里和普通脚本不同它们通常以插件脚本标记存在于project.js里代码不被webpack打包直接以单文件形式出现在assets目录下。还原时工具会识别这类文件并原样拷贝不参与类名切割。但如果插件脚本里引用了原生Android/iOS代码那部分是无法从JS产物还原的只能到原生工程目录里找。AssetBundle和主包在2.4里的处理方式也有区别。AssetBundle构建出的bundle目录有自己独立的settings.js里面同样携带着自己的uuidMap。工具在还原时如果遇到bundle会递归调用同样的解析流程把bundle里的资源和脚本也还原出来并在主工程的meta里建立引用关系。分包微信小游戏分包的处理逻辑类似关键在于各个包的settings.js不能被遗漏。6. 工具边界、合规红线与实用建议6.1 哪些项目适合还原哪些不建议折腾经过多次实测我认为适合还原的项目特征很明确Cocos Creator 2.4.x开发、构建时未开启高强度代码混淆、源工程使用的第三方组件不多。这类项目还原成功率最高产出也最有价值。反之这三种情况不建议折腾一是构建时用了自定义的加密插件、对game.js整体加密过的还原难度指数级上升二是项目重度依赖付费插件且插件在构建后功能性失效的还原出来也跑不起来三是只想要素材不想要工程的情况直接去assets目录拿原图更快没必要跑完整还原。6.2 关于合规还原自己拥有的项目这是所有逆向相关话题都绕不开的部分。我的观点很明确工具的使用边界是你对目标产物拥有合法权利包括你自己的项目备份恢复、你所在公司内部的项目资产抢救、经版权方明确授权的学习研究。它不应该被用于解除他人游戏的保护机制、移除广告、偷窃商业素材或代码。我在工具里也做了一层防护还原过程中如果检测到资源或脚本带有明显的外源版权标记或者工程里存在付费插件授权校验代码工具会在日志里明确提示并跳过这部分内容的还原。意料之外的收获是这层防护反而让工具在公司内部推进时少了很多阻力——它天然就是恢复资产的工具而不是抄代码的工具。6.3 后续可扩展方向这套工具目前只是我个人的半成品后续还有很多值得做的方向代码语义恢复从还原出的JS反推更接近TS的写法类型推导、装饰器补全让代码可读性直接对齐源工程开发体验。依赖关系图生成扫描脚本之间的require/import关系自动生成模块依赖树帮开发者快速理解还原工程的结构。资源清理与压缩检测识别构建产物里的冗余资源、引用计数为零的资源给出清理建议这一步对还原后的工程质量很有价值。多版本引擎适配把2.4的解析规则抽象成配置化向后兼容3.x的序列化格式。3.x的管线变化很大目前单独维护一套解析器成本较高但值得投入。最后说一句我的个人经验还原工程这件事最忌讳的就是贪多求全。拿到产物后先不要急着跑工具花半小时把settings.js里的uuidMap、project.js里的scriptList、构建时的引擎版本看清楚能为你省下后面好几个小时的排错时间。工具能自动完成的事情是翻译但关键的决定权始终在你手上——每一份还原成功、能在编辑器里重新打开的工程背后都是你对项目结构本身的理解在起作用。