- Published on
 
javascript-obfuscator —— 保护你的js代码
- Authors
 
- Name
 - Pony Ma
 
javascript-obfuscator —— 保护你的前端代码
JavaScript代码由于天然暴露在客户端的特性,很容易被人读取。在代码的安全性和知识产权日益重要的当下,保护JavaScript代码不被恶意用户轻易理解和篡改就显得尤为重要。
这就是 javascript-obfuscator 上场的时候了
什么是**javascript-obfuscator**
javascript-obfuscator 是一个开源的 JavaScript 混淆工具,他通过各种技术手段改变代码的外观和结构,而不影响其执行功能。使代码变得难以阅读和理解,从而增加了逆向工程的难度。
怎么实现混淆
javascript-obfuscator 通过以下几种方式来混淆代码:
- 变量名和函数名替换:将这些名字替换为难以理解的字符串,使代码不可读,隐藏其原始意图和功能。
 - 字符串数组转换和加密:将代码中的字符串转移到一个数组中,在运行时动态解密,增加逆向难度。
 - 控制流混淆:改变代码的执行流程,使逻辑结构变得复杂、难以追踪。
 - 死代码注入:加入无用的代码片段,误导分析者。
 - 环境检测:检测代码是否运行在某些特定环境(如浏览器的开发者工具)中,并根据需要改变代码行为或停止运行。
 - 自我防御机制:如果检测到代码结构被修改(例如尝试美化代码)代码将改变行为或停止运行。
 
使用方式
javascript-obfuscator 是一个nodeJS的包,你可以直接通过yarn或者npm进行安装
$ yarn add --dev javascript-obfuscator
$ npm install --save-dev javascript-obfuscator
直接导入
你可以直接在JS文件中进行导入,调用混淆函数进行代码混淆
var JavaScriptObfuscator = require('javascript-obfuscator');
var obfuscationResult = JavaScriptObfuscator.obfuscate(
    `
        (function(){
            var variable1 = '5' - 3;
            var variable2 = '5' + 3;
            var variable3 = '5' + - '2';
            var variable4 = ['10','10','10','10','10'].map(parseInt);
            var variable5 = 'foo ' + 1 + 1;
            console.log(variable1);
            console.log(variable2);
            console.log(variable3);
            console.log(variable4);
            console.log(variable5);
        })();
    `,
    {
        compact: false,
        controlFlowFlattening: true,
        controlFlowFlatteningThreshold: 1,
        numbersToExpressions: true,
        simplify: true,
        stringArrayShuffle: true,
        splitStrings: true,
        stringArrayThreshold: 1
    }
);
console.log(obfuscationResult.getObfuscatedCode());
/*
var _0x9947 = [
    'map',
    'log',
    'foo\x20',
    'bvmqO',
    '133039ViRMWR',
    'xPfLC',
    'ytpdx',
    '1243717qSZCyh',
    '2|7|4|6|9|',
    '1ErtbCr',
    '1608314VKvthn',
    '1ZRaFKN',
    'XBoAA',
    '423266kQOYHV',
    '3|0|5|8|1',
    '235064xPNdKe',
    '13RUDZfG',
    '157gNPQGm',
    '1639212MvnHZL',
    'rDjOa',
    'iBHph',
    '9926iRHoRl',
    'split'
];
function _0x33e4(_0x1809b5, _0x37ef6e) {
    return _0x33e4 = function (_0x338a69, _0x39ad79) {
        _0x338a69 = _0x338a69 - (0x1939 + -0xf * 0x1f3 + 0x1 * 0x469);
        var _0x2b223a = _0x9947[_0x338a69];
        return _0x2b223a;
    }, _0x33e4(_0x1809b5, _0x37ef6e);
}
(function (_0x431d87, _0x156c7f) {
    var _0x10cf6e = _0x33e4;
    while (!![]) {
        try {
            var _0x330ad1 = -parseInt(_0x10cf6e(0x6c)) * -parseInt(_0x10cf6e(0x6d)) + -parseInt(_0x10cf6e(0x74)) * -parseInt(_0x10cf6e(0x78)) + parseInt(_0x10cf6e(0x6a)) + -parseInt(_0x10cf6e(0x70)) + parseInt(_0x10cf6e(0x6e)) * -parseInt(_0x10cf6e(0x75)) + parseInt(_0x10cf6e(0x72)) + -parseInt(_0x10cf6e(0x67)) * parseInt(_0x10cf6e(0x73));
            if (_0x330ad1 === _0x156c7f)
                break;
            else
                _0x431d87['push'](_0x431d87['shift']());
        } catch (_0x9f878) {
            _0x431d87['push'](_0x431d87['shift']());
        }
    }
}(_0x9947, -0xb6270 + 0x4dfd2 * 0x2 + 0x75460 * 0x2), function () {
    var _0x1f346d = _0x33e4, _0x860db8 = {
            'ytpdx': _0x1f346d(0x6b) + _0x1f346d(0x71),
            'bvmqO': function (_0x560787, _0x519b9e) {
                return _0x560787 - _0x519b9e;
            },
            'rDjOa': function (_0x4501fe, _0x2b07a3) {
                return _0x4501fe + _0x2b07a3;
            },
            'xPfLC': function (_0x5f3c9b, _0x434936) {
                return _0x5f3c9b + _0x434936;
            },
            'XBoAA': function (_0x535b8a, _0x42eef4) {
                return _0x535b8a + _0x42eef4;
            },
            'iBHph': _0x1f346d(0x65)
        }, _0x346c55 = _0x860db8[_0x1f346d(0x69)][_0x1f346d(0x79)]('|'), _0x3bf817 = 0x4bb * 0x1 + 0x801 + -0xcbc;
    while (!![]) {
        switch (_0x346c55[_0x3bf817++]) {
        case '0':
            console[_0x1f346d(0x7b)](_0x4c96d8);
            continue;
        case '1':
            console[_0x1f346d(0x7b)](_0x101028);
            continue;
        case '2':
            var _0x65977d = _0x860db8[_0x1f346d(0x66)]('5', -0x586 + -0x2195 + -0x6 * -0x685);
            continue;
        case '3':
            console[_0x1f346d(0x7b)](_0x65977d);
            continue;
        case '4':
            var _0x56d39b = _0x860db8[_0x1f346d(0x76)]('5', -'2');
            continue;
        case '5':
            console[_0x1f346d(0x7b)](_0x56d39b);
            continue;
        case '6':
            var _0x544285 = [
                '10',
                '10',
                '10',
                '10',
                '10'
            ][_0x1f346d(0x7a)](parseInt);
            continue;
        case '7':
            var _0x4c96d8 = _0x860db8[_0x1f346d(0x68)]('5', 0x622 * -0x6 + 0x4a * 0x3 + 0x1 * 0x23f1);
            continue;
        case '8':
            console[_0x1f346d(0x7b)](_0x544285);
            continue;
        case '9':
            var _0x101028 = _0x860db8[_0x1f346d(0x6f)](_0x860db8[_0x1f346d(0x6f)](_0x860db8[_0x1f346d(0x77)], 0x6fb * 0x5 + 0x1ebf * 0x1 + -0x41a5), 0x209 * 0xa + 0x1314 + -0x276d);
            continue;
        }
        break;
    }
}());
*/
命令行工具
也可以通过命令行工具直接针对整个JS文件进行混淆
javascript-obfuscator input.js --output output.js --compact true --control-flow-flattening false
在线工具
官方还提供了一个在线的混淆工具,方便进行测试和学习
常见配置项
官方提供了很多混淆的配置项,来决定你代码的混淆程度,例如
compact代码是否在一行controlFlowFlattening控制流扁平化deadCodeInjection插入随机死代码debugProtection禁用开发者工具debugProtectionInterval无限进入debugger模式disableConsoleOutput禁用所有console
更多配置项请参考官方文档
注意事项
- 性能影响:混淆会增加代码的复杂度,对代码的性能产生一定影响,混淆越多性能越低,需要在安全和性能之间做好取舍
 - 调试困难:一旦代码被混淆,调试和维护将变得更加困难,难住的不仅仅是恶意用户,还有开发者
 
逆向
目前大部分网站都是基于Obfuscator开发框架的二次封装,可能会和原有框架逻辑略有不同
判定
Ob反混淆的代码结构很明显,一般分为三大块
- 声明大数组(密码表)
 - 对数组进行移位(密码表纠正)
 - 加密函数,进行代码解密
 
处理
网上有很多反混淆的工具,很方便做一些初级的解混淆。 https://tool.yuanrenxue.cn/decode_obfuscator https://deobfuscate.relative.im/ https://obf-io.deobfuscate.io/
Ob最大的难题是移除一些 格式化检查 和 内存爆破 问题,通常都是用正则表达式来检查,同时利用循环执行大正则实现内存溢出,导致页面不可用
常见位置
updateCookie, newState,等等
解决方式
hook正则表达式
RegExp.prototype.test = function(params) { console.log(params); debugger; return true; }既然是通过正则就行检查,那我们可以直接hook
RegExp函数不格式化代码 不格式化代码的话就不会触发一些一些检查,但相应的代码就会很难调试
删掉相关检查 找到相关的检查代码,将其删掉,这个很考验经验和眼力了