Published on

javascript-obfuscator —— 保护你的js代码

Authors
  • avatar
    Name
    Pony Ma
    Twitter

javascript-obfuscator —— 保护你的前端代码

JavaScript代码由于天然暴露在客户端的特性,很容易被人读取。在代码的安全性和知识产权日益重要的当下,保护JavaScript代码不被恶意用户轻易理解和篡改就显得尤为重要。

这就是 javascript-obfuscator 上场的时候了

什么是**javascript-obfuscator**

javascript-obfuscator 是一个开源的 JavaScript 混淆工具,他通过各种技术手段改变代码的外观和结构,而不影响其执行功能。使代码变得难以阅读和理解,从而增加了逆向工程的难度。

Github地址

JavaScript Obfuscator Tool

怎么实现混淆

javascript-obfuscator 通过以下几种方式来混淆代码:

  1. 变量名和函数名替换:将这些名字替换为难以理解的字符串,使代码不可读,隐藏其原始意图和功能。
  2. 字符串数组转换和加密:将代码中的字符串转移到一个数组中,在运行时动态解密,增加逆向难度。
  3. 控制流混淆:改变代码的执行流程,使逻辑结构变得复杂、难以追踪。
  4. 死代码注入:加入无用的代码片段,误导分析者。
  5. 环境检测:检测代码是否运行在某些特定环境(如浏览器的开发者工具)中,并根据需要改变代码行为或停止运行。
  6. 自我防御机制:如果检测到代码结构被修改(例如尝试美化代码)代码将改变行为或停止运行。

使用方式

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

在线工具

官方还提供了一个在线的混淆工具,方便进行测试和学习

JavaScript Obfuscator Tool

常见配置项

官方提供了很多混淆的配置项,来决定你代码的混淆程度,例如

  • compact 代码是否在一行
  • controlFlowFlattening 控制流扁平化
  • deadCodeInjection 插入随机死代码
  • debugProtection 禁用开发者工具
  • debugProtectionInterval 无限进入debugger模式
  • disableConsoleOutput 禁用所有console

更多配置项请参考官方文档

JavaScript Obfuscator Tool

注意事项

  • 性能影响:混淆会增加代码的复杂度,对代码的性能产生一定影响,混淆越多性能越低,需要在安全和性能之间做好取舍
  • 调试困难:一旦代码被混淆,调试和维护将变得更加困难,难住的不仅仅是恶意用户,还有开发者

逆向

目前大部分网站都是基于Obfuscator开发框架的二次封装,可能会和原有框架逻辑略有不同

判定

Ob反混淆的代码结构很明显,一般分为三大块

  1. 声明大数组(密码表)
  2. 对数组进行移位(密码表纠正)
  3. 加密函数,进行代码解密

处理

网上有很多反混淆的工具,很方便做一些初级的解混淆。 https://tool.yuanrenxue.cn/decode_obfuscator https://deobfuscate.relative.im/ https://obf-io.deobfuscate.io/

Ob最大的难题是移除一些 格式化检查内存爆破 问题,通常都是用正则表达式来检查,同时利用循环执行大正则实现内存溢出,导致页面不可用

常见位置

updateCookie, newState,等等

解决方式

  1. hook正则表达式

    RegExp.prototype.test = function(params) {
        console.log(params);
        debugger;
        return true;
    }
    

    既然是通过正则就行检查,那我们可以直接hook RegExp 函数

  2. 不格式化代码 不格式化代码的话就不会触发一些一些检查,但相应的代码就会很难调试

  3. 删掉相关检查 找到相关的检查代码,将其删掉,这个很考验经验和眼力了