前言
⚠️ 本文仅为个人学习与技术研究的记录,不涉及任何商业用途。请读者务必遵守相关法律法规,不要将文中方法用于破解、传播或牟利。如需完整体验游戏,请支持正版。
这几天一直在闭关修炼,折腾一堆没见过的新奇玩意,但是不知道发什么文章
直到前几天朋友发了我一款小游戏(🧈)看起来是触摸类型的
需要通过触摸来完成进度条等解锁下一关
以及还有好感度(XY)上升,满了成功,敌方(暂且称呼)观察你触摸时候的两个进度条,上升满了就失败
然后他说:
1
| 哎呀,神奈酱神奈酱~这个游戏太难了能不能帮我看看
|
那很气人了,起初我是不想去搞得,但是又看见个奇怪的加密,似乎市面上都没相关解密方案,看了一圈即便有工具,也没相关教程
那我不就上了吗🫠
那么就以这个游戏:女xx的xx课堂作为例子
获取文件
上手我先是观察了一番,发现dex什么的没啥可以下手的,翻了下assets
发现有几个奇怪东西,没想到是编译后的.jsc文件

加密的jsc |
index.feeb8.jsc |
cocos2d-jsb.d6486.jsc |
index.09c80.jsc |
physics.37b5c.jsc |
settings.a6099.jsc |
哎呀这不就有意思了吗~
接下来,我们通过010Editor
查看这几个jsc,我就先拿其中一个作为例子:

通过cocos2d-jsb.d6486.jsc
我们可以得知这个是使用Cocos Creator来进行开发的游戏,那目标很明确了!
确认目标
接下来我们要实现的几个目标就是
- 对 APK 进行逆向并获取 Key
- 通过 Key 对 JSC 文件进行还原
- 对还原后的 JS 进行修改与数据调整
- 重新封包 JSC 并打包回 APK
实现目标
ok,那么知道了目标,我们先了解一下知识
神奈的芝士小科普~
Cocos Creator是什么?
这一款 游戏开发引擎/IDE,由 Cocos 引擎团队开发。它主要用于 2D/3D 游戏的快速开发,尤其适合做移动端(Android、iOS)、网页(WebGL/HTML5)、PC 平台的小体量游戏。
- Cocos Creator 是基于 Cocos2d-x 演化而来的 完整游戏开发工具。
- 它集成了 场景编辑器、UI 编辑器、资源管理、脚本系统 等功能。
- 使用 JavaScript / TypeScript 编程(以前也支持 Lua),所以对前端开发者比较友好。
开始第一步,获取到so文件并丛中找出key
Cocos Creator的游戏一般会在lib
文件夹里面有libcocos2djs.so


接下来,我们需要通过010Editor
,这个神奇的小妙具!
将libcocos2djs.so
拖入到010Editor
接下来你会看见一堆你看不懂我也看不懂的东西😉总之都看不懂嘿嘿!

然后我们使用神奇小妙招CTRL + F
打开搜索框
我们搜索文本:Cocos Game

你会看见 巴拉巴拉巴拉 呸!
1 2 3
| cocos_jni_env_init.cocos_android_app_init. Cocos Game.cd2d90bd-ad59-40. jsb-adapter/jsb-builtin.js.main.js
|
其中:Cocos Game
后面的cd2d90bd-ad59-40
就是我们解密需要的key
进行第二步通过 Key 对 JSC 文件进行还原
接下来就需要使用由luckyaibin
大佬开发的cocoscreatorjscdecrypt
来进行还原和逆向
首先我们先clone他的仓库
1
| git clone https://github.com/luckyaibin/cocoscreatorjscdecrypt.git
|
clone下来之后,会有这几个目录结构

我们根据readme先对其进行安装需要的东西
1 2
| npm install xxtea-node npm install pako
|
解密
在安装完成所需要的东西之后,我们打开decode.js
可以看到
1 2 3 4 5 6 7 8
| var fs = require("fs"); var path = require("path") var pako = require("pako") var xxtea = require("xxtea-node");
var FILEPATH = path.resolve('./assets'); var KEY = "cd2d90bd-ad59-40" var UNZIP = true
|
这一段神奇代码
在此之前看一下这个原理~
解密和加密原理(可跳过)
点击展开原理
所以,这段代码的用途是 把 Cocos Creator 编译后的加密 .jsc
文件还原成可读的 .js
源码。
2. 执行流程
2.1 遍历文件
- 默认
FILEPATH = ./assets
- 递归调用
fileDisplay(filePath)
- 遍历所有子目录,找到
.jsc
文件后调用 xxteaDecode
。
2.2 解密逻辑
1
| var res = xxtea.decrypt(data, xxtea.toBytes(KEY))
|
2.3 解压逻辑
1 2 3 4
| if (UNZIP) { console.log("开始解压", filename) res = pako.inflate(res) }
|
2.4 写入新文件
1 2
| var newName = filename + ".js" fs.writeFile(newName, res, function(error){ ... })
|
那么根据脚本我们需要在
1 2
| var FILEPATH = path.resolve('./assets'); var KEY = "cd2d90bd-ad59-40"
|
当前目录新建一个assets
的文件夹,并打开decode.js里面的KEY,填入我们逆向后的KEY
接下来我们执行
会出现以下命令行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| PS F:\Reverse Tool\cocoscreatorjscdecrypt> node .\decode.js F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc 开始解压 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc 开始解压 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc 开始解压 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc 开始解压 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc 开始解压 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc.js PS F:\Reverse Tool\cocoscreatorjscdecrypt>
|

之后我们打开asstes就会看到我们解密好的js文件

然后第三步 对还原后的 JS 进行修改与数据调整
我们之前了解到,这个游戏判断
- 👀 视觉进度条:被发现时上升,满格判定失败
- 🔊 声音进度条:触摸时上升,满格判定失败
- ❤️ 爱心进度条:互动时上升,满格则通关
那么我们的目标就是
- 视觉进度条:被发现时上升,满格则无效化(不触发事件)
- 声音进度条:触摸时上升,满格则无效化(不触发事件)
- 爱心进度条:互动时上升,满格则通关(照旧)
- 爱心进度条:互动时上升速度加快,以及可全自动上升
修改数据
我们找到改游戏的具体业务逻辑:index.feeb8.jsc.js
在这个解密后的 .js 文件中,游戏的核心逻辑主要在 update() 方法里:
例子:
1 2 3 4 5 6 7 8 9 10
| if (this.core.lookValue > 100) { this.core.lookValue = 100; this.core.gameOver(false); }
if (this.core.feelValue > 99.5) { this.core.feelValue = 100; this.core.gameOver(true); }
|
在index.feeb8.jsc.js里面找到
1 2
| e.prototype.update = function (t) { var e = this;
|
开头几行之后插入以下内容
1 2 3 4 5 6 7 8
| if (!this.gameover && this.core.feelValue < 100) { this.core.feelValue += 20 * t; if (this.core.feelValue >= 100) { this.core.feelValue = 100; this.playStatus = 2; this.core.gameOver(true); } }
|
但是,你会发现,游戏无法结算了???
问题不big!
我们只需要再动点小手术:
cc.sys.localStorage.setItem("Level", "x")
→ 用于记录解锁进度;
还有可能 scheduleOnce
延迟回调,用于弹出结算、解锁动画等。
在多个 feelValue
满了 的地方都有类似处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| this.core.feelValue = 100; this.playStatus = 2; this.hand.active = !1; this.faceAnim.play("lv1h"); this.soundUtil.stopEffectById(this.cxAudioPlayID); this.soundUtil.playcxEffect(5); this.eye.active = !1; this.eyeActive = !1;
parseInt(cc.sys.localStorage.getItem("Level")) < 2 && cc.sys.localStorage.setItem("Level", "2");
this.scheduleOnce(function () { e.core.reduceFeelValue = !0; e.soundUtil.playHighEffecr(); }, 1);
this.scheduleOnce(function () { e.core.isWatchMode ? e.initItems() : e.core.gameOver(!0); }, 5);
|
然后我们就直接抄!别管这么多,3721就是抄!
80!80!80!
只需要在自动上涨成功时,模仿正常通关的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| if (!this.gameover && this.core.feelValue < 100) { this.core.feelValue += 20 * t; if (this.core.feelValue >= 100) { this.core.feelValue = 100; this.playStatus = 2; this.gameover = true;
if (parseInt(cc.sys.localStorage.getItem("Level")) < 2) { cc.sys.localStorage.setItem("Level", "2"); }
this.hand && (this.hand.active = false); this.eye && (this.eye.active = false); this.eyeActive = false; this.faceAnim && this.faceAnim.play("lv1h"); this.soundUtil.stopEffectById(this.cxAudioPlayID); this.soundUtil.playcxEffect(5);
this.scheduleOnce(() => { this.core.reduceFeelValue = true; this.soundUtil.playHighEffecr(); }, 1);
this.scheduleOnce(() => { this.core.isWatchMode ? this.initItems() : this.core.gameOver(true); }, 5); } }
|
但是里面是还有个画廊,和三个问号的隐藏关卡?
我测你牛魔
正常游戏流程中存在如下语句
1 2 3
| cc.sys.localStorage.setItem("E1", "1"); cc.sys.localStorage.setItem("E2", "2"); cc.sys.localStorage.setItem("E3", "4");
|
要解锁怎么办?
V50KFC即可解锁后续
仍然在 update 函数中插入这段代码即可:
1 2 3 4 5 6 7 8
| cc.sys.localStorage.setItem("Level", "999"); cc.sys.localStorage.setItem("E1", "7"); cc.sys.localStorage.setItem("E2", "7"); cc.sys.localStorage.setItem("E3", "7"); cc.sys.localStorage.setItem("E4", "7"); cc.sys.localStorage.setItem("E5", "7"); cc.sys.localStorage.setItem("E6", "7");
|
有些特殊情况会出现四个update函数
如果你不知道,对不上,就全部注入一遍
而我这里则是
1 2 3 4 5
| e.prototype.update = function (t) { var e = this; if ((1 === this.core.gameStatus || 4 === this.core.gameStatus) && 3 !== this.playStatus) { ...
|
这个主控函数
要是还是不结算?
那我也没招了

最后第四步,重新封包 JSC 并打包回 APK
我们找到crack.js
打开这个文件,还是依旧,修改KEY
1 2 3 4 5 6 7 8 9 10
| const md5File = require('md5-file')
var fs = require("fs"); var path = require("path") var pako = require("pako") var xxtea = require("xxtea-node");
var FILEPATH = path.resolve('./assets'); var KEY = "cd2d90bd-ad59-40" var UNZIP = true
|
修改完成后,我们执行
我们可以得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| PS F:\Reverse Tool\cocoscreatorjscdecrypt> node .\crack.js e invalid F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc.js 加密 开始压缩 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\cocos2d-jsb.d6486.jsc.jsc invalid F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc.js 加密 开始压缩 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.09c80.jsc.jsc invalid F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc.js 加密 开始压缩 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\index.feeb8.jsc.jsc invalid F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc.js 加密 开始压缩 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\physics.37b5c.jsc.jsc invalid F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc.js 加密 开始压缩 F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc.js 写入完毕: F:\Reverse Tool\cocoscreatorjscdecrypt\assets\settings.a6099.jsc.jsc PS F:\Reverse Tool\cocoscreatorjscdecrypt>
|

这样我们就成功打包回去了~
结语
最后我们能看到后缀名为.jsc.jsc
这个就是封回去的~
我们只需要根据apk源目录替换回去即可正常使用
在研究过程中,我们学会了很多,对 Cocos开发的游戏结构的理解,也能积累 逆向分析与调试技巧。
⚠️ 但必须强调:逆向与修改只能用于学习研究,禁止商业用途与非法传播。
要不然…
你进去了我就会
