使用javascript-obfuscator对js文件进行混淆

4,340 阅读14分钟

JavaScript Obfuscator 是一款功能强大的免费 JavaScript 混淆器,包含多种功能,可为您的源代码提供保护。

主要特征:

  • 变量重命名
  • 字符串提取和加密
  • 死代码注入
  • 控制流扁平化
  • 各种代码转换
  • 还有更多...

插件:

在线版本:

obfuscator.io

⚠️ 注意

仅仅混淆属于您的代码。

不建议混淆第三方库文件,因为混淆后的代码要慢 15-80%(取决于怎么配置),而且文件要大得多。

安装

npm install javascript-obfuscator -g

使用

混淆单个文件

javascript-obfuscator input_file.js

执行后,会生成input_file_obfuscated.js

批量混淆

  • javascript-obfuscator ./ --output ./:采用递归的方式混淆当前目录下的所有js文件(包括子文件),对原文件进行修改,不会生成新的js文件

  • javascript-obfuscator ./ :采用递归的方式混淆当前目录下的所有js文件(包括子文件),对原文件进行拷贝,会生成新的js文件,在新的js文件中进行修改。

--ouput a.js:指定输出文件名
--ouput ./output:指定输出目录

效果

原始js文件:

var welcome = function(name) {
	return `welcome${name}`;
}

console.log(welcome("aa"));
console.log(welcome("bb"));

混淆后的文件

var a0_0x26ed16=a0_0x10fd;(function(_0x1442b9,_0x22b0d4){var _0x2d7fdd=a0_0x10fd,_0x1f1760=_0x1442b9();while(!![]){try{var _0x2dd2e5=-parseInt(_0x2d7fdd(0x91))/0x1+parseInt(_0x2d7fdd(0x8f))/0x2+parseInt(_0x2d7fdd(0x8b))/0x3+parseInt(_0x2d7fdd(0x8a))/0x4+parseInt(_0x2d7fdd(0x8d))/0x5+-parseInt(_0x2d7fdd(0x8c))/0x6*(-parseInt(_0x2d7fdd(0x90))/0x7)+-parseInt(_0x2d7fdd(0x8e))/0x8;if(_0x2dd2e5===_0x22b0d4)break;else _0x1f1760['push'](_0x1f1760['shift']());}catch(_0x45972f){_0x1f1760['push'](_0x1f1760['shift']());}}}(a0_0x37ea,0x40f71));var welcome=function(_0x1adb13){var _0x4b4afe=a0_0x10fd;return _0x4b4afe(0x93)+_0x1adb13;};function a0_0x10fd(_0x2e5e0a,_0x2b7804){var _0x37ea85=a0_0x37ea();return a0_0x10fd=function(_0x10fd94,_0x491768){_0x10fd94=_0x10fd94-0x8a;var _0x2a25f6=_0x37ea85[_0x10fd94];return _0x2a25f6;},a0_0x10fd(_0x2e5e0a,_0x2b7804);}function a0_0x37ea(){var _0x4d028c=['1619632VlYCof','20745QJxTYh','246654sbQVxA','1290425RuLxuv','4184280NHKkFh','532838XbZkeu','49APPhdD','434958kZhPSm','log','welcome'];a0_0x37ea=function(){return _0x4d028c;};return a0_0x37ea();}console[a0_0x26ed16(0x92)](welcome('aa')),console['log'](welcome('bb'));

JavaScript Obfuscator Options

如果不适用默认的混淆配置,需要在项目根目录中创建 javascript-obfuscator 的配置文件 obfuscator.config.json,再根据自己的需要去衡量设置

下面是混淆的配置项:

配置:

{
    compact: true,
    controlFlowFlattening: false,
    controlFlowFlatteningThreshold: 0.75,
    deadCodeInjection: false,
    deadCodeInjectionThreshold: 0.4,
    debugProtection: false,
    debugProtectionInterval: 0,
    disableConsoleOutput: false,
    domainLock: [],
    domainLockRedirectUrl: 'about:blank',
    forceTransformStrings: [],
    identifierNamesCache: null,
    identifierNamesGenerator: 'hexadecimal',
    identifiersDictionary: [],
    identifiersPrefix: '',
    ignoreImports: false,
    inputFileName: '',
    log: false,
    numbersToExpressions: false,
    optionsPreset: 'default',
    renameGlobals: false,
    renameProperties: false,
    renamePropertiesMode: 'safe',
    reservedNames: [],
    reservedStrings: [],
    seed: 0,
    selfDefending: false,
    simplify: true,
    sourceMap: false,
    sourceMapBaseUrl: '',
    sourceMapFileName: '',
    sourceMapMode: 'separate',
    sourceMapSourcesMode: 'sources-content',
    splitStrings: false,
    splitStringsChunkLength: 10,
    stringArray: true,
    stringArrayCallsTransform: true,
    stringArrayCallsTransformThreshold: 0.5,
    stringArrayEncoding: [],
    stringArrayIndexesType: [
        'hexadecimal-number'
    ],
    stringArrayIndexShift: true,
    stringArrayRotate: true,
    stringArrayShuffle: true,
    stringArrayWrappersCount: 1,
    stringArrayWrappersChainedCalls: true,
    stringArrayWrappersParametersMaxCount: 2,
    stringArrayWrappersType: 'variable',
    stringArrayThreshold: 0.75,
    target: 'browser',
    transformObjectKeys: false,
    unicodeEscapeSequence: false 
}

CLI 配置:

-v, --version
    -h, --help

    -o, --output

    --compact <boolean>
    --config <string>
    --control-flow-flattening <boolean>
    --control-flow-flattening-threshold <number>
    --dead-code-injection <boolean>
    --dead-code-injection-threshold <number>
    --debug-protection <boolean>
    --debug-protection-interval <boolean>
    --disable-console-output <boolean>
    --domain-lock '<list>' (comma separated)
    --exclude '<list>' (comma separated)
    --identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
    --identifiers-dictionary '<list>' (comma separated)
    --identifiers-prefix <string>
    --log <boolean>
    --numbers-to-expressions <boolean>
    --options-preset <string> [default, low-obfuscation, medium-obfuscation, high-obfuscation]
    --rename-globals <boolean>
    --rename-properties <boolean>
    --reserved-names '<list>' (comma separated)
    --reserved-strings '<list>' (comma separated)
    --rotate-string-array <boolean>
    --seed <string|number>
    --self-defending <boolean>
    --shuffle-string-array <boolean>
    --simplify <boolean>
    --source-map <boolean>
    --source-map-base-url <string>
    --source-map-file-name <string>
    --source-map-mode <string> [inline, separate]
    --split-strings <boolean>
    --split-strings-chunk-length <number>
    --string-array <boolean>
    --string-array-encoding '<list>' (comma separated) [none, base64, rc4]
    --string-array-threshold <number>
    --target <string> [browser, browser-no-eval, node]
    --transform-object-keys <boolean>
    --unicode-escape-sequence <boolean>

compact

类型:boolean默认值:true

压缩代码输出在一行。

config

类型:string默认值:``

包含模糊处理程序选项的JS/JSON配置文件的名称。这些选项将被直接传递给CLI的选项覆盖

controlFlowFlattening

类型:boolean默认:false

⚠️ 此选项极大地影响性能,运行时速度降低1.5倍。使用controlFlowFlatteningThreshold设置将受控制流展平影响的节点的百分比。

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

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

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

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

此设置对于较大的代码量特别有用,因为大量的控制流转换会降低代码速度并增加代码大小。

controlFlowFlatteningThreshold: 0等于controlFlowFlattening: false

deadCodeInjection

类型:boolean默认:false

⚠️ 大大增加了模糊代码的大小(最多200%),只有在混淆代码的大小无关紧要时才使用。使用deadCodeInjectionThreshold设置将受死代码注入影响的节点的百分比。
⚠️ 此选项强制启用stringArray选项。

使用这个选项,随机的死代码块将被添加到模糊代码中。

Example:

// input
(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
            console.log('cde');
            console.log('efg');
            console.log('hij');
        };
        
        var bar = function () {
            console.log('klm');
            console.log('nop');
            console.log('qrs');
        };
    
        var baz = function () {
            console.log('tuv');
            console.log('wxy');
            console.log('z');
        };
    
        foo();
        bar();
        baz();
    }
})();

// output
var _0x5024 = [    'zaU',    'log',    'tuv',    'wxy',    'abc',    'cde',    'efg',    'hij',    'QhG',    'TeI',    'klm',    'nop',    'qrs',    'bZd',    'HMx'];
var _0x4502 = function (_0x1254b1, _0x583689) {
    _0x1254b1 = _0x1254b1 - 0x0;
    var _0x529b49 = _0x5024[_0x1254b1];
    return _0x529b49;
};
(function () {
    if (!![]) {
        var _0x16c18d = function () {
            if (_0x4502('0x0') !== _0x4502('0x0')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        var _0x1f7292 = function () {
            if (_0x4502('0x8') === _0x4502('0x9')) {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            } else {
                console[_0x4502('0x1')](_0x4502('0xa'));
                console[_0x4502('0x1')](_0x4502('0xb'));
                console[_0x4502('0x1')](_0x4502('0xc'));
            }
        };
        var _0x33b212 = function () {
            if (_0x4502('0xd') !== _0x4502('0xe')) {
                console[_0x4502('0x1')](_0x4502('0x2'));
                console[_0x4502('0x1')](_0x4502('0x3'));
                console[_0x4502('0x1')]('z');
            } else {
                console[_0x4502('0x1')](_0x4502('0x4'));
                console[_0x4502('0x1')](_0x4502('0x5'));
                console[_0x4502('0x1')](_0x4502('0x6'));
                console[_0x4502('0x1')](_0x4502('0x7'));
            }
        };
        _0x16c18d();
        _0x1f7292();
        _0x33b212();
    }
}());

deadCodeInjectionThreshold

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

允许设置受deadCodeInjection影响的节点的百分比。

debugProtection

类型:boolean默认:false

⚠️ 如果打开开发人员工具,可以冻结浏览器。

这个选项使得几乎不可能使用开发人员工具的debugger函数(在WebKit-based和Mozilla Firefox上)。

debugProtectionInterval

类型:boolean默认:false

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

如果选中,则使用一个间隔来强制Console选项卡上的debug模式,这使得使用开发人员工具的其他功能更加困难。如果启用debugProtection,则有效。

disableConsoleOutput

类型:boolean默认:false

通过将console.logconsole.infoconsole.errorconsole.warnconsole.debugconsole.exceptionconsole.trace替换为空函数来禁用它们。这使得调试器的使用更加困难。

domainLock

类型:string[]默认:[]

⚠️ 此选项不适用于target: 'node'

只允许在特定域和/或sub-domains上运行模糊处理的源代码。这使得人们很难复制并粘贴源代码并在别处运行。

多域和sub-domains

可以将代码锁定到多个域或sub-domain。例如,要锁定它,使代码只在www.example.comaddwww.example.com上运行。要使它在根域上工作,包括sub-domain(example.comsub.example.com),请使用.example.com

exclude

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

表示要从模糊处理中排除的文件的文件名或全局名。

identifierNamesGenerator

类型:string默认值:hexadecimal

设置标识符名称生成器。

Available values:

  • dictionary:来自identifiersDictionary列表的标识符名称
  • hexadecimal:标识符名称,如_0xabc123
  • mangled:短标识符名称,如abc
  • mangled-shuffled:与mangled相同,但字母表混乱

identifiersDictionary

类型:string[]默认:[]

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

identifiersPrefix

类型:string默认值:''

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

如果要模糊处理多个文件,请使用此选项。此选项有助于避免这些文件的全局标识符之间的冲突。每个文件的前缀应该不同。

inputFileName

类型:string默认值:''

允许使用源代码设置输入文件的名称。此名称将在内部用于源映射生成。

log

类型:boolean默认值:false

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

numbersToExpressions

类型:boolean默认值:false

启用数字转换为表达式

Example:

// input
const foo = 1234;

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

optionsPreset

类型:string默认值:default

允许设置预设选项。

Available values:

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

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

renameGlobals

类型:boolean默认值:false

⚠️ 这个选项会破坏你的代码。只有当你知道它的作用时才启用它!

使用声明启用全局变量和函数名的模糊处理。

renameProperties

类型:boolean默认值:false

⚠️ 在大多数情况下,此选项将破坏代码。只有当你知道它的作用时才启用它!

启用属性名称的重命名。所有built-inDOM属性和核心JavaScript类中的属性都将被忽略。

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

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

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']());
}());

reservedNames

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

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

Example:

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

reservedStrings

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

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

Example:

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

rotateStringArray

类型:boolean默认值:true

⚠️ stringArray必须启用

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

如果原始源代码不小,建议使用此选项,因为helper函数可以引起注意。

seed

类型:string|number默认值:0

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

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

selfDefending

类型:boolean默认值:false

⚠️ 使用此选项进行模糊处理后,请不要以任何方式更改混淆的代码,因为任何类似代码丑陋的更改都会触发自我保护,代码将不再工作!
⚠️ 此选项强制将compact值设置为true

此选项使输出代码对格式化和变量重命名具有弹性。如果一个人试图在模糊的代码上使用JavaScript美化器,代码将不再工作,这使得理解和修改它变得更加困难。

shuffleStringArray

类型:boolean默认值:true

⚠️ stringArray必须启用

随机洗牌stringArray数组项。

simplify

类型:boolean默认:true

通过简化实现额外的代码混淆。

⚠️ 在将来的版本中,boolean文本的模糊处理(true=>!![])将移动到该选项下。

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

类型:boolean默认值:false

启用模糊代码的源映射生成。

源代码映射可以帮助您调试模糊的JavaScript源代码。如果希望或需要在生产环境中调试,可以将单独的源映射文件上载到一个秘密位置,然后将浏览器指向该位置。

sourceMapBaseUrl

类型:string默认值:``

sourceMapMode: 'separate'时,将基url设置为源映射导入url。

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

类型:string默认值:``

设置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

类型:string默认:separate

指定源映射生成模式:

  • inline-使用源映射生成单个文件,而不是使用单独的文件;
  • separate-使用源映射生成相应的'.map'文件。如果您通过CLI运行模糊处理程序-使用模糊处理代码//# sourceMappingUrl=file.js.map将指向源映射文件的链接添加到文件末尾。

splitStrings

类型:boolean默认:false

{strings}长度为48的字符串,拆分成文本长度为970的文本块。

Example:

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

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

splitStringsChunkLength

类型:number默认:10

设置splitStrings选项的块长度。

stringArray

类型:boolean默认:true

移除字符串文字并将其放入特殊数组中。例如,var m = "Hello World";中的字符串"Hello World"将替换为var m = _0x12c456[0x1];

stringArrayEncoding

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

⚠️ stringArray选项必须启用

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

使用base64rc4stringArray的所有字符串文本进行编码,并插入一个在运行时用于解码的特殊代码。

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

Available values:

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

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

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

stringArrayThreshold

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

⚠️ stringArray选项必须启用

您可以使用此设置来调整将字符串文本插入stringArray的概率(从0到1)。

此设置对于较大的代码量特别有用,因为它反复调用string array,并且会降低代码的速度。

stringArrayThreshold: 0等于stringArray: false

target

类型:string默认值:browser

允许为模糊代码设置目标环境。

Available values:

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

当前browsernode目标的输出代码是相同的,但某些browser-specific选项不允许与node目标一起使用。browser-no-eval目标的输出代码未使用eval

transformObjectKeys

类型:boolean默认:false

启用对象键的转换。

Example:

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

// output
var _0x2fae = [
    'baz',
    'test2',
    'foo',
    'test1',
    'bar'
];
var _0x377c = function (_0x1fbd3f, _0x59c72f) {
    _0x1fbd3f = _0x1fbd3f - 0x0;
    var _0x14fada = _0x2fae[_0x1fbd3f];
    return _0x14fada;
};
(function () {
    var _0x8a12db = {};
    _0x8a12db[_0x377c('0x0')] = _0x377c('0x1');
    var _0xc75419 = {};
    _0xc75419[_0x377c('0x2')] = _0x377c('0x3');
    _0xc75419[_0x377c('0x4')] = _0x8a12db;
    var _0x191393 = _0xc75419;
}());

unicodeEscapeSequence

类型:boolean默认:false

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

Unicode转义序列大大增加了代码的大小,字符串可以很容易地恢复到其原始视图。建议仅对小源代码启用此选项。