V7投稿 | 教你加密你的资源Web,Native

为何要加密

  1. 游戏资源容易被盗,简单修改或者不修改直接盗用
  2. 游戏配置一目了然,修改游戏数值制作BT版传播

加密的作用

  1. 提高获取以上信息的门槛

友情提示

  • 该教程基于cocos creator 2.4.9讲解
  • 因为我们的环境避免不了在本地运行,所以理论上无论怎么花式加密,都只是提高破解门槛,并不能保证绝对安全

常见加密算法选择

  • 不可逆加密
    1. MD5
    2. SHA-256
  • 可逆加密
    • 对称加密
      1. DES
      2. AES
      3. XOR
    • 非对称加密
      1. RSA

我们加密后的资源是需要解密出来用的,所以不可逆的加密显然不符合我们需求,所以直接舍弃

XOR加密其实最开始是没有在常见加密算法里面的,我们这边的需求是不希望加密之后文件变的很大,影响下载速度,然后尝试找不改变文件大小的加密算法,这个已经提前交给G哥image找过一次
image

性能对比

接下来我们从可逆加密中进行性能排行,这个交给G哥image 得知 XOR性能也相当不错
image

毫无疑问我们这次选择的是XOR加密

加密代码

  1. 每个平台打包都会生成对应平台的文件夹,所以我们首先要写的就是遍历文件夹中的所有文件。如下
    image
  2. 遍历所有文件时把符合条件(文件后缀)的文件进行加密写入,并在前面加上自定义的签名标识,加密部分交给G哥image
    image

解密代码

web 平台

1. 分析源码

我们以图片为例。通过2.4下载与解析文档得知,文件的下载与解析都是要经过 cc.assetManager.downloader的,我们通过assetManager找到downloaderimage
再跳转到downloader的实现,阅读完源码发现每个不同后缀都会跳转到不同的下载方法里
image
我们再跳转到downloadImage方法,可以看到是有两种不同的方法来下载image的,一种是下载二进制的方式(blob),一种是HTMLImageElement的方式。
image
当然我们现在加密了图片,所以现在我们这两个方法都是不能用的,强行下载下来也会造成解析错误,我们需要定制成我们自己的下载逻辑

2. 重写下载逻辑

因为我们需要对下载下来的数据做分析,所以我们选择arraybuffer作为responseType。恰巧downloader中直接就有这个方法,下载来的arraybuffer我们需要提取前段的数据跟加密时添加的签名标识判断是不是我们加密过的数据。属于我们加密的数据,咱们就对数据进行解密,首先我们把arraybuffer数据进行还原,然后再进行解密,然后再还原为arraybuffer数据。最后使用arraybuffer数据创建blob返回。以下是逻辑代码
image
到这里web平台的解密就完成了。

!!!因为修改了js引擎源码,所以需要删除js引擎的缓存,并重新编译js引擎代码才会生效。js引擎缓存路径【resources\engine\bin\.cache】。重新编译有两种方法,一种是直接重启引擎。一种是 开发者编译引擎(但是不知道为什么我的2.4.9版本这个办法是无效的)。编译之后检查路径【resources\engine\bin\.cache】有没有生成缓存

native 平台

1. 分析源码

我们以图片为例。
原生平台跟web平台不一样,原生平台式内置的资源,所以web所使用的在downloader中下手,再原生并不管用。我们先搜搜在2dx中关于图片的东西
image
搜索出三个,有一个是ios的,里面只有一个saveToFile的方法,对我们意义不大。我们在CCImage.h去找,也就是头文件看一下声明
image
我们找到了这个声明,按照描述的意思是:从指定路径加载图片。
我们再跳转到对应实现
image
看到这个bytes我们就很熟悉了。
image
返回的也正如我们所料,有了这个我们就可以对data进行操作了,解密逻辑其实也是跟downloader这块是一样的,不多说直接放解密代码
image

结语

  1. 以上基于cocos creator 2.4.9讲解,总体来说,通过跟G哥image的通力合作,个人感觉整个加密解密的过程算是比较简单的,中间很多代码都是 “借鉴了G哥image”。
  2. XOR加密的安全性很低,但是也是最简单,速度最快的。其实只要找到了加密解密的地方,加密方法是可选的,可以根据项目需求,替换对应加密,解密的逻辑就行了
  3. 有什么问题,可以在评论区留言,我会尽力回答

夹带点私货

  1. 如果你不想自己手动写这些逻辑,我这边提供现成插件2.4.x Web,Native加密方案(XOR),只要【19.99】源码出售,没有加密,可以随意自行修改,比如你想用你自己的加密方式。
  2. 之前还有开发过一个2.4.x热更新大厅子游戏分包,子游戏支持脚本不重启热更,有需要的可以看看,只要【19.99】同样源码出售,没有加密,可以随意自行修改。

在此声明

  • 之所以源码出售而不加密不是我不会加密,而是方便开发者有不同项目需求方便修改,也方便一些想写插件的的开发者给个参考。
  • 所以也希望购买的各位大佬,高抬贵手,不要私自传播,毕竟这个售价并不需要你多努力,不需要你工资多高,不需要你很好好工作就买的起。
  • 如果发现私自传播比较严重,那我之后的插件,也就只能提高售价门槛并且进行加密了,或者干脆不分享了
7赞

g哥牛的!

大佬 666

灰常实用666

感觉错过了几个亿 cocos creator资源加密(支持web和native)

:+1::+1::+1:

有个思路, 是不是可以hook一下downloader脚本,就不用修改引擎, 重新编译了

:rofl:

如果只是图片的话,确实不用修改引擎,直接用插件的方式替换原方法。但是涉及到json就需要,详情可以看downloader.js中的downloadBundle方法是有使用downloadJson这个方法,downloadBundle方法这个方法的优先级太高了,所以改源码是最稳妥的,并且源码可以先做备份,如果有问题还原测试即可

哈哈哈哈,之前我确实没刷到过你这篇。致敬开源大佬。提个建议:web加密是支持音频的(目前我测试了MP3是支持的),音频(MP3)走的也是downloadArrayBuffer,大佬可以考虑加一下

提供一个思路哈, 我是在main.js里面直接hook了, json, 以及脚本都支持

var failedBundles = [];
    var oldLoadBundle = cc.assetManager.downloader._downloaders["bundle"];
    function hookLoadBundle() {
        const REGEX = /^(?:\w+:\/\/|\.+\/).+/;
        const downloadJson = cc.assetManager.downloader._downloaders[".json"];
        const downloadScript = cc.assetManager.downloader.downloadScript;
        cc.assetManager.downloader._downloaders["bundle"] = function (
            nameOrUrl,
            options,
            onComplete
        ) {
            var bundleName = cc.path.basename(nameOrUrl);
            var url = nameOrUrl;
            if (!REGEX.test(url)) url = "assets/" + bundleName;
            var version = options.version || cc.assetManager.downloader.bundleVers[bundleName];
            var count = 0;
            var config = `${url}/config.${version ? version + "." : ""}json`;
            var out = null,
                error = null;
            downloadJson(config, options, function (err, response) {
                if (err) {
                    error = err;
                    failedBundles.push({
                        name: bundleName,
                        md5: version,
                    });
                } else {
                    progress += 1;
                }
                out = response;
                out && (out.base = url + "/");
                count++;

                onProgress();
                if (count === 2) {
                    onComplete(error, out);
                }
            });

            var js = `${url}/index.${version ? version + "." : ""}js`;

            downloadScript(js, options, function (err, txt) {
                if (err) {
                    error = err;
                    failedBundles.push({
                        name: bundleName,
                        md5: version,
                    });
                } else {
                    progress += 1;
                }
                count++;

                onProgress();
                if (count === 2) {
                    onComplete(error, out);
                }
            });
        };
    }

这也是一种好方法