[已发布][插件] Cocos Creator 构建后 · 代码混淆 3.x+2.x 版 (优惠中)

[已发布][插件] Cocos Creator 构建后 · 代码混淆 3.x+2.x 版 (优惠中)

https://store.cocos.com/app/detail/3740

已支持 2.x 和 3.x 的微信小游戏和抖音小游戏的 AST 形式的不可逆代码混淆,
提高马甲包的过审概率, 但是过审还有人工审核, 运气也占一点点

image
image

开发环境

  • 引擎版本: Cocos 2.x 和 Cocos 3.x
  • 编程语言:Javascript

目前[已测试]平台

请注意 Cocos 2.1.3 版本的插件暂不支持 AST 混淆, 仅支持普通 JS-OB 混淆

微信小游戏 抖音小游戏 H5 Cocos Creator 2.1.3, 2.3.4, 2.4.5, 2.4.9 Cocos Creator 3.4.0, 3.5.2, 3.6.0
:white_check_mark: :white_check_mark: :white_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:

引: 感谢皮皮大佬的支持

技术介绍

  1. 本插件所有代码不加密(有注释)
  2. 也可以自定义一个加密方法(参考我写的cmd文件)
  3. 可混淆类型为 \color{blue}{ wechatgame 、web-mobile 、web-desktop 、bytedance-mini-game } 等 4 种混淆方式, 已增加 \color{red}{微信小游戏} 混淆方式, 已增加 \color{red}{抖音小游戏} 混淆方式
  4. 针对每个参数都进行了详细的讲解
  5. 可以在项目设置里面设置混淆的参数
  6. 可以快捷键打开混淆参数设置面板
  7. 在构建的时候, 可以打开混淆面板设置参数
  8. 针对所有设置的混淆参数都做了多语言处理(支持中英文切换)
  9. 可以在项目里面看到保存混淆参数的 Json 文件(run-time)
  10. 可以混淆构建目录下所有的 JSON 文件(仅有小部分场景适用)
  11. 添加 \color{red}{AST} (抽象语法树) + MD5 \color{red}{不可逆} 代码结构混淆, 增加马甲包过审概率
  12. 使用 AST 保证每次 JS 代码混淆的结果的唯一, 保证混淆后的代码每次都不重复.
  13. 可以手动选择需要混淆的 JS 文件, 并选择需要进行混淆的类型, 然后开启混淆
  14. 添加 2.x 的版本, 2.x 的已测试了三个不同版本的构建 (改写手动选择混淆面板适配 2.x )
  15. 在 2.x 构建后混淆时, 自动启用默认的参数进行混淆, 参数的设置仍旧采取 3.x 的设置规则
  16. 支持 Cocos 2.x 和 Cocos 3.x 全局安装和使用插件

插件安装失败的解决方案

已支持 Cocos 2.x 和 3.x 全局安装使用插件, 建议从此处下载安装
Cocos 编辑器 > 顶部菜单栏 > 商城 > 下载后点击安装


如果提示:

  • 解压失败, 提示安装失败, 需要手动解压时
  • 提示默认 JSON 已丢失

0.1 请注意查看项目文件夹(如下)

请保证解压后的文件目录保持如下
extensions/cc-obfuscated-3_x

cc-obfuscated-3_x 是插件的文件夹名称, 请不要修改, 否则就会提示 JSON 已丢失



0.2 手动解压安装插件的操作步骤(Gif 展示)

0.2.1 解压和安装插件的示意图 1

0.2.2 解压和安装插件的示意图 2

0.2.3 解压和安装插件的示意图(第二种)

就是修改一下压缩包的名称为 cc-obfuscated-3_x.zip , 然后导入插件包

参数配置说明

支持项目构建后自动混淆代码, 目前参数固定采用 cc_obfuscated_js.json 的参数混淆

配置的 JSON 文件位于此处 :> cc-obfuscated-3_x/cc_obfuscated_js.json

如需修改混淆的配置, 修改此 json 文件即可, 参数的示意请参考面板

痛点解决

  • 可自由选择是否在构建后混淆代码
  • 支持 3.x 构建后自动或者不自动混淆代码
  • 支持微信小游戏的代码混淆

目前已测试平台

H5 微信小游戏 抖音小游戏 Android原生 iOS原生 OPPO小游戏 vivo小游戏
:heavy_check_mark: :heavy_check_mark: :heavy_check_mark:

效果预览


使用方法

1. 扫描并启用代码混淆插件

2. 具体参数请参考皮皮大佬的解析

3. 开启或者关闭构建后自动混淆代码的功能

4. 开始构建代码

4.1 微信小游戏混淆演示

4.2 抖音小游戏混淆演示


5. 查看加密的代码文件

6. 查看控制台断点和加密后的效果

7. 关闭混淆功能继续构建

8. 查看未混淆的代码

本插件的目录结构(内置一些个人思路)

cc-obfuscated-3_x
    ├─doc_参考_陈皮皮大佬的
    │  └─preview_html(一个 md 转写的 html)
    ├─myself_混淆
    │  └─我自己写的 cmd 调用 javascript-obfuscator 混淆的代码(请自行修改后缀名)
    ├─node_modules
    │  └─库文件
    ├─panel
    │  ├─面板文件
    └─typings
        └─插件 editor 的 editor.d.ts 文件(适配 3.x 版本)

/extensions/
        /cc-obfuscated-3_x
        │  jsconfig.json
        │  main-panel-manager.js
        │  main.js
        │  obfusDir.txt
        │  package.json
        │  sceneJs.js
        │  
        ├─i18n
        │      en.js
        │      zh.js
        │      
        ├─lib
        │  │  node-fetch.js
        │  │  set_index.js
        │  │  vue.global.prod.js
        │  │  
        │  └─common
        │          config-manager.js
        │          ob_config.json
        │          ob_package.json
        │          
        ├─myself_混淆_cmd
        │      1.npm安装js混淆插件javascript-obfuscator.cmd.txt
        │      2.开始混淆代码_需要自己改Js代码名称.cmd.txt
        │      3.打开替换的文件_内有目录.bat.txt
        │      hunXiao.json
        │      
        ├─node_modules 
        │          
        ├─panel
        │      icon_build.png
        │      index.css
        │      index.html
        │      index.js
        │      index_before.html
        │      README.html
        │      
        ├─panel_build
        │      custom_builder.js
        │      custom_hooks.js
        │      
        ├─runtime_Ts
        │      cc_obfuscated_js.json
        │      cc_obfuscated_js.json.meta
        │      
        ├─typings
        │      editor-main-util.js
        │      editor.d.ts
        │      i18n.js
        │      vue.d.ts
        │      window-util.js
        │      
        └─z_cmd_检查混淆的依赖包的最新信息
                一键更新 · 混淆插件 · 依赖项 npm .cmd
                检查最新的混淆包版本.cmd.txt

鸣谢

  • 感谢皮皮大佬的支持

引用参考

陈皮皮 / Cocos 扩展 - 代码混淆工具
json-obfuscator
md5 + esprima + escodegen + confusion + confuser
陈皮皮 / 一个开源的自动代码混淆插件

后续计划 (免费更新)

  • 优化使用体验
  • 或增或减内容

补充说明

  • 目前的是Beta版本,商用的话请大家酌情考虑
  • 暂未做过各种压测和Bug调测,仅跑通效果
  • 后续功能完善后,价格也会随之提高,感谢支持
  • 如果有发现bug,请联系我,我会尽快修复
  • 感谢各位的支持

购买须知

  • 请各位朋友看后仔细思考,是否符合您的项目或者研究需求,谨慎使用
  • 目前内容相对比较少, 后续才会迭代
  • 因为本产品为付费虚拟商品,一经购买成功概不退款,请在购买谨慎确认购买内容。
3赞

新版到 v1.0.3 (还在审核中), 后续版本准备加入面板来调整属性内容

补充一个在线混淆处理 JS 代码的 \color{red}{JS obfuscator 官网}

大家有需要的可以去自行混淆处理

JS obfuscator 官网

https://obfuscator.io/

image

大图预览

常问问题

为什么要混淆我的 JavaScript 代码?

保护代码是个好主意的原因有很多,例如:

  • 防止任何人简单地复制/粘贴您的工作。这对于 100% 客户端项目尤其重要,例如 HTML5 游戏;
  • 删除不需要的注释和空格。使其加载速度更快,更难理解;
  • 保护尚未支付的工作。您可以向客户展示您的工作,因为他们知道在支付发票之前他们不会拥有源代码。

这个混淆器绝对万无一失吗?

不,虽然不可能恢复确切的原始源代码,但有时间、知识和耐心的人可以对其进行逆向工程。

由于 JavaScript 在浏览器上运行,浏览器的 JavaScript 引擎必须能够读取和解释它,所以没有办法阻止它。任何承诺不诚实的工具。

为什么我的混淆代码比我的原始源代码大?

因为混淆器引入了新的代码片段,旨在保护和防御调试和逆向工程。字符串也被转换为 \xAB 十六进制代码,使事情变得更难理解。您不必太担心代码大小,因为有很多重复,因此您的网络服务器将非常好地压缩混淆代码(如果您在服务器上启用了 GZIP 压缩,现在大多数都这样做)。

我可以在混淆的输出上运行诸如 UglifyJS 或 Google Closure Compiler 之类的压缩程序吗?

不,不建议这样做,在某些情况下它会破坏代码(例如,如果您启用 self-defending )。不过,您可以在之前通过压缩程序运行您的代码,以确保它删除死代码并进行其他优化。

你存储我的源代码吗?

不会。源代码由我们的应用程序服务器处理,然后到混淆器并返回到浏览器,因此它只在我们的服务器内存中停留很短的时间(通常是几毫秒)。

我可以从混淆的源代码中恢复原始源代码吗?

不,不可能将经过混淆的代码恢复为原始代码,因此请妥善保管原始代码。

这个工具是否适用于 Node.js 源代码?

是的。

我想在我自己的服务器/机器上运行混淆器。可能吗?

当然。该工具使用一个免费的开源(BSD-2-Clause 许可)混淆器,用 TypeScript 编写。你可以去它的 GitHub 页面并在那里阅读更多内容。

还有一些插件,例如:webpack-obfuscator、 gulp -javascript-obfuscatorgrunt-contrib-obfuscator

此外,这个网络应用程序也是开源的。查看我们的 GitHub

还有哪些类似的工具?

如果您只对丑化和压缩代码感兴趣,我建议您使用 JSCompress.com

补充

validatejavascript 快速查找和修复 JavaScript 错误

源代码的抽象语法结构的树状表示, 抽象语法树

AST 混淆 || AST Abstract Syntax Tree || 反混淆(部分)

补充-可用选项:

compact

类型: boolean 默认值: true

紧凑的代码输出在一行上。

config

类型: string 默认值:``

包含混淆器选项的 JS/JSON 配置文件的名称。这些将被直接传递给 CLI 的选项覆盖

controlFlowFlattening

类型: boolean 默认值: false

:warning: 此选项极大地影响性能,运行时速度降低 1.5 倍。用于 controlFlowFlatteningThreshold 设置受控制流扁平化影响的节点百分比。

启用代码控制流扁平化。控制流扁平化是一种阻碍程序理解的源代码结构转换。

例子:

// input
(function(){
    function foo () {
        return function () {
            var sum = 1 + 2;
            console.log(1);
            console.log(2);
            console.log(3);
            console.log(4);
            console.log(5);
            console.log(6);
        }
    }
    
    foo()();
})();

// output
(function () {
    function _0x3bfc5c() {
        return function () {
            var _0x3260a5 = {
                'WtABe': '4|0|6|5|3|2|1',
                'GokKo': function _0xf87260(_0x427a8e, _0x43354c) {
                    return _0x427a8e + _0x43354c;
                }
            };
            var _0x1ad4d6 = _0x3260a5['WtABe']['split']('|'), _0x1a7b12 = 0x0;
            while (!![]) {
                switch (_0x1ad4d6[_0x1a7b12++]) {
                case '0':
                    console['log'](0x1);
                    continue;
                case '1':
                    console['log'](0x6);
                    continue;
                case '2':
                    console['log'](0x5);
                    continue;
                case '3':
                    console['log'](0x4);
                    continue;
                case '4':
                    var _0x1f2f2f = _0x3260a5['GokKo'](0x1, 0x2);
                    continue;
                case '5':
                    console['log'](0x3);
                    continue;
                case '6':
                    console['log'](0x2);
                    continue;
                }
                break;
            }
        };
    }

	_0x3bfc5c()();
}());

controlFlowFlatteningThreshold

类型: number 默认值: 0.75 最小值: 0 最大值: 1

controlFlowFlattening 转换将应用于任何给定节点的概率。

此设置对于较大的代码量特别有用,因为大量的控制流转换会减慢您的代码并增加代码量。

controlFlowFlatteningThreshold: 0 等于 controlFlowFlattening: false

deadCodeInjection

类型: boolean 默认值: false

:warning: 显着增加混淆代码的大小(高达 200%),仅在混淆代码的大小无关紧要时使用。用于 deadCodeInjectionThreshold 设置受死代码注入影响的节点百分比。
:warning: 此选项强制启用 stringArray 选项。

使用此选项,死代码的随机块将被添加到混淆代码中。

例子:

// input
(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
        };
        var bar = function () {
            console.log('def');
        };
        var baz = function () {
            console.log('ghi');
        };
        var bark = function () {
            console.log('jkl');
        };
        var hawk = function () {
            console.log('mno');
        };

        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

// output
var _0x37b8 = [
    'YBCtz',
    'GlrkA',
    'urPbb',
    'abc',
    'NMIhC',
    'yZgAj',
    'zrAId',
    'EtyJA',
    'log',
    'mno',
    'jkl',
    'def',
    'Quzya',
    'IWbBa',
    'ghi'
];
function _0x43a7(_0x12cf56, _0x587376) {
    _0x43a7 = function (_0x2f87a8, _0x47eac2) {
        _0x2f87a8 = _0x2f87a8 - (0x16a7 * 0x1 + 0x5 * 0x151 + -0x1c92);
        var _0x341e03 = _0x37b8[_0x2f87a8];
        return _0x341e03;
    };
    return _0x43a7(_0x12cf56, _0x587376);
}
(function () {
    if (!![]) {
        var _0xbbe28f = function () {
            var _0x2fc85f = _0x43a7;
            if (_0x2fc85f(0xaf) === _0x2fc85f(0xae)) {
                _0x1dd94f[_0x2fc85f(0xb2)](_0x2fc85f(0xb5));
            } else {
                console[_0x2fc85f(0xb2)](_0x2fc85f(0xad));
            }
        };
        var _0x5e46bc = function () {
            var _0x15b472 = _0x43a7;
            if (_0x15b472(0xb6) !== _0x15b472(0xaa)) {
                console[_0x15b472(0xb2)](_0x15b472(0xb5));
            } else {
                _0x47eac2[_0x15b472(0xb2)](_0x15b472(0xad));
            }
        };
        var _0x3669e8 = function () {
            var _0x47a442 = _0x43a7;
            if (_0x47a442(0xb7) !== _0x47a442(0xb0)) {
                console[_0x47a442(0xb2)](_0x47a442(0xb8));
            } else {
                _0x24e0bf[_0x47a442(0xb2)](_0x47a442(0xb3));
            }
        };
        var _0x28b05a = function () {
            var _0x497902 = _0x43a7;
            if (_0x497902(0xb1) === _0x497902(0xb1)) {
                console[_0x497902(0xb2)](_0x497902(0xb4));
            } else {
                _0x59c9c6[_0x497902(0xb2)](_0x497902(0xb4));
            }
        };
        var _0x402a54 = function () {
            var _0x1906b7 = _0x43a7;
            if (_0x1906b7(0xab) === _0x1906b7(0xac)) {
                _0xb89cd0[_0x1906b7(0xb2)](_0x1906b7(0xb8));
            } else {
                console[_0x1906b7(0xb2)](_0x1906b7(0xb3));
            }
        };
        _0xbbe28f();
        _0x5e46bc();
        _0x3669e8();
        _0x28b05a();
        _0x402a54();
    }
}());

deadCodeInjectionThreshold

类型: number 默认值: 0.4 最小值: 0 最大值: 1

允许设置受 deadCodeInjection .

debugProtection

类型: boolean 默认值: false

:warning: 如果您打开开发者工具,可能会冻结您的浏览器。

此选项几乎无法使用 debugger 开发工具的功能(基于 WebKit 和 Mozilla Firefox)。

debugProtectionInterval

类型: number 默认值: 0

:warning: 可以冻结你的浏览器!使用风险自负。

如果设置,则以毫秒为单位的间隔用于强制控制台选项卡上的调试模式,从而更难使用开发人员工具的其他功能。如果 debugProtection 启用则工作。推荐值介于 20004000 毫秒之间。

disableConsoleOutput

类型: boolean 默认值: false

:warning: 此选项 console 全局禁用所有脚本的调用

禁用 console.log , console.info , console.error , console.warn , console.debug , console.exceptionconsole.trace 用空函数替换它们。这使得调试器的使用更加困难。

domainLock

类型: string[] 默认值: []

:warning: 此选项不适用于 target: 'node'

允许仅在特定域和/或子域上运行经过混淆的源代码。这使得人们很难复制和粘贴您的源代码并在其他地方运行它。

如果源代码未在此选项指定的域上运行,浏览器将被重定向到传递给 domainLockRedirectUrl 选项的 URL。

多个域和子域

可以将您的代码锁定到多个域或子域。例如,将其锁定以使代码仅在 www.example.com add上运行 www.example.com 。要使其在包括任何子域 ( example.com , sub.example.com ) 的根域上工作,请使用 .example.com .

domainLockRedirectUrl

类型: string 默认值: about:blank

:warning: 此选项不适用于 target: 'node'

如果源代码未在指定的域上运行,则允许将浏览器重定向到传递的 URL domainLock

exclude

类型: string[] 默认值: []

指示要从混淆中排除的文件的文件名或 glob。

forceTransformStrings

类型: string[] 默认值: []

启用字符串文字的强制转换,它与传递的 RegExp 模式匹配。

:warning: 此选项仅影响不应转换的字符串 stringArrayThreshold (或将来可能的其他阈值)

该选项优先于 reservedStrings 选项,但没有优先于 conditional comments .

例子:

	{
		forceTransformStrings: [
			'some-important-value',
			'some-string_\d'
		]
	}

identifierNamesCache

类型: Object | null 默认值: null

此选项的主要目标是能够在混淆多个源/文件期间使用相同的标识符名称。

目前支持两种类型的标识符:

  • 全局标识符:
    • 所有全局标识符都将写入缓存;
    • 所有匹配的 未声明 的全局标识符将被缓存中的值替换。
  • 属性标识符,仅在 renameProperties 启用选项时:
    • 所有属性标识符都将写入缓存;
    • 所有匹配的属性标识符都将被缓存中的值替换。

Node.js API

如果 null 传递了一个值,则完全禁用缓存。

如果传递了一个空对象 ( {} ),则启用将标识符名称写入缓存对象 ( TIdentifierNamesCache type)。这个缓存对象将通过对象的 getIdentifierNamesCache 方法调用来访问 ObfuscationResult

生成的缓存对象接下来可以用作 identifierNamesGenerator 选项值,以便在对下一个源的所有匹配标识符名称进行混淆期间使用这些名称。

例子:

const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
    `
        function foo(arg) {
           console.log(arg)
        }
        
        function bar() {
            var bark = 2;
        }
    `,
    {
        compact: false,
        identifierNamesCache: {},
        renameGlobals: true
    }
)

console.log(source1ObfuscationResult.getIdentifierNamesCache());
/*
    { 
        globalIdentifiers: {
            foo: '_0x5de86d',
            bar: '_0x2a943b'
        }
    }
*/



const source2ObfuscationResult = JavaScriptObfuscator.obfuscate(
    `
        // Expecting that these global functions are defined in another obfuscated file
        foo(1);
        bar();
        
        // Expecting that this global function is defined in third-party package
        baz();
    `,
    {
        compact: false,
        identifierNamesCache: source1ObfuscationResult.getIdentifierNamesCache(),
        renameGlobals: true
    }
)

console.log(source2ObfuscationResult.getObfuscatedCode());
/*
    _0x5de86d(0x1);
    _0x2a943b();
    baz();
 */

命令行界面

CLI 有一个不同的选项 --identifier-names-cache-path ,允许定义 .json 将用于读取和写入标识符名称缓存的现有文件的路径。

如果将传递空文件的路径 - 标识符名称缓存将被写入该文件。

这个具有现有缓存的文件可以再次用作 --identifier-names-cache-path 选项值,以便在对下一个文件的所有匹配标识符名称进行混淆时使用这些名称。

identifierNamesGenerator

类型: string 默认值: hexadecimal

设置标识符名称生成器。

可用值:

  • dictionary : identifiersDictionary 列表中的标识符名称
  • hexadecimal : 标识符名称如 _0xabc123
  • mangled : 短标识符名称,如 a , b , c
  • mangled-shuffled : 相同, mangled 但使用混洗字母

identifiersDictionary

类型: string[] 默认值: []

identifierNamesGenerator 为: dictionary 选项设置标识符字典。字典中的每个标识符都将用于几个变体,每个字符的大小写不同。因此,字典中标识符的数量应该取决于原始源代码中的标识符数量。

identifiersPrefix

类型: string 默认值: ''

为所有全局标识符设置前缀。

当您想要混淆多个文件时使用此选项。此选项有助于避免这些文件的全局标识符之间的冲突。每个文件的前缀应该不同。

ignoreRequireImports

类型: boolean 默认值: false

防止混淆 require 导入。在某些情况下,当出于某种原因运行时环境仅需要使用静态字符串进行这些导入时,可能会有所帮助。

inputFileName

类型: string 默认值: ''

允许使用源代码设置输入文件的名称。此名称将在内部用于源映射生成。使用 NodeJS API 并且 sourceMapSourcesMode 选项具有值时需要 sources

log

类型: boolean 默认值: false

允许将信息记录到控制台。

numbersToExpressions

类型: boolean 默认值: false

启用数字转换为表达式

例子:

// input
const foo = 1234;

// output
const foo=-0xd93+-0x10b4+0x41*0x67+0x84e*0x3+-0xff8;

optionsPreset

类型: string 默认值: default

允许设置选项预设

可用值:

  • default ;
  • low-obfuscation ;
  • medium-obfuscation ;
  • high-obfuscation .

所有添加选项将与选定的选项预设合并。

renameGlobals

类型: boolean 默认值: false

:warning: 此选项可能会破坏您的代码。仅当您知道它的作用时才启用它!

使用声明 启用对全局变量和函数名称的混淆。

renameProperties

类型: boolean 默认值: false

:warning: 此选项 可能 会破坏您的代码。仅当您知道它的作用时才启用它!

启用属性名称的重命名。所有内置的 DOM 属性和核心 JavaScript 类中的属性都将被忽略。

要在此选项的模式之间切换 safeunsafe 请使用 renamePropertiesMode 选项。

要设置重命名属性名称的格式,请使用 identifierNamesGenerator 选项。

要控制哪些属性将被重命名,请使用 reservedNames 选项。

例子:

// input
(function () {
    const foo = {
        prop1: 1,
        prop2: 2,
        calc: function () {
            return this.prop1 + this.prop2;
        }
    };
    
    console.log(foo.calc());
})();

// output
(function () {
    const _0x46529b = {
        '_0x10cec7': 0x1,
        '_0xc1c0ca': 0x2,
        '_0x4b961d': function () {
            return this['_0x10cec7'] + this['_0xc1c0ca'];
        }
    };
    console['log'](_0x46529b['_0x4b961d']());
}());

renamePropertiesMode

类型: string 默认值: safe

:warning: 即使在 safe 模式下, renameProperties 选项也 可能 会破坏您的代码。

指定 renameProperties 选项模式:

  • safe - 发布后的默认行为 2.11.0 。尝试以更安全的方式重命名属性以防止运行时错误。在此模式下,某些属性将被排除在重命名之外。
  • unsafe - 发布前的默认行为 2.11.0 。以不安全的方式重命名属性,没有任何限制。

如果一个文件正在使用其他文件的属性,请使用 identifierNamesCache 选项在这些文件之间保持相同的属性名称。

reservedNames

类型: string[] 默认值: []

禁用与传递的 RegExp 模式匹配的标识符的混淆和生成。

例子:

	{
		reservedNames: [
			'^someVariable',
			'functionParameter_\d'
		]
	}

reservedStrings

类型: string[] 默认值: []

禁用与传递的 RegExp 模式匹配的字符串文字的转换。

例子:

	{
		reservedStrings: [
			'react-native',
			'\.\/src\/test',
			'some-string_\d'
		]
	}

seed

类型: string|number 默认值: 0

此选项为随机生成器设置种子。这对于创建可重复的结果很有用。

如果种子是 0 - 随机生成器将在没有种子的情况下工作。

selfDefending

类型: boolean 默认值: false

:warning: 使用此选项进行混淆后,请勿以任何方式更改混淆代码,因为任何像丑化代码这样的更改都会触发自我防御,代码将不再起作用!
:warning: 此选项强制将 compact 值设置为 true

此选项使输出代码对格式化和变量重命名具有弹性。如果有人试图在混淆后的代码上使用 JavaScript 美化器,代码将不再工作,从而使其更难理解和修改。

simplify

类型: boolean 默认值: true

通过简化启用额外的代码混淆。

:warning: 在未来的版本中, boolean 文字混淆 ( true => !![] ) 将移至此选项下。

例子:

// input
if (condition1) {
    const foo = 1;
    const bar = 2;
  
    console.log(foo);
  
    return bar;
} else if (condition2) {
    console.log(1);
    console.log(2);
    console.log(3);
  
    return 4;
} else {
    return 5;
}

// output
if (condition1) {
    const foo = 0x1, bar = 0x2;
    return console['log'](foo), bar;
} else
    return condition2 ? (console['log'](0x1), console['log'](0x2), console['log'](0x3), 0x4) : 0x5;

sourceMap

类型: boolean 默认值: false

为混淆代码启用源映射生成。

源映射可以帮助您调试混淆的 JavaScript 源代码。如果您想要或需要在生产中进行调试,您可以将单独的源映射文件上传到一个秘密位置,然后将您的浏览器指向那里。

sourceMapBaseUrl

类型: string 默认值:``

sourceMapMode: 'separate' .

命令行示例:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000'

结果:

//# sourceMappingURL=http://localhost:9000/out.js.map

sourceMapFileName

类型: string 默认值:``

设置输出源映射的文件名 sourceMapMode: 'separate'

命令行示例:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000' --source-map-file-name example

结果:

//# sourceMappingURL=http://localhost:9000/example.js.map

sourceMapMode

类型: string 默认值: separate

指定源映射生成模式:

  • inline - 在每个 .js 文件的末尾添加源映射;
  • separate - 使用源地图生成相应的“.map”文件。如果您通过 CLI 运行混淆器 - 将源映射文件的链接添加到带有混淆代码的文件末尾 //# sourceMappingUrl=file.js.map

sourceMapSourcesMode

类型: string 默认值: sources-content

允许控制源地图的字段 sourcessourcesContent

  • sources-content - 添加 dummy sources 字段, sourcesContent 使用原始源代码添加字段;
  • sources - 添加 sources 具有有效来源描述的字段,不添加 sourcesContent 字段。使用 NodeJS API 时,需要定义 inputFileName 将用作 sources 字段值的选项。

splitStrings

类型: boolean 默认值: false

将文字字符串拆分为具有 splitStringsChunkLength 选项值长度的块。

例子:

// input
(function(){
    var test = 'abcdefg';
})();

// output
(function(){
    var _0x5a21 = 'ab' + 'cd' + 'ef' + 'g';
})();

splitStringsChunkLength

类型: number 默认值: 10

设置选项的块长度 splitStrings

stringArray

类型: boolean 默认值: true

删除字符串文字并将它们放在一个特殊的数组中。例如,字符串 "Hello World" in var m = "Hello World"; 将被替换为类似 var m = _0x12c456[0x1];

stringArrayCallsTransform

类型: boolean 默认值: false

:warning: stringArray 选项必须启用

启用对 stringArray . 这些调用的所有参数都可以根据 stringArrayCallsTransformThreshold 值提取到不同的对象。因此,自动查找对字符串数组的调用变得更加困难。

例子:

function foo() {
    var k = {
        c: 0x2f2,
        d: '0x396',
        e: '0x397',
        f: '0x39a',
        g: '0x39d',
        h: 0x398,
        l: 0x394,
        m: '0x39b',
        n: '0x39f',
        o: 0x395,
        p: 0x395,
        q: 0x399,
        r: '0x399'
    };
    var c = i(k.d, k.e);
    var d = i(k.f, k.g);
    var e = i(k.h, k.l);
    var f = i(k.m, k.n);
    function i(c, d) {
        return b(c - k.c, d);
    }
    var g = i(k.o, k.p);
    var h = i(k.q, k.r);
}
function j(c, d) {
    var l = { c: 0x14b };
    return b(c - -l.c, d);
}
console[j(-'0xa6', -'0xa6')](foo());
function b(c, d) {
    var e = a();
    b = function (f, g) {
        f = f - 0xa3;
        var h = e[f];
        return h;
    };
    return b(c, d);
}
function a() {
    var m = [
        'string5',
        'string1',
        'log',
        'string3',
        'string6',
        'string2',
        'string4'
    ];
    a = function () {
        return m;
    };
    return a();
}

stringArrayCallsTransformThreshold

类型: number 默认值: 0.5

:warning:必须启用 stringArray 选项 stringArrayCallsTransformThreshold

您可以使用此设置来调整对字符串数组的调用将被转换的概率(从 0 到 1)。

stringArrayEncoding

类型: string[] 默认值: []

:warning: stringArray 选项必须启用

此选项会减慢您的脚本速度。

stringArray 对using base64 or的所有字符串文字进行编码, rc4 并插入用于在运行时对其进行解码的特殊代码。

每个 stringArray 值都将通过从传递列表中随机选择的编码进行编码。这使得使用多种编码成为可能。

可用值:

  • 'none' ( boolean ): 不编码 stringArray
  • 'base64' ( string ): 使用编码 stringArraybase64
  • 'rc4' ( ):使用 对值进行 string 编码。 比 慢约 30-50% ,但更难获得初始值。 建议在使用编码时禁用选项,以防止混淆代码非常大。 stringArray rc4 base64 unicodeEscapeSequence rc4

例如,对于以下选项值,某些 stringArray 值将不会被编码,而某些值将使用 base64rc4 encoding 进行编码:

stringArrayEncoding: [
    'none',
    'base64',
    'rc4'
]

stringArrayIndexesType

类型: string[] 默认值: ['hexadecimal-number']

:warning: stringArray 选项必须启用

允许控制字符串数组调用索引的类型。

每个 stringArray 调用索引都将通过从传递列表中随机选择的类型进行转换。这使得使用多种类型成为可能。

可用值:

  • 'hexadecimal-number' ( default ): 将字符串数组调用索引转换为十六进制数
  • 'hexadecimal-numeric-string' : 将字符串数组调用索引转换为十六进制数字字符串

2.9.0 发布之前 javascript-obfuscator 将所有字符串数组调用索引转换为 hexadecimal-numeric-string 类型。这使得一些手动反混淆稍微困难一些,但它允许通过自动反混淆器轻松检测这些调用。

hexadecimal-number 类型方法使代码中的字符串数组调用模式的自动检测更加困难。

将来会添加更多类型。

stringArrayIndexShift

类型: boolean 默认值: true

:warning: stringArray 选项必须启用

为所有字符串数组调用启用额外的索引移位

stringArrayRotate

类型: boolean 默认值: true

:warning: stringArray 必须启用

stringArray 数组移动固定和随机(在代码混淆处生成)位置。这使得将删除字符串的顺序与其原始位置匹配变得更加困难。

stringArrayShuffle

类型: boolean 默认值: true

:warning: stringArray 必须启用

随机打乱 stringArray 数组项。

stringArrayWrappersCount

类型: number 默认值: 1

:warning: stringArray 选项必须启用

string array 设置每个根或函数范围内的包装器计数。每个范围内的实际包装器数量 literal 受此范围内节点数的限制。

例子:

// Input
const foo = 'foo';
const bar = 'bar';
        
function test () {
    const baz = 'baz';
    const bark = 'bark';
    const hawk = 'hawk';
}

const eagle = 'eagle';

// Output, stringArrayWrappersCount: 5
const _0x3f6c = [
    'bark',
    'bar',
    'foo',
    'eagle',
    'hawk',
    'baz'
];
const _0x48f96e = _0x2e13;
const _0x4dfed8 = _0x2e13;
const _0x55e970 = _0x2e13;
function _0x2e13(_0x33c4f5, _0x3f6c62) {
    _0x2e13 = function (_0x2e1388, _0x60b1e) {
        _0x2e1388 = _0x2e1388 - 0xe2;
        let _0x53d475 = _0x3f6c[_0x2e1388];
        return _0x53d475;
    };
    return _0x2e13(_0x33c4f5, _0x3f6c62);
}
const foo = _0x48f96e(0xe4);
const bar = _0x4dfed8(0xe3);
function test() {
    const _0x1c262f = _0x2e13;
    const _0x54d7a4 = _0x2e13;
    const _0x5142fe = _0x2e13;
    const _0x1392b0 = _0x1c262f(0xe7);
    const _0x201a58 = _0x1c262f(0xe2);
    const _0xd3a7fb = _0x1c262f(0xe6);
}
const eagle = _0x48f96e(0xe5);

stringArrayWrappersChainedCalls

类型: boolean 默认值: true

:warning:必须启用 stringArray 选项 stringArrayWrappersCount

启用包装器之间的链接调用 string array

例子:

// Input
const foo = 'foo';
const bar = 'bar';
        
function test () {
    const baz = 'baz';
    const bark = 'bark';

    function test1() {
        const hawk = 'hawk';
        const eagle = 'eagle';
    } 
}

// Output, stringArrayWrappersCount: 5, stringArrayWrappersChainedCalls: true
const _0x40c2 = [
    'bar',
    'bark',
    'hawk',
    'eagle',
    'foo',
    'baz'
];
const _0x31c087 = _0x3280;
const _0x31759a = _0x3280;
function _0x3280(_0x1f52ee, _0x40c2a2) {
    _0x3280 = function (_0x3280a4, _0xf07b02) {
        _0x3280a4 = _0x3280a4 - 0x1c4;
        let _0x57a182 = _0x40c2[_0x3280a4];
        return _0x57a182;
    };
    return _0x3280(_0x1f52ee, _0x40c2a2);
}
const foo = _0x31c087(0x1c8);
const bar = _0x31c087(0x1c4);
function test() {
    const _0x848719 = _0x31759a;
    const _0x2693bf = _0x31c087;
    const _0x2c08e8 = _0x848719(0x1c9);
    const _0x359365 = _0x2693bf(0x1c5);
    function _0x175e90() {
        const _0x310023 = _0x848719;
        const _0x2302ef = _0x2693bf;
        const _0x237437 = _0x310023(0x1c6);
        const _0x56145c = _0x310023(0x1c7);
    }
}

stringArrayWrappersParametersMaxCount

类型: number 默认值: 2

:warning: stringArray 选项必须启用
:warning: 目前此选项仅影响由选项值添加的包装器 stringArrayWrappersType function

允许控制字符串数组包装器参数的最大数量。默认值和最小值是 22 和之间的推荐值 5

stringArrayWrappersType

类型: string 默认值: variable

:warning:必须启用 stringArray 选项 stringArrayWrappersCount

stringArrayWrappersCount 允许选择由选项附加的包装类型。

可用值:

  • 'variable' : 在每个作用域的顶部附加变量包装器。快速的性能。
  • 'function' : 在每个范围内的随机位置附加函数包装器。性能比 with 慢, variable 但提供更严格的混淆。

function 当性能损失对混淆应用程序没有太大影响时,强烈建议使用包装器进行更高的混淆。

'function' 期权价值示例:

// input
const foo = 'foo';

function test () {
    const bar = 'bar';
    console.log(foo, bar);
}

test();

// output
const a = [
    'log',
    'bar',
    'foo'
];
const foo = d(0x567, 0x568);
function b(c, d) {
    b = function (e, f) {
        e = e - 0x185;
        let g = a[e];
        return g;
    };
    return b(c, d);
}
function test() {
    const c = e(0x51c, 0x51b);
    function e (c, g) {
        return b(c - 0x396, g);
    }
    console[f(0x51b, 0x51d)](foo, c);
    function f (c, g) {
        return b(c - 0x396, g);
    }
}
function d (c, g) {
    return b(g - 0x3e1, c);
}
test();

stringArrayThreshold

类型: number 默认值: 0.8 最小值: 0 最大值: 1

:warning: stringArray 选项必须启用

您可以使用此设置来调整将字符串文字插入到 stringArray .

此设置对于大代码量特别有用,因为它反复调用 string array 并且会减慢您的代码。

stringArrayThreshold: 0 等于 stringArray: false

target

类型: string 默认值: browser

允许为混淆代码设置目标环境。

可用值:

  • browser ;
  • browser-no-eval ;
  • node .

browser 当前和目标的输出代码 node 是相同的,但是某些特定于浏览器的选项不允许与 node 目标一起使用。目标的输出代码 browser-no-eval 未使用 eval .

transformObjectKeys

类型: boolean 默认值: false

启用对象键的转换。

例子:

// input
(function(){
    var object = {
        foo: 'test1',
        bar: {
            baz: 'test2'
        }
    };
})();

// output
var _0x4735 = [
    'foo',
    'baz',
    'bar',
    'test1',
    'test2'
];
function _0x390c(_0x33d6b6, _0x4735f4) {
    _0x390c = function (_0x390c37, _0x1eed85) {
        _0x390c37 = _0x390c37 - 0x198;
        var _0x2275f8 = _0x4735[_0x390c37];
        return _0x2275f8;
    };
    return _0x390c(_0x33d6b6, _0x4735f4);
}
(function () {
    var _0x17d1b7 = _0x390c;
    var _0xc9b6bb = {};
    _0xc9b6bb[_0x17d1b7(0x199)] = _0x17d1b7(0x19c);
    var _0x3d959a = {};
    _0x3d959a[_0x17d1b7(0x198)] = _0x17d1b7(0x19b);
    _0x3d959a[_0x17d1b7(0x19a)] = _0xc9b6bb;
    var _0x41fd86 = _0x3d959a;
}());

unicodeEscapeSequence

类型: boolean 默认值: false

允许启用/禁用字符串转换为 unicode 转义序列。

Unicode 转义序列大大增加了代码大小,并且可以轻松地将字符串恢复为其原始视图。建议仅对小型源代码启用此选项。

预设选项

高混淆,低性能

性能会比没有混淆的时候慢很多

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 1,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 1,
    debugProtection: true,
    debugProtectionInterval: 4000,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: true,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: true,
    splitStringsChunkLength: 5,
    stringArray: true,
    stringArrayCallsTransform: true,
    stringArrayEncoding: ['rc4'],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 5,
    stringArrayWrappersChainedCalls: true,    
    stringArrayWrappersParametersMaxCount: 5,
    stringArrayWrappersType: 'function',
    stringArrayThreshold: 1,
    transformObjectKeys: true,
    unicodeEscapeSequence: false
}

中等混淆,最佳性能

性能会比没有混淆的时候慢

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: true,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: true,
    splitStringsChunkLength: 10,
    stringArray: true,
    stringArrayCallsTransform: true,
    stringArrayCallsTransformThreshold: 0.75,
    stringArrayEncoding: ['base64'],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 2,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 4,
    stringArrayWrappersType: 'function',
    stringArrayThreshold: 0.75,
    transformObjectKeys: true,
    unicodeEscapeSequence: false
}

低混淆,高性能

表现将处于相对正常的水平

{
    compact: true,
    controlFlowFlattening: false,
    deadCodeInjection: false,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: false,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: false,
    stringArray: true,
    stringArrayCallsTransform: false,
    stringArrayEncoding: [],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 1,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 2,
    stringArrayWrappersType: 'variable',
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

默认预设,高性能

{
    compact: true,
    controlFlowFlattening: false,
    deadCodeInjection: false,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: false,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: false,
    renameGlobals: false,
    selfDefending: false,
    simplify: true,
    splitStrings: false,
    stringArray: true,
    stringArrayCallsTransform: false,
    stringArrayCallsTransformThreshold: 0.5,
    stringArrayEncoding: [],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 1,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 2,
    stringArrayWrappersType: 'variable',
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

Add + Available Options:

compact

Type: boolean Default: true

Compact code output on one line.

config

Type: string Default: ``

Name of JS/JSON config file which contains obfuscator options. These will be overridden by options passed directly to CLI

controlFlowFlattening

Type: boolean Default: false

:warning: This option greatly affects the performance up to 1.5x slower runtime speed. Use controlFlowFlatteningThreshold to set percentage of nodes that will affected by control flow flattening.

Enables code control flow flattening. Control flow flattening is a structure transformation of the source code that hinders program comprehension.

Example:

// input
(function(){
    function foo () {
        return function () {
            var sum = 1 + 2;
            console.log(1);
            console.log(2);
            console.log(3);
            console.log(4);
            console.log(5);
            console.log(6);
        }
    }
    
    foo()();
})();

// output
(function () {
    function _0x3bfc5c() {
        return function () {
            var _0x3260a5 = {
                'WtABe': '4|0|6|5|3|2|1',
                'GokKo': function _0xf87260(_0x427a8e, _0x43354c) {
                    return _0x427a8e + _0x43354c;
                }
            };
            var _0x1ad4d6 = _0x3260a5['WtABe']['split']('|'), _0x1a7b12 = 0x0;
            while (!![]) {
                switch (_0x1ad4d6[_0x1a7b12++]) {
                case '0':
                    console['log'](0x1);
                    continue;
                case '1':
                    console['log'](0x6);
                    continue;
                case '2':
                    console['log'](0x5);
                    continue;
                case '3':
                    console['log'](0x4);
                    continue;
                case '4':
                    var _0x1f2f2f = _0x3260a5['GokKo'](0x1, 0x2);
                    continue;
                case '5':
                    console['log'](0x3);
                    continue;
                case '6':
                    console['log'](0x2);
                    continue;
                }
                break;
            }
        };
    }

	_0x3bfc5c()();
}());

controlFlowFlatteningThreshold

Type: number Default: 0.75 Min: 0 Max: 1

The probability that the controlFlowFlattening transformation will be applied to any given node.

This setting is especially useful for large code size because large amounts of control flow transformations can slow down your code and increase code size.

controlFlowFlatteningThreshold: 0 equals to controlFlowFlattening: false .

deadCodeInjection

Type: boolean Default: false

:warning: Dramatically increases size of obfuscated code (up to 200%), use only if size of obfuscated code doesn’t matter. Use deadCodeInjectionThreshold to set percentage of nodes that will affected by dead code injection.
:warning: This option forcibly enables stringArray option.

With this option, random blocks of dead code will be added to the obfuscated code.

Example:

// input
(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
        };
        var bar = function () {
            console.log('def');
        };
        var baz = function () {
            console.log('ghi');
        };
        var bark = function () {
            console.log('jkl');
        };
        var hawk = function () {
            console.log('mno');
        };

        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

// output
var _0x37b8 = [
    'YBCtz',
    'GlrkA',
    'urPbb',
    'abc',
    'NMIhC',
    'yZgAj',
    'zrAId',
    'EtyJA',
    'log',
    'mno',
    'jkl',
    'def',
    'Quzya',
    'IWbBa',
    'ghi'
];
function _0x43a7(_0x12cf56, _0x587376) {
    _0x43a7 = function (_0x2f87a8, _0x47eac2) {
        _0x2f87a8 = _0x2f87a8 - (0x16a7 * 0x1 + 0x5 * 0x151 + -0x1c92);
        var _0x341e03 = _0x37b8[_0x2f87a8];
        return _0x341e03;
    };
    return _0x43a7(_0x12cf56, _0x587376);
}
(function () {
    if (!![]) {
        var _0xbbe28f = function () {
            var _0x2fc85f = _0x43a7;
            if (_0x2fc85f(0xaf) === _0x2fc85f(0xae)) {
                _0x1dd94f[_0x2fc85f(0xb2)](_0x2fc85f(0xb5));
            } else {
                console[_0x2fc85f(0xb2)](_0x2fc85f(0xad));
            }
        };
        var _0x5e46bc = function () {
            var _0x15b472 = _0x43a7;
            if (_0x15b472(0xb6) !== _0x15b472(0xaa)) {
                console[_0x15b472(0xb2)](_0x15b472(0xb5));
            } else {
                _0x47eac2[_0x15b472(0xb2)](_0x15b472(0xad));
            }
        };
        var _0x3669e8 = function () {
            var _0x47a442 = _0x43a7;
            if (_0x47a442(0xb7) !== _0x47a442(0xb0)) {
                console[_0x47a442(0xb2)](_0x47a442(0xb8));
            } else {
                _0x24e0bf[_0x47a442(0xb2)](_0x47a442(0xb3));
            }
        };
        var _0x28b05a = function () {
            var _0x497902 = _0x43a7;
            if (_0x497902(0xb1) === _0x497902(0xb1)) {
                console[_0x497902(0xb2)](_0x497902(0xb4));
            } else {
                _0x59c9c6[_0x497902(0xb2)](_0x497902(0xb4));
            }
        };
        var _0x402a54 = function () {
            var _0x1906b7 = _0x43a7;
            if (_0x1906b7(0xab) === _0x1906b7(0xac)) {
                _0xb89cd0[_0x1906b7(0xb2)](_0x1906b7(0xb8));
            } else {
                console[_0x1906b7(0xb2)](_0x1906b7(0xb3));
            }
        };
        _0xbbe28f();
        _0x5e46bc();
        _0x3669e8();
        _0x28b05a();
        _0x402a54();
    }
}());

deadCodeInjectionThreshold

Type: number Default: 0.4 Min: 0 Max: 1

Allows to set percentage of nodes that will affected by deadCodeInjection .

debugProtection

Type: boolean Default: false

:warning: Can freeze your browser if you open the Developer Tools.

This option makes it almost impossible to use the debugger function of the Developer Tools (both on WebKit-based and Mozilla Firefox).

debugProtectionInterval

Type: number Default: 0

:warning: Can freeze your browser! Use at own risk.

If set, an interval in milliseconds is used to force the debug mode on the Console tab, making it harder to use other features of the Developer Tools. Works if debugProtection is enabled. Recommended value is between 2000 and 4000 milliseconds.

disableConsoleOutput

Type: boolean Default: false

:warning: This option disables console calls globally for all scripts

Disables the use of console.log , console.info , console.error , console.warn , console.debug , console.exception and console.trace by replacing them with empty functions. This makes the use of the debugger harder.

domainLock

Type: string[] Default: []

:warning: This option does not work with target: 'node'

Allows to run the obfuscated source code only on specific domains and/or sub-domains. This makes really hard for someone to just copy and paste your source code and run it elsewhere.

If the source code isn’t run on the domains specified by this option, the browser will be redirected to a passed to the domainLockRedirectUrl option URL.

Multiple domains and sub-domains

It’s possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on www.example.com add www.example.com . To make it work on the root domain including any sub-domains ( example.com , sub.example.com ), use .example.com .

domainLockRedirectUrl

Type: string Default: about:blank

:warning: This option does not work with target: 'node'

Allows the browser to be redirected to a passed URL if the source code isn’t run on the domains specified by domainLock

exclude

Type: string[] Default: []

A file names or globs which indicates files to exclude from obfuscation.

forceTransformStrings

Type: string[] Default: []

Enables force transformation of string literals, which being matched by passed RegExp patterns.

:warning: This option affects only strings that shouldn’t be transformed by stringArrayThreshold (or possible other thresholds in the future)

The option has a priority over reservedStrings option but hasn’t a priority over conditional comments .

Example:

	{
		forceTransformStrings: [
			'some-important-value',
			'some-string_\d'
		]
	}

identifierNamesCache

Type: Object | null Default: null

The main goal for this option is the ability to use the same identifier names during obfuscation of multiple sources/files.

Currently the two types of the identifiers are supported:

  • Global identifiers:
    • All global identifiers will be written to the cache;
    • All matched undeclared global identifiers will be replaced by the values from the cache.
  • Property identifiers, only when renameProperties option is enabled:
    • All property identifiers will be written to the cache;
    • All matched property identifiers will be replaced by the values from the cache.

Node.js API

If a null value is passed, completely disables the cache.

If an empty object ( {} ) is passed, enables the writing identifier names to the cache-object ( TIdentifierNamesCache type). This cache-object will be accessed through the getIdentifierNamesCache method call of ObfuscationResult object.

The resulting cache-object can be next used as identifierNamesGenerator option value for using these names during obfuscation of all matched identifier names of next sources.

Example:

const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
    `
        function foo(arg) {
           console.log(arg)
        }
        
        function bar() {
            var bark = 2;
        }
    `,
    {
        compact: false,
        identifierNamesCache: {},
        renameGlobals: true
    }
)

console.log(source1ObfuscationResult.getIdentifierNamesCache());
/*
    { 
        globalIdentifiers: {
            foo: '_0x5de86d',
            bar: '_0x2a943b'
        }
    }
*/



const source2ObfuscationResult = JavaScriptObfuscator.obfuscate(
    `
        // Expecting that these global functions are defined in another obfuscated file
        foo(1);
        bar();
        
        // Expecting that this global function is defined in third-party package
        baz();
    `,
    {
        compact: false,
        identifierNamesCache: source1ObfuscationResult.getIdentifierNamesCache(),
        renameGlobals: true
    }
)

console.log(source2ObfuscationResult.getObfuscatedCode());
/*
    _0x5de86d(0x1);
    _0x2a943b();
    baz();
 */

CLI

CLI has a different option --identifier-names-cache-path that allows defining a path to the existing .json file that will be used to read and write identifier names cache.

If a path to the empty file will be passed - identifier names cache will be written to that file.

This file with existing cache can be used again as --identifier-names-cache-path option value for using these names during obfuscation of all matched identifier names of the next files.

identifierNamesGenerator

Type: string Default: hexadecimal

Sets identifier names generator.

Available values:

  • dictionary : identifier names from identifiersDictionary list
  • hexadecimal : identifier names like _0xabc123
  • mangled : short identifier names like a , b , c
  • mangled-shuffled : same as mangled but with shuffled alphabet

identifiersDictionary

Type: string[] Default: []

Sets identifiers dictionary for identifierNamesGenerator : dictionary option. Each identifier from the dictionary will be used in a few variants with a different casing of each character. Thus, the number of identifiers in the dictionary should depend on the identifiers amount at original source code.

identifiersPrefix

Type: string Default: ''

Sets prefix for all global identifiers.

Use this option when you want to obfuscate multiple files. This option helps to avoid conflicts between global identifiers of these files. Prefix should be different for every file.

ignoreRequireImports

Type: boolean Default: false

Prevents obfuscation of require imports. Could be helpful in some cases when for some reason runtime environment requires these imports with static strings only.

inputFileName

Type: string Default: ''

Allows to set name of the input file with source code. This name will be used internally for source map generation. Required when using NodeJS API and sourceMapSourcesMode option has sources value`.

log

Type: boolean Default: false

Enables logging of the information to the console.

numbersToExpressions

Type: boolean Default: false

Enables numbers conversion to expressions

Example:

// input
const foo = 1234;

// output
const foo=-0xd93+-0x10b4+0x41*0x67+0x84e*0x3+-0xff8;

optionsPreset

Type: string Default: default

Allows to set options preset.

Available values:

  • default ;
  • low-obfuscation ;
  • medium-obfuscation ;
  • high-obfuscation .

All addition options will be merged with selected options preset.

renameGlobals

Type: boolean Default: false

:warning: this option can break your code. Enable it only if you know what it does!

Enables obfuscation of global variable and function names with declaration .

renameProperties

Type: boolean Default: false

:warning: this option MAY break your code. Enable it only if you know what it does!

Enables renaming of property names. All built-in DOM properties and properties in core JavaScript classes will be ignored.

To switch between safe and unsafe modes of this option use renamePropertiesMode option.

To set format of renamed property names use identifierNamesGenerator option.

To control which properties will be renamed use reservedNames option.

Example:

// input
(function () {
    const foo = {
        prop1: 1,
        prop2: 2,
        calc: function () {
            return this.prop1 + this.prop2;
        }
    };
    
    console.log(foo.calc());
})();

// output
(function () {
    const _0x46529b = {
        '_0x10cec7': 0x1,
        '_0xc1c0ca': 0x2,
        '_0x4b961d': function () {
            return this['_0x10cec7'] + this['_0xc1c0ca'];
        }
    };
    console['log'](_0x46529b['_0x4b961d']());
}());

renamePropertiesMode

Type: string Default: safe

:warning: Even in safe mode, renameProperties option MAY break your code.

Specifies renameProperties option mode:

  • safe - default behaviour after 2.11.0 release. Trying to rename properties in a more safe way to prevent runtime errors. With this mode some properties will be excluded from renaming.
  • unsafe - default behaviour before 2.11.0 release. Renames properties in an unsafe way without any restrictions.

If one file is using properties from other file, use identifierNamesCache option to keep the same property names between these files.

reservedNames

Type: string[] Default: []

Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns.

Example:

	{
		reservedNames: [
			'^someVariable',
			'functionParameter_\d'
		]
	}

reservedStrings

Type: string[] Default: []

Disables transformation of string literals, which being matched by passed RegExp patterns.

Example:

	{
		reservedStrings: [
			'react-native',
			'\.\/src\/test',
			'some-string_\d'
		]
	}

seed

Type: string|number Default: 0

This option sets seed for random generator. This is useful for creating repeatable results.

If seed is 0 - random generator will work without seed.

selfDefending

Type: boolean Default: false

:warning: Don’t change obfuscated code in any way after obfuscation with this option, because any change like uglifying of code can trigger self defending and code wont work anymore!
:warning: This option forcibly sets compact value to true

This option makes the output code resilient against formatting and variable renaming. If one tries to use a JavaScript beautifier on the obfuscated code, the code won’t work anymore, making it harder to understand and modify it.

simplify

Type: boolean Default: true

Enables additional code obfuscation through simplification.

:warning: in future releases obfuscation of boolean literals ( true => !![] ) will be moved under this option.

Example:

// input
if (condition1) {
    const foo = 1;
    const bar = 2;
  
    console.log(foo);
  
    return bar;
} else if (condition2) {
    console.log(1);
    console.log(2);
    console.log(3);
  
    return 4;
} else {
    return 5;
}

// output
if (condition1) {
    const foo = 0x1, bar = 0x2;
    return console['log'](foo), bar;
} else
    return condition2 ? (console['log'](0x1), console['log'](0x2), console['log'](0x3), 0x4) : 0x5;

sourceMap

Type: boolean Default: false

Enables source map generation for obfuscated code.

Source maps can be useful to help you debug your obfuscated JavaScript source code. If you want or need to debug in production, you can upload the separate source map file to a secret location and then point your browser there.

sourceMapBaseUrl

Type: string Default: ``

Sets base url to the source map import url when sourceMapMode: 'separate' .

CLI example:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000'

Result:

//# sourceMappingURL=http://localhost:9000/out.js.map

sourceMapFileName

Type: string Default: ``

Sets file name for output source map when sourceMapMode: 'separate' .

CLI example:

javascript-obfuscator input.js --output out.js --source-map true --source-map-base-url 'http://localhost:9000' --source-map-file-name example

Result:

//# sourceMappingURL=http://localhost:9000/example.js.map

sourceMapMode

Type: string Default: separate

Specifies source map generation mode:

  • inline - add source map at the end of each .js files;
  • separate - generates corresponding ‘.map’ file with source map. In case you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated code //# sourceMappingUrl=file.js.map .

sourceMapSourcesMode

Type: string Default: sources-content

Allows to control sources and sourcesContent fields of the source map:

  • sources-content - adds dummy sources field, adds sourcesContent field with the original source code;
  • sources - adds sources field with a valid source description, does not add sourcesContent field. When using NodeJS API it’s required to define inputFileName option that will be used as sources field value.

splitStrings

Type: boolean Default: false

Splits literal strings into chunks with length of splitStringsChunkLength option value.

Example:

// input
(function(){
    var test = 'abcdefg';
})();

// output
(function(){
    var _0x5a21 = 'ab' + 'cd' + 'ef' + 'g';
})();

splitStringsChunkLength

Type: number Default: 10

Sets chunk length of splitStrings option.

stringArray

Type: boolean Default: true

Removes string literals and place them in a special array. For instance, the string "Hello World" in var m = "Hello World"; will be replaced with something like var m = _0x12c456[0x1];

stringArrayCallsTransform

Type: boolean Default: false

:warning: stringArray option must be enabled

Enables the transformation of calls to the stringArray . All arguments of these calls may be extracted to a different object depending on stringArrayCallsTransformThreshold value. So it makes it even harder to automatically find calls to the string array.

Example:

function foo() {
    var k = {
        c: 0x2f2,
        d: '0x396',
        e: '0x397',
        f: '0x39a',
        g: '0x39d',
        h: 0x398,
        l: 0x394,
        m: '0x39b',
        n: '0x39f',
        o: 0x395,
        p: 0x395,
        q: 0x399,
        r: '0x399'
    };
    var c = i(k.d, k.e);
    var d = i(k.f, k.g);
    var e = i(k.h, k.l);
    var f = i(k.m, k.n);
    function i(c, d) {
        return b(c - k.c, d);
    }
    var g = i(k.o, k.p);
    var h = i(k.q, k.r);
}
function j(c, d) {
    var l = { c: 0x14b };
    return b(c - -l.c, d);
}
console[j(-'0xa6', -'0xa6')](foo());
function b(c, d) {
    var e = a();
    b = function (f, g) {
        f = f - 0xa3;
        var h = e[f];
        return h;
    };
    return b(c, d);
}
function a() {
    var m = [
        'string5',
        'string1',
        'log',
        'string3',
        'string6',
        'string2',
        'string4'
    ];
    a = function () {
        return m;
    };
    return a();
}

stringArrayCallsTransformThreshold

Type: number Default: 0.5

:warning: stringArray and stringArrayCallsTransformThreshold options must be enabled

You can use this setting to adjust the probability (from 0 to 1) that calls to the string array will be transformed.

stringArrayEncoding

Type: string[] Default: []

:warning: stringArray option must be enabled

This option can slow down your script.

Encode all string literals of the stringArray using base64 or rc4 and inserts a special code that used to decode it back at runtime.

Each stringArray value will be encoded by the randomly picked encoding from the passed list. This makes possible to use multiple encodings.

Available values:

  • 'none' ( boolean ): doesn’t encode stringArray value
  • 'base64' ( string ): encodes stringArray value using base64
  • 'rc4' ( string ): encodes stringArray value using rc4 . About 30-50% slower than base64 , but more harder to get initial values. It’s recommended to disable unicodeEscapeSequence option when using rc4 encoding to prevent very large size of obfuscated code.

For example with the following option values some stringArray value won’t be encoded, and some values will be encoded with base64 and rc4 encoding:

stringArrayEncoding: [
    'none',
    'base64',
    'rc4'
]

stringArrayIndexesType

Type: string[] Default: ['hexadecimal-number']

:warning: stringArray option must be enabled

Allows to control the type of string array call indexes.

Each stringArray call index will be transformed by the randomly picked type from the passed list. This makes possible to use multiple types.

Available values:

  • 'hexadecimal-number' ( default ): transforms string array call indexes as hexadecimal numbers
  • 'hexadecimal-numeric-string' : transforms string array call indexes as hexadecimal numeric string

Before 2.9.0 release javascript-obfuscator transformed all string array call indexes with hexadecimal-numeric-string type. This makes some manual deobfuscation slightly harder but it allows easy detection of these calls by automatic deobfuscators.

The new hexadecimal-number type approaches to make harder auto-detect of string array call patterns in the code.

More types will be added in the future.

stringArrayIndexShift

Type: boolean Default: true

:warning: stringArray option must be enabled

Enables additional index shift for all string array calls

stringArrayRotate

Type: boolean Default: true

:warning: stringArray must be enabled

Shift the stringArray array by a fixed and random (generated at the code obfuscation) places. This makes it harder to match the order of the removed strings to their original place.

stringArrayShuffle

Type: boolean Default: true

:warning: stringArray must be enabled

Randomly shuffles the stringArray array items.

stringArrayWrappersCount

Type: number Default: 1

:warning: stringArray option must be enabled

Sets the count of wrappers for the string array inside each root or function scope. The actual count of wrappers inside each scope is limited by a count of literal nodes within this scope.

Example:

// Input
const foo = 'foo';
const bar = 'bar';
        
function test () {
    const baz = 'baz';
    const bark = 'bark';
    const hawk = 'hawk';
}

const eagle = 'eagle';

// Output, stringArrayWrappersCount: 5
const _0x3f6c = [
    'bark',
    'bar',
    'foo',
    'eagle',
    'hawk',
    'baz'
];
const _0x48f96e = _0x2e13;
const _0x4dfed8 = _0x2e13;
const _0x55e970 = _0x2e13;
function _0x2e13(_0x33c4f5, _0x3f6c62) {
    _0x2e13 = function (_0x2e1388, _0x60b1e) {
        _0x2e1388 = _0x2e1388 - 0xe2;
        let _0x53d475 = _0x3f6c[_0x2e1388];
        return _0x53d475;
    };
    return _0x2e13(_0x33c4f5, _0x3f6c62);
}
const foo = _0x48f96e(0xe4);
const bar = _0x4dfed8(0xe3);
function test() {
    const _0x1c262f = _0x2e13;
    const _0x54d7a4 = _0x2e13;
    const _0x5142fe = _0x2e13;
    const _0x1392b0 = _0x1c262f(0xe7);
    const _0x201a58 = _0x1c262f(0xe2);
    const _0xd3a7fb = _0x1c262f(0xe6);
}
const eagle = _0x48f96e(0xe5);

stringArrayWrappersChainedCalls

Type: boolean Default: true

:warning: stringArray and stringArrayWrappersCount options must be enabled

Enables the chained calls between string array wrappers.

Example:

// Input
const foo = 'foo';
const bar = 'bar';
        
function test () {
    const baz = 'baz';
    const bark = 'bark';

    function test1() {
        const hawk = 'hawk';
        const eagle = 'eagle';
    } 
}

// Output, stringArrayWrappersCount: 5, stringArrayWrappersChainedCalls: true
const _0x40c2 = [
    'bar',
    'bark',
    'hawk',
    'eagle',
    'foo',
    'baz'
];
const _0x31c087 = _0x3280;
const _0x31759a = _0x3280;
function _0x3280(_0x1f52ee, _0x40c2a2) {
    _0x3280 = function (_0x3280a4, _0xf07b02) {
        _0x3280a4 = _0x3280a4 - 0x1c4;
        let _0x57a182 = _0x40c2[_0x3280a4];
        return _0x57a182;
    };
    return _0x3280(_0x1f52ee, _0x40c2a2);
}
const foo = _0x31c087(0x1c8);
const bar = _0x31c087(0x1c4);
function test() {
    const _0x848719 = _0x31759a;
    const _0x2693bf = _0x31c087;
    const _0x2c08e8 = _0x848719(0x1c9);
    const _0x359365 = _0x2693bf(0x1c5);
    function _0x175e90() {
        const _0x310023 = _0x848719;
        const _0x2302ef = _0x2693bf;
        const _0x237437 = _0x310023(0x1c6);
        const _0x56145c = _0x310023(0x1c7);
    }
}

stringArrayWrappersParametersMaxCount

Type: number Default: 2

:warning: stringArray option must be enabled
:warning: Currently this option affects only wrappers added by stringArrayWrappersType function option value

Allows to control the maximum number of string array wrappers parameters. Default and minimum value is 2 . Recommended value between 2 and 5 .

stringArrayWrappersType

Type: string Default: variable

:warning: stringArray and stringArrayWrappersCount options must be enabled

Allows to select a type of the wrappers that are appending by the stringArrayWrappersCount option.

Available values:

  • 'variable' : appends variable wrappers at the top of each scope. Fast performance.
  • 'function' : appends function wrappers at random positions inside each scope. Slower performance than with variable but provides more strict obfuscation.

Highly recommended to use function wrappers for higher obfuscation when a performance loss doesn’t have a high impact on an obfuscated application.

Example of the 'function' option value:

// input
const foo = 'foo';

function test () {
    const bar = 'bar';
    console.log(foo, bar);
}

test();

// output
const a = [
    'log',
    'bar',
    'foo'
];
const foo = d(0x567, 0x568);
function b(c, d) {
    b = function (e, f) {
        e = e - 0x185;
        let g = a[e];
        return g;
    };
    return b(c, d);
}
function test() {
    const c = e(0x51c, 0x51b);
    function e (c, g) {
        return b(c - 0x396, g);
    }
    console[f(0x51b, 0x51d)](foo, c);
    function f (c, g) {
        return b(c - 0x396, g);
    }
}
function d (c, g) {
    return b(g - 0x3e1, c);
}
test();

stringArrayThreshold

Type: number Default: 0.8 Min: 0 Max: 1

:warning: stringArray option must be enabled

You can use this setting to adjust the probability (from 0 to 1) that a string literal will be inserted into the stringArray .

This setting is especially useful for large code size because it repeatedly calls to the string array and can slow down your code.

stringArrayThreshold: 0 equals to stringArray: false .

target

Type: string Default: browser

Allows to set target environment for obfuscated code.

Available values:

  • browser ;
  • browser-no-eval ;
  • node .

Currently output code for browser and node targets is identical, but some browser-specific options are not allowed to use with node target. Output code for browser-no-eval target is not using eval .

transformObjectKeys

Type: boolean Default: false

Enables transformation of object keys.

Example:

// input
(function(){
    var object = {
        foo: 'test1',
        bar: {
            baz: 'test2'
        }
    };
})();

// output
var _0x4735 = [
    'foo',
    'baz',
    'bar',
    'test1',
    'test2'
];
function _0x390c(_0x33d6b6, _0x4735f4) {
    _0x390c = function (_0x390c37, _0x1eed85) {
        _0x390c37 = _0x390c37 - 0x198;
        var _0x2275f8 = _0x4735[_0x390c37];
        return _0x2275f8;
    };
    return _0x390c(_0x33d6b6, _0x4735f4);
}
(function () {
    var _0x17d1b7 = _0x390c;
    var _0xc9b6bb = {};
    _0xc9b6bb[_0x17d1b7(0x199)] = _0x17d1b7(0x19c);
    var _0x3d959a = {};
    _0x3d959a[_0x17d1b7(0x198)] = _0x17d1b7(0x19b);
    _0x3d959a[_0x17d1b7(0x19a)] = _0xc9b6bb;
    var _0x41fd86 = _0x3d959a;
}());

unicodeEscapeSequence

Type: boolean Default: false

Allows to enable/disable string conversion to unicode escape sequence.

Unicode escape sequence increases code size greatly and strings easily can be reverted to their original view. Recommended to enable this option only for small source code.

Preset Options

High obfuscation, low performance

The performance will be much slower than without obfuscation

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 1,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 1,
    debugProtection: true,
    debugProtectionInterval: 4000,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: true,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: true,
    splitStringsChunkLength: 5,
    stringArray: true,
    stringArrayCallsTransform: true,
    stringArrayEncoding: ['rc4'],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 5,
    stringArrayWrappersChainedCalls: true,    
    stringArrayWrappersParametersMaxCount: 5,
    stringArrayWrappersType: 'function',
    stringArrayThreshold: 1,
    transformObjectKeys: true,
    unicodeEscapeSequence: false
}

Medium obfuscation, optimal performance

The performance will be slower than without obfuscation

{
    compact: true,
    controlFlowFlattening: true,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: true,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: true,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: true,
    splitStringsChunkLength: 10,
    stringArray: true,
    stringArrayCallsTransform: true,
    stringArrayCallsTransformThreshold: 0.75,
    stringArrayEncoding: ['base64'],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 2,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 4,
    stringArrayWrappersType: 'function',
    stringArrayThreshold: 0.75,
    transformObjectKeys: true,
    unicodeEscapeSequence: false
}

Low obfuscation, High performance

The performance will be at a relatively normal level

{
    compact: true,
    controlFlowFlattening: false,
    deadCodeInjection: false,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: true,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: false,
    renameGlobals: false,
    selfDefending: true,
    simplify: true,
    splitStrings: false,
    stringArray: true,
    stringArrayCallsTransform: false,
    stringArrayEncoding: [],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 1,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 2,
    stringArrayWrappersType: 'variable',
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

Default preset, High performance

{
    compact: true,
    controlFlowFlattening: false,
    deadCodeInjection: false,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: false,
    identifierNamesGenerator: 'hexadecimal',
    log: false,
    numbersToExpressions: false,
    renameGlobals: false,
    selfDefending: false,
    simplify: true,
    splitStrings: false,
    stringArray: true,
    stringArrayCallsTransform: false,
    stringArrayCallsTransformThreshold: 0.5,
    stringArrayEncoding: [],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 1,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 2,
    stringArrayWrappersType: 'variable',
    stringArrayThreshold: 0.75,
    unicodeEscapeSequence: false
}

补充 Add :no_mouth:

插件安装失败的解决方案

如果提示:

  • 解压失败, 提示安装失败, 需要手动解压时
  • 提示默认 JSON 已丢失

0.1 请注意查看项目文件夹(如下)

请保证解压后的文件目录保持如下
extensions/cc-obfuscated-3_x

cc-obfuscated-3_x 是插件的文件夹名称, 请不要修改, 否则就会提示 JSON 已丢失



0.2.1 解压和安装插件的示意图 1

0.2.2 解压和安装插件的示意图 2

0.2.3 解压和安装插件的示意图(第二种)

就是修改一下压缩包的名称为 cc-obfuscated-3_x.zip , 然后导入插件包

Add Update [\color{red}{更新}]

0x02.1 支持多语言化显示和面板参数提示

0x02.2 支持在构建面板查看+项目设置面板设置混淆参数

0x02.3.1 实时调节参数, 保存参数到 Json 文件

0x02.3.2 实时调节参数, 保存参数到 Json 文件

0x02.3.3 修复 select 属性关联值的问题

Add 更新

增加一个 JSON 文件转码成 Unicode 编码的选项功能

本次更新-增加可选混淆 JSON 文件的功能(使用的 Unicode 编码转换), 仅有小部分场景适合使用,
备注: 此类混淆基本上没啥用处, 而且大部分打包的场景非常不适用 (打开黑屏),
所以需要做混淆 JSON 的朋友, 请谨慎+试验使用 !!



支持原生 android 和ios 吗?

不好意思哈, 目前版本并不支持原生 Android 和 IOS,只支持小游戏

0x3.3 Add AST (抽象语法树) + MD5 执行不可逆混淆

采取以下技术点, 对构建后的 JS 文件的 AST 语法树里面的, 可重命名的部分, 执行 MD5 的不可逆混淆
保证每次混淆产生的结果都不重复, 增强马甲包过审的概率…
md5 + esprima + escodegen + confusion + confuser





Update Add 添加个手动选择 JS 文件进行混淆的功能面板

3.4 手动选择需要混淆的 JS 文件, 开始混淆


1赞

Update Add 适配 2.x 版本 :blush:

3.5 添加适配 2.x 版本的构建功能(简化版)

3.5.1 升级到 2.x 选择JS文件混淆 (Cocos 2.3.4测试)

3.5.2 升级到 2.x 开始构建-web端 (Cocos 2.4.9测试)

3.5.3 升级到 2.x 开始构建-抖音小游戏 (Cocos 2.4.5测试)

3.5.4 升级到 2.x 开始构建-微信小游戏 (Cocos 2.3.4测试)

使用插件内的两个 \color{red}{混淆} 方式, 在 Cocos Creator v2.4.9 内的混淆方式的比较

1. 选择 .js 文件 (混淆前) ->代码如下

window.__require = function t(e, r, o) {
    function n(i, l) {
        if (!r[i]) {
            if (!e[i]) {
                var u = i.split("/");
                if (u = u[u.length - 1], !e[u]) {
                    var p = "function" == typeof __require && __require;
                    if (!l && p) return p(u, !0);
                    if (c) return c(u, !0);
                    throw new Error("Cannot find module '" + i + "'")
                }
                i = u
            }
            var f = r[i] = {
                exports: {}
            };
            e[i][0].call(f.exports,
            function(t) {
                return n(e[i][1][t] || t)
            },
            f, f.exports, t, e, r, o)
        }
        return r[i].exports
    }
    for (var c = "function" == typeof __require && __require,
    i = 0; i < o.length; i++) n(o[i]);
    return n
} ({
    Helloworld: [function(t, e, r) {
        "use strict";
        cc._RF.push(e, "e1b90/rohdEk4SdmmEZANaD", "Helloworld");
        var o, n = this && this.__extends || (o = function(t, e) {
            return (o = Object.setPrototypeOf || {
                __proto__: []
            }
            instanceof Array &&
            function(t, e) {
                t.__proto__ = e
            } ||
            function(t, e) {
                for (var r in e) Object.prototype.hasOwnProperty.call(e, r) && (t[r] = e[r])
            })(t, e)
        },
        function(t, e) {
            function r() {
                this.constructor = t
            }
            o(t, e),
            t.prototype = null === e ? Object.create(e) : (r.prototype = e.prototype, new r)
        }),
        c = this && this.__decorate ||
        function(t, e, r, o) {
            var n, c = arguments.length,
            i = c < 3 ? e: null === o ? o = Object.getOwnPropertyDescriptor(e, r) : o;
            if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) i = Reflect.decorate(t, e, r, o);
            else for (var l = t.length - 1; l >= 0; l--)(n = t[l]) && (i = (c < 3 ? n(i) : c > 3 ? n(e, r, i) : n(e, r)) || i);
            return c > 3 && i && Object.defineProperty(e, r, i),
            i
        };
        Object.defineProperty(r, "__esModule", {
            value: !0
        });
        var i = cc._decorator,
        l = i.ccclass,
        u = i.property,
        p = function(t) {
            function e() {
                var e = null !== t && t.apply(this, arguments) || this;
                return e.label = null,
                e.text = "hello",
                e
            }
            return n(e, t),
            e.prototype.start = function() {
                this.label.string = this.text
            },
            c([u(cc.Label)], e.prototype, "label", void 0),
            c([u], e.prototype, "text", void 0),
            c([l], e)
        } (cc.Component);
        r.
    default = p,
        cc._RF.pop()
    },
    {}]
},
{},
["Helloworld"]);

2.1 使用 \color{red}{AST} 执行变数(不可逆简单) 混淆 ->代码如下

(function(_0C7gylk) {
    window[_0C7gylk[0]] = function t(_1Czxfbgj, _2C5jgvs, _3Cfzhe2k) {
        function _4Csabiej(_6Cf10rbo, _8C08duth) {
            if (!_2C5jgvs[_6Cf10rbo]) {
                if (!_1Czxfbgj[_6Cf10rbo]) {
                    var _9Cfck3hn = _6Cf10rbo[_0C7gylk[1]](_0C7gylk[2]);
                    if (_9Cfck3hn = _9Cfck3hn[_9Cfck3hn[_0C7gylk[3]] - 1], !_1Czxfbgj[_9Cfck3hn]) {
                        var _10Ccfey38 = _0C7gylk[4] == typeof __require && __require;
                        if (!_8C08duth && _10Ccfey38) {
                            return _10Ccfey38(_9Cfck3hn, !0)
                        }
                        if (_5Csnc0hc) {
                            return _5Csnc0hc(_9Cfck3hn, !0)
                        }
                        throw new Error(_0C7gylk[5] + _6Cf10rbo + _0C7gylk[6])
                    }
                    _6Cf10rbo = _9Cfck3hn
                }
                var _11C3dr02n = _2C5jgvs[_6Cf10rbo] = { ['exports'] : {}
                };
                _1Czxfbgj[_6Cf10rbo][0][_0C7gylk[7]](_11C3dr02n[_0C7gylk[8]],
                function(_8C08duth) {
                    return _4Csabiej(_1Czxfbgj[_6Cf10rbo][1][_8C08duth] || _8C08duth)
                },
                _11C3dr02n, _11C3dr02n[_0C7gylk[8]], t, _1Czxfbgj, _2C5jgvs, _3Cfzhe2k)
            }
            return _2C5jgvs[_6Cf10rbo][_0C7gylk[8]]
        }
        for (var _5Csnc0hc = _0C7gylk[4] == typeof __require && __require, _6Cf10rbo = 0; _6Cf10rbo < _3Cfzhe2k[_0C7gylk[3]]; _6Cf10rbo++) {
            _4Csabiej(_3Cfzhe2k[_6Cf10rbo])
        }
        return _4Csabiej
    } ({ ['Helloworld'] : [function(_12C04xq7d, _13Crtra1, _14Ct5mln8) {
            'use strict';
            cc[_0C7gylk[10]][_0C7gylk[9]](_13Crtra1, _0C7gylk[11], _0C7gylk[12]);
            var _15C31xmih;
            var _16Cqjqev5u = this && this[_0C7gylk[13]] || (_15C31xmih = function(_12C04xq7d, _13Crtra1) {
                return (_15C31xmih = Object[_0C7gylk[14]] || { ['__proto__'] : []
                }
                instanceof Array &&
                function(_12C04xq7d, _13Crtra1) {
                    _12C04xq7d[_0C7gylk[15]] = _13Crtra1
                } ||
                function(_12C04xq7d, _13Crtra1) {
                    for (var _14Ct5mln8 in _13Crtra1) {
                        Object[_0C7gylk[17]][_0C7gylk[16]][_0C7gylk[7]](_13Crtra1, _14Ct5mln8) && (_12C04xq7d[_14Ct5mln8] = _13Crtra1[_14Ct5mln8])
                    }
                })(_12C04xq7d, _13Crtra1)
            },
            function(_12C04xq7d, _13Crtra1) {
                function _14Ct5mln8() {
                    this[_0C7gylk[18]] = _12C04xq7d
                }~ (_15C31xmih(_12C04xq7d, _13Crtra1), _12C04xq7d[_0C7gylk[17]] = null === _13Crtra1 ? Object[_0C7gylk[19]](_13Crtra1) : (_14Ct5mln8[_0C7gylk[17]] = _13Crtra1[_0C7gylk[17]], new _14Ct5mln8))
            });
            var _17Cfo1d7n = this && this[_0C7gylk[20]] ||
            function(_12C04xq7d, _13Crtra1, _14Ct5mln8, _15C31xmih) {
                var _16Cqjqev5u, _17Cfo1d7n = arguments[_0C7gylk[3]],
                _18Clbz01r = _17Cfo1d7n < 3 ? _13Crtra1: null === _15C31xmih ? _15C31xmih = Object[_0C7gylk[21]](_13Crtra1, _14Ct5mln8) : _15C31xmih;
                if (_0C7gylk[22] == typeof Reflect && _0C7gylk[4] == typeof Reflect[_0C7gylk[23]]) {
                    _18Clbz01r = Reflect[_0C7gylk[23]](_12C04xq7d, _13Crtra1, _14Ct5mln8, _15C31xmih)
                } else {
                    for (var _19C8e4pmd = _12C04xq7d[_0C7gylk[3]] - 1; _19C8e4pmd >= 0; _19C8e4pmd--) { (_16Cqjqev5u = _12C04xq7d[_19C8e4pmd]) && (_18Clbz01r = (_17Cfo1d7n < 3 ? _16Cqjqev5u(_18Clbz01r) : _17Cfo1d7n > 3 ? _16Cqjqev5u(_13Crtra1, _14Ct5mln8, _18Clbz01r) : _16Cqjqev5u(_13Crtra1, _14Ct5mln8)) || _18Clbz01r)
                    }
                }
                return _17Cfo1d7n > 3 && _18Clbz01r && Object[_0C7gylk[24]](_13Crtra1, _14Ct5mln8, _18Clbz01r),
                _18Clbz01r
            };
            Object[_0C7gylk[24]](_14Ct5mln8, _0C7gylk[25], { ['value'] : !0
            });
            var _18Clbz01r = cc[_0C7gylk[26]];
            var _19C8e4pmd = _18Clbz01r[_0C7gylk[27]];
            var _20Csn1gm = _18Clbz01r[_0C7gylk[28]];
            var _21Cfm82de = function(_12C04xq7d) {
                function _13Crtra1() {
                    var _13Crtra1 = null !== _12C04xq7d && _12C04xq7d[_0C7gylk[29]](this, arguments) || this;
                    return _13Crtra1[_0C7gylk[30]] = null,
                    _13Crtra1[_0C7gylk[31]] = _0C7gylk[32],
                    _13Crtra1
                }
                return _16Cqjqev5u(_13Crtra1, _12C04xq7d),
                _13Crtra1[_0C7gylk[17]][_0C7gylk[33]] = function() {
                    this[_0C7gylk[30]][_0C7gylk[34]] = this[_0C7gylk[31]]
                },
                _17Cfo1d7n([_20Csn1gm(cc[_0C7gylk[35]])], _13Crtra1[_0C7gylk[17]], _0C7gylk[30], void 0),
                _17Cfo1d7n([_20Csn1gm], _13Crtra1[_0C7gylk[17]], _0C7gylk[31], void 0),
                _17Cfo1d7n([_19C8e4pmd], _13Crtra1)
            } (cc[_0C7gylk[36]]);
            typeof(_14Ct5mln8[_0C7gylk[37]] = _21Cfm82de, cc[_0C7gylk[10]][_0C7gylk[38]]())
        },
        {}]
    },
    {},
    [_0C7gylk[12]])
} ['call'](this, ['__require', 'split', '/', 'length', 'function', "Cannot find module '", "'", 'call', 'exports', 'push', '_RF', 'e1b90/rohdEk4SdmmEZANaD', 'Helloworld', '__extends', 'setPrototypeOf', '__proto__', 'hasOwnProperty', 'prototype', 'constructor', 'create', '__decorate', 'getOwnPropertyDescriptor', 'object', 'decorate', 'defineProperty', '__esModule', '_decorator', 'ccclass', 'property', 'apply', 'label', 'text', 'hello', 'start', 'string', 'Label', 'Component', 'default', 'pop']))

2.2 使用 \color{red}{AST} 执行(固定字符) 简单混淆 ->代码如下

这样看起来是不是就顺眼多了 ? 放眼望去, 都是 \color{red}{Cocos} 的字符串:smile:

(function(CocosCreator) {
    window[CocosCreator[0]] = function t(Cocos2ccc, Cocos2cccc, _3Cfzhe2k) {
        function _4Csabiej(Cocos2x, Cocos2ccccc) {
            if (!Cocos2cccc[Cocos2x]) {
                if (!Cocos2ccc[Cocos2x]) {
                    var Cocos2_x = Cocos2x[CocosCreator[1]](CocosCreator[2]);
                    if (Cocos2_x = Cocos2_x[Cocos2_x[CocosCreator[3]] - 1], !Cocos2ccc[Cocos2_x]) {
                        var Cocos2cccccc = CocosCreator[4] == typeof __require && __require;
                        if (!Cocos2ccccc && Cocos2cccccc) {
                            return Cocos2cccccc(Cocos2_x, !0)
                        }
                        if (_5Csnc0hc) {
                            return _5Csnc0hc(Cocos2_x, !0)
                        }
                        throw new Error(CocosCreator[5] + Cocos2x + CocosCreator[6])
                    }
                    Cocos2x = Cocos2_x
                }
                var _11C3dr02n = Cocos2cccc[Cocos2x] = { ['exports'] : {}
                };
                Cocos2ccc[Cocos2x][0][CocosCreator[7]](_11C3dr02n[CocosCreator[8]],
                function(Cocos2ccccc) {
                    return _4Csabiej(Cocos2ccc[Cocos2x][1][Cocos2ccccc] || Cocos2ccccc)
                },
                _11C3dr02n, _11C3dr02n[CocosCreator[8]], t, Cocos2ccc, Cocos2cccc, _3Cfzhe2k)
            }
            return Cocos2cccc[Cocos2x][CocosCreator[8]]
        }
        for (var _5Csnc0hc = CocosCreator[4] == typeof __require && __require, Cocos2x = 0; Cocos2x < _3Cfzhe2k[CocosCreator[3]]; Cocos2x++) {
            _4Csabiej(_3Cfzhe2k[Cocos2x])
        }
        return _4Csabiej
    } ({ ['Helloworld'] : [function(Cocos2c, Cocos2cc, Cocos2cccccccc) {
            'use strict';
            cc[CocosCreator[10]][CocosCreator[9]](Cocos2cc, CocosCreator[11], CocosCreator[12]);
            var Cocos2ccccccc;
            var _16Cqjqev5u = this && this[CocosCreator[13]] || (Cocos2ccccccc = function(Cocos2c, Cocos2cc) {
                return (Cocos2ccccccc = Object[CocosCreator[14]] || { ['__proto__'] : []
                }
                instanceof Array &&
                function(Cocos2c, Cocos2cc) {
                    Cocos2c[CocosCreator[15]] = Cocos2cc
                } ||
                function(Cocos2c, Cocos2cc) {
                    for (var Cocos2cccccccc in Cocos2cc) {
                        Object[CocosCreator[17]][CocosCreator[16]][CocosCreator[7]](Cocos2cc, Cocos2cccccccc) && (Cocos2c[Cocos2cccccccc] = Cocos2cc[Cocos2cccccccc])
                    }
                })(Cocos2c, Cocos2cc)
            },
            function(Cocos2c, Cocos2cc) {
                function Cocos2cccccccc() {
                    this[CocosCreator[18]] = Cocos2c
                }~ (Cocos2ccccccc(Cocos2c, Cocos2cc), Cocos2c[CocosCreator[17]] = null === Cocos2cc ? Object[CocosCreator[19]](Cocos2cc) : (Cocos2cccccccc[CocosCreator[17]] = Cocos2cc[CocosCreator[17]], new Cocos2cccccccc))
            });
            var _17Cfo1d7n = this && this[CocosCreator[20]] ||
            function(Cocos2c, Cocos2cc, Cocos2cccccccc, Cocos2ccccccc) {
                var _16Cqjqev5u, _17Cfo1d7n = arguments[CocosCreator[3]],
                Cocos2ccccccccc = _17Cfo1d7n < 3 ? Cocos2cc: null === Cocos2ccccccc ? Cocos2ccccccc = Object[CocosCreator[21]](Cocos2cc, Cocos2cccccccc) : Cocos2ccccccc;
                if (CocosCreator[22] == typeof Reflect && CocosCreator[4] == typeof Reflect[CocosCreator[23]]) {
                    Cocos2ccccccccc = Reflect[CocosCreator[23]](Cocos2c, Cocos2cc, Cocos2cccccccc, Cocos2ccccccc)
                } else {
                    for (var _19C8e4pmd = Cocos2c[CocosCreator[3]] - 1; _19C8e4pmd >= 0; _19C8e4pmd--) { (_16Cqjqev5u = Cocos2c[_19C8e4pmd]) && (Cocos2ccccccccc = (_17Cfo1d7n < 3 ? _16Cqjqev5u(Cocos2ccccccccc) : _17Cfo1d7n > 3 ? _16Cqjqev5u(Cocos2cc, Cocos2cccccccc, Cocos2ccccccccc) : _16Cqjqev5u(Cocos2cc, Cocos2cccccccc)) || Cocos2ccccccccc)
                    }
                }
                return _17Cfo1d7n > 3 && Cocos2ccccccccc && Object[CocosCreator[24]](Cocos2cc, Cocos2cccccccc, Cocos2ccccccccc),
                Cocos2ccccccccc
            };
            Object[CocosCreator[24]](Cocos2cccccccc, CocosCreator[25], { ['value'] : !0
            });
            var Cocos2ccccccccc = cc[CocosCreator[26]];
            var _19C8e4pmd = Cocos2ccccccccc[CocosCreator[27]];
            var _20Csn1gm = Cocos2ccccccccc[CocosCreator[28]];
            var _21Cfm82de = function(Cocos2c) {
                function Cocos2cc() {
                    var Cocos2cc = null !== Cocos2c && Cocos2c[CocosCreator[29]](this, arguments) || this;
                    return Cocos2cc[CocosCreator[30]] = null,
                    Cocos2cc[CocosCreator[31]] = CocosCreator[32],
                    Cocos2cc
                }
                return _16Cqjqev5u(Cocos2cc, Cocos2c),
                Cocos2cc[CocosCreator[17]][CocosCreator[33]] = function() {
                    this[CocosCreator[30]][CocosCreator[34]] = this[CocosCreator[31]]
                },
                _17Cfo1d7n([_20Csn1gm(cc[CocosCreator[35]])], Cocos2cc[CocosCreator[17]], CocosCreator[30], void 0),
                _17Cfo1d7n([_20Csn1gm], Cocos2cc[CocosCreator[17]], CocosCreator[31], void 0),
                _17Cfo1d7n([_19C8e4pmd], Cocos2cc)
            } (cc[CocosCreator[36]]);
            typeof(Cocos2cccccccc[CocosCreator[37]] = _21Cfm82de, cc[CocosCreator[10]][CocosCreator[38]]())
        },
        {}]
    },
    {},
    [CocosCreator[12]])
} ['call'](this, ['__require', 'split', '/', 'length', 'function', "Cannot find module '", "'", 'call', 'exports', 'push', '_RF', 'e1b90/rohdEk4SdmmEZANaD', 'Helloworld', '__extends', 'setPrototypeOf', '__proto__', 'hasOwnProperty', 'prototype', 'constructor', 'create', '__decorate', 'getOwnPropertyDescriptor', 'object', 'decorate', 'defineProperty', '__esModule', '_decorator', 'ccclass', 'property', 'apply', 'label', 'text', 'hello', 'start', 'string', 'Label', 'Component', 'default', 'pop']))

3. 使用 JS-OB 执行混淆(这个开源的,容易被反解析) ->代码如下

var _0x7b8d = ['_decorator', 'exports', 'hasOwnProperty', '__require', 'pop', '_RF', 'function', 'property', 'label', 'start', 'Helloworld', 'create', 'Cannot\x20find\x20module\x20\x27', 'constructor', 'prototype', 'text', 'defineProperty', 'length', 'getOwnPropertyDescriptor', 'string', 'split', 'default', 'ccclass', 'Label', 'object', 'Component', 'setPrototypeOf', '__esModule', '__extends', '__proto__', 'hello', 'decorate', 'e1b90/rohdEk4SdmmEZANaD']; (function(_0xf9a805, _0x7b8d30) {
    var _0x6d916c = function(_0x1c31b1) {
        while (--_0x1c31b1) {
            _0xf9a805['push'](_0xf9a805['shift']());
        }
    };
    _0x6d916c(++_0x7b8d30);
} (_0x7b8d, 0xa5));
var _0x6d91 = function(_0xf9a805, _0x7b8d30) {
    _0xf9a805 = _0xf9a805 - 0x0;
    var _0x6d916c = _0x7b8d[_0xf9a805];
    return _0x6d916c;
};
window[_0x6d91('0x3')] = function t(_0x51a2da, _0x20f7e7, _0x536365) {
    function _0x4e6a82(_0x5135a9, _0x38fc5f) {
        if (!_0x20f7e7[_0x5135a9]) {
            if (!_0x51a2da[_0x5135a9]) {
                var _0x441ed6 = _0x5135a9[_0x6d91('0x14')]('/');
                if (_0x441ed6 = _0x441ed6[_0x441ed6[_0x6d91('0x11')] - 0x1], !_0x51a2da[_0x441ed6]) {
                    var _0x5b0069 = _0x6d91('0x6') == typeof __require && __require;
                    if (!_0x38fc5f && _0x5b0069) return _0x5b0069(_0x441ed6, !0x0);
                    if (_0x22f365) return _0x22f365(_0x441ed6, !0x0);
                    throw new Error(_0x6d91('0xc') + _0x5135a9 + '\x27');
                }
                _0x5135a9 = _0x441ed6;
            }
            var _0x20ad22 = _0x20f7e7[_0x5135a9] = {
                'exports': {}
            };
            _0x51a2da[_0x5135a9][0x0]['call'](_0x20ad22[_0x6d91('0x1')],
            function(_0x377fec) {
                return _0x4e6a82(_0x51a2da[_0x5135a9][0x1][_0x377fec] || _0x377fec);
            },
            _0x20ad22, _0x20ad22[_0x6d91('0x1')], t, _0x51a2da, _0x20f7e7, _0x536365);
        }
        return _0x20f7e7[_0x5135a9][_0x6d91('0x1')];
    }
    for (var _0x22f365 = _0x6d91('0x6') == typeof __require && __require, _0x28fedd = 0x0; _0x28fedd < _0x536365[_0x6d91('0x11')]; _0x28fedd++) _0x4e6a82(_0x536365[_0x28fedd]);
    return _0x4e6a82;
} ({
    'Helloworld': [function(_0x1d951b, _0x3f51c8, _0x3e1d40) {
        'use strict';
        cc['_RF']['push'](_0x3f51c8, _0x6d91('0x20'), _0x6d91('0xa'));
        var _0x18f2a0, _0x48f1df = this && this[_0x6d91('0x1c')] || (_0x18f2a0 = function(_0x7b053d, _0x2642fa) {
            return (_0x18f2a0 = Object[_0x6d91('0x1a')] || {
                '__proto__': []
            }
            instanceof Array &&
            function(_0x5c5d41, _0x2335a6) {
                _0x5c5d41[_0x6d91('0x1d')] = _0x2335a6;
            } ||
            function(_0xa9446e, _0x5da323) {
                for (var _0x358ecf in _0x5da323) Object[_0x6d91('0xe')][_0x6d91('0x2')]['call'](_0x5da323, _0x358ecf) && (_0xa9446e[_0x358ecf] = _0x5da323[_0x358ecf]);
            })(_0x7b053d, _0x2642fa);
        },
        function(_0x25e2e1, _0x448248) {
            function _0x2b99fe() {
                this[_0x6d91('0xd')] = _0x25e2e1;
            }
            _0x18f2a0(_0x25e2e1, _0x448248),
            _0x25e2e1[_0x6d91('0xe')] = null === _0x448248 ? Object[_0x6d91('0xb')](_0x448248) : (_0x2b99fe['prototype'] = _0x448248[_0x6d91('0xe')], new _0x2b99fe());
        }),
        _0x1b404b = this && this['__decorate'] ||
        function(_0x15876e, _0x5a8ade, _0xe6c639, _0x35b214) {
            var _0xe0ba59, _0xb125b2 = arguments[_0x6d91('0x11')],
            _0x4b3218 = _0xb125b2 < 0x3 ? _0x5a8ade: null === _0x35b214 ? _0x35b214 = Object[_0x6d91('0x12')](_0x5a8ade, _0xe6c639) : _0x35b214;
            if (_0x6d91('0x18') == typeof Reflect && 'function' == typeof Reflect[_0x6d91('0x1f')]) _0x4b3218 = Reflect[_0x6d91('0x1f')](_0x15876e, _0x5a8ade, _0xe6c639, _0x35b214);
            else for (var _0x2057d9 = _0x15876e['length'] - 0x1; _0x2057d9 >= 0x0; _0x2057d9--)(_0xe0ba59 = _0x15876e[_0x2057d9]) && (_0x4b3218 = (_0xb125b2 < 0x3 ? _0xe0ba59(_0x4b3218) : _0xb125b2 > 0x3 ? _0xe0ba59(_0x5a8ade, _0xe6c639, _0x4b3218) : _0xe0ba59(_0x5a8ade, _0xe6c639)) || _0x4b3218);
            return _0xb125b2 > 0x3 && _0x4b3218 && Object[_0x6d91('0x10')](_0x5a8ade, _0xe6c639, _0x4b3218),
            _0x4b3218;
        };
        Object[_0x6d91('0x10')](_0x3e1d40, _0x6d91('0x1b'), {
            'value': !0x0
        });
        var _0x196f24 = cc[_0x6d91('0x0')],
        _0x3c3586 = _0x196f24[_0x6d91('0x16')],
        _0x475603 = _0x196f24[_0x6d91('0x7')],
        _0x43e364 = function(_0x10655b) {
            function _0x1ff5d4() {
                var _0x18e4d3 = null !== _0x10655b && _0x10655b['apply'](this, arguments) || this;
                return _0x18e4d3[_0x6d91('0x8')] = null,
                _0x18e4d3[_0x6d91('0xf')] = _0x6d91('0x1e'),
                _0x18e4d3;
            }
            return _0x48f1df(_0x1ff5d4, _0x10655b),
            _0x1ff5d4['prototype'][_0x6d91('0x9')] = function() {
                this[_0x6d91('0x8')][_0x6d91('0x13')] = this[_0x6d91('0xf')];
            },
            _0x1b404b([_0x475603(cc[_0x6d91('0x17')])], _0x1ff5d4[_0x6d91('0xe')], _0x6d91('0x8'), void 0x0),
            _0x1b404b([_0x475603], _0x1ff5d4[_0x6d91('0xe')], _0x6d91('0xf'), void 0x0),
            _0x1b404b([_0x3c3586], _0x1ff5d4);
        } (cc[_0x6d91('0x19')]);
        _0x3e1d40[_0x6d91('0x15')] = _0x43e364,
        cc[_0x6d91('0x5')][_0x6d91('0x4')]();
    },
    {}]
},
{},
['Helloworld']);

已支持 Cocos 2.x 和 3.x 全局安装使用插件, 可以从此处下载安装,
也支持使用 Cocos Dashboard 安装 (Dashboard 目前只支持 3.x 插件)
Cocos 编辑器 > 顶部菜单栏 > 商城 > 下载后点击安装



Update Add Cocos 2.x 版本

Cocos 3.x 版本的还在测试中, 此功能后续会考虑发布个独立的插件 (方便拓展开发)

3.6 添加 Cocos 2.x 更改 UUID 的功能

3.6.1 一键更改所有长短码的 UUID

3.6.2 查看备份的文件内容