浅析JavaScript Obfuscator(一)
字数 1715 2025-08-06 08:35:30

JavaScript Obfuscator 混淆技术详解(一)

JavaScript Obfuscator Tool 简介

JavaScript Obfuscator 是一款用于混淆 JavaScript 代码的工具,通过多种变换技术使代码难以阅读和理解,同时保持功能不变。官网提供了丰富的混淆选项:

基础选项

  • Disable Console Output:禁用控制台全局调用所有脚本(默认false)
  • Self Defending:使代码抵抗格式化和变量重命名(默认false)
  • Debug Protection:使浏览器开发者工具中的调试器无法正常使用(默认false)

字符串变换选项

  • String Array:用特殊字符串数组替换原有字符串值(默认true)
  • String Array Rotate:使字符串数组进行位移(默认true)
  • String Array Shuffle:随机打乱字符串数组(默认true)
  • String Array Threshold:指定字符串值放入字符串数组的比例(默认0.8)
  • String Array Index Shift:对字符串数组调用的索引进行位移(默认true)
  • String Array Indexes Type:指定字符串数组调用的索引类型(默认Hexadecimal Number)
  • String Array Calls Transform:使字符串数组调用取值过程更复杂(默认false)
  • String Array Wrappers Count:指定字符串数组包装器的数量(默认1)
  • String Array Wrappers Type:指定字符串数组包装器的类型(默认variable)
  • String Array Wrappers Chained Calls:启用字符串数组包装器的链式调用(默认true)
  • String Array Encoding:指定字符串数组的编码方式(默认无)
  • Split Strings:将字符串拆分为指定长度的块(默认false)

标识符变换选项

  • Identifier Names Generator:指定标识符名称的生成方式(默认Hexadecimal)
  • Identifiers Prefix:指定全局标识符名称的前缀(默认无)
  • Rename Globals:混淆全局变量和函数名称(默认false)
  • Rename Properties:重命名属性名称(默认false)

其他变换选项

  • Compact:使代码变为一行(默认true)
  • Simplify:通过简化对额外的代码进行混淆(默认true)
  • Transform Object Keys:使对象的键进行变换(默认false)
  • Numbers To Expressions:使数字转换为表达式(默认false)
  • Control Flow Flattening:使代码控制流扁平化(默认false)
  • Dead Code Injection:在代码中插入随机的死代码块(默认false)

默认级别混淆特点分析

以Crypto-JS项目中的md5.js为例,默认级别混淆呈现以下特点:

特点一:十六进制化

所有十进制数都被转换为十六进制,变量名等标识符替换为以_0x开头的伪十六进制表示。

示例:

// 原始代码
for (var i = 0; i < 16; i++) {
    var offset_i = offset + i;
    var M_offset_i = M[offset_i];
    M[offset_i] = (
        (((M_offset_i << 8)  | (M_offset_i >>> 24)) & 0x00ff00ff) |
        (((M_offset_i << 24) | (M_offset_i >>> 8))  & 0xff00ff00)
    );
}

// 混淆后代码
for (var _0x546cf3 = 0x0; _0x546cf3 < 0x10; _0x546cf3++) {
    var _0x54e4d4 = _0x1effab + _0x546cf3,
        _0x323639 = _0x5e5b11[_0x54e4d4];
    _0x5e5b11[_0x54e4d4] = 
        (_0x323639 << 0x8 | _0x323639 >>> 0x18) & 0xff00ff |
        (_0x323639 << 0x18 | _0x323639 >>> 0x8) & 0xff00ff00;
}

特点二:字符串化

字符串类型的值使用字符串数组形式替换,属性访问从c.m改为c['m']形式。

示例:

// 原始代码
var MD5 = C_algo.MD5 = Hasher.extend({
    _doReset: function () {
        this._hash = new WordArray.init([
            0x67452301, 0xefcdab89,
            0x98badcfe, 0x10325476
        ]);
    }
});

// 混淆后代码
var _0x227d20 = _0xc3f48e['MD5'] = _0x38037[_0x509931(0x1c9)]({
    '_doReset': function() {
        var _0x148e27 = _0x509931;
        this[_0x148e27(0x1bb)] = new _0x522fc1[(_0x148e27(0x1c1))]([
            0x67452301, 0xefcdab89,
            0x98badcfe, 0x10325476
        ]);
    }
});

特点三:逻辑简化

主要对else语句块和return语句进行简化。

示例1:if-else简化

// 原始代码
if (typeof exports === "object") {
    module.exports = exports = factory(require("./core"));
}
else if (typeof define === "function" && define.amd) {
    define(["./core"], factory);
}
else {
    factory(root.CryptoJS);
}

// 混淆后代码
if (typeof exports === _0x4be726(0x1bf)) module[_0x4be726(0x1b7)] = exports = _0x3138cf(require(_0x4be726(0x1bc)));
else typeof define === _0x4be726(0x1ca) && define[_0x4be726(0x1af)] ? define([_0x4be726(0x1bc)], _0x3138cf) : _0x3138cf(_0x527611[_0x4be726(0x1b3)]);

示例2:return语句简化

// 原始代码
function (CryptoJS) {
    ...
    return CryptoJS.MD5;
}

// 混淆后代码
function (CryptoJS) {
    return function(_0x14f7a1) {
        ...
    }(Math), _0x43590a[_0x4e10a0(0x1cb)];
}

字符串数组取值机制

字符串数组的取值过程由三个主要函数构成:

1. 字符串数组定义函数

function _0x10e7() {
    var _0x4bbd32 = ['object', '2133236AnmzGt', 'init', '37782620StZnsn', 'floor', '2284455sDMAIQ', '_data', '18EBGUjf', 'sin', 'clone', 'extend', 'function', 'MD5', 'words', '899535gIwEuD', '_process', 'length', 'abs', 'call', '10rXFioH', '_createHmacHelper', 'amd', 'algo', '1037863FtBCAS', 'WordArray', 'CryptoJS', '4QLUdFg', 'sigBytes', 'Hasher', 'exports', 'lib', '_createHelper', 'HmacMD5', '_hash', './core', '1428792YZRUwn', '2351988oecqJc'];
    _0x10e7 = function() {
        return _0x4bbd32;
    };
    return _0x10e7();
}

2. 数组索引复位并取值函数

function _0x3126(_0x30a9d6, _0x49b9ba) {
    var _0x10e724 = _0x10e7();
    return _0x3126 = function(_0x3126ad, _0x475b54) {
        _0x3126ad = _0x3126ad - 0x1aa;
        var _0x45a84 = _0x10e724[_0x3126ad];
        return _0x45a84;
    }, _0x3126(_0x30a9d6, _0x49b9ba);
};

3. 字符串数组"洗牌"函数

(function(_0x2226ad, _0x1b76a1) {
    var _0x38ef3e = _0x3126,
        _0x1a727d = _0x2226ad();
    while (!![]) {
        try {
            var _0x11c77c = -parseInt(_0x38ef3e(0x1b1)) / 0x1 + -parseInt(_0x38ef3e(0x1c0)) / 0x2 + parseInt(_0x38ef3e(0x1c4)) / 0x3 * (-parseInt(_0x38ef3e(0x1b4)) / 0x4) + parseInt(_0x38ef3e(0x1ad)) / 0x5 * (-parseInt(_0x38ef3e(0x1be)) / 0x6) + parseInt(_0x38ef3e(0x1cd)) / 0x7 + parseInt(_0x38ef3e(0x1bd)) / 0x8 * (parseInt(_0x38ef3e(0x1c6)) / 0x9) + parseInt(_0x38ef3e(0x1c2)) / 0xa;
            if (_0x11c77c === _0x1b76a1) break;
            else _0x1a727d['push'](_0x1a727d['shift']());
        } catch (_0x4fda4d) {
            _0x1a727d['push'](_0x1a727d['shift']());
        }
    }
}(_0x10e7, 0x95e73));

这个函数通过不断将数组首项移到末项来打乱数组顺序,直到特定数学条件满足为止。其中包含的如2133236AnmzGt等特殊字符串用于作为数组位移操作停止的条件项。

参考

JavaScript Obfuscator 混淆技术详解(一) JavaScript Obfuscator Tool 简介 JavaScript Obfuscator 是一款用于混淆 JavaScript 代码的工具,通过多种变换技术使代码难以阅读和理解,同时保持功能不变。官网提供了丰富的混淆选项: 基础选项 Disable Console Output :禁用控制台全局调用所有脚本(默认false) Self Defending :使代码抵抗格式化和变量重命名(默认false) Debug Protection :使浏览器开发者工具中的调试器无法正常使用(默认false) 字符串变换选项 String Array :用特殊字符串数组替换原有字符串值(默认true) String Array Rotate :使字符串数组进行位移(默认true) String Array Shuffle :随机打乱字符串数组(默认true) String Array Threshold :指定字符串值放入字符串数组的比例(默认0.8) String Array Index Shift :对字符串数组调用的索引进行位移(默认true) String Array Indexes Type :指定字符串数组调用的索引类型(默认Hexadecimal Number) String Array Calls Transform :使字符串数组调用取值过程更复杂(默认false) String Array Wrappers Count :指定字符串数组包装器的数量(默认1) String Array Wrappers Type :指定字符串数组包装器的类型(默认variable) String Array Wrappers Chained Calls :启用字符串数组包装器的链式调用(默认true) String Array Encoding :指定字符串数组的编码方式(默认无) Split Strings :将字符串拆分为指定长度的块(默认false) 标识符变换选项 Identifier Names Generator :指定标识符名称的生成方式(默认Hexadecimal) Identifiers Prefix :指定全局标识符名称的前缀(默认无) Rename Globals :混淆全局变量和函数名称(默认false) Rename Properties :重命名属性名称(默认false) 其他变换选项 Compact :使代码变为一行(默认true) Simplify :通过简化对额外的代码进行混淆(默认true) Transform Object Keys :使对象的键进行变换(默认false) Numbers To Expressions :使数字转换为表达式(默认false) Control Flow Flattening :使代码控制流扁平化(默认false) Dead Code Injection :在代码中插入随机的死代码块(默认false) 默认级别混淆特点分析 以Crypto-JS项目中的md5.js为例,默认级别混淆呈现以下特点: 特点一:十六进制化 所有十进制数都被转换为十六进制,变量名等标识符替换为以 _0x 开头的伪十六进制表示。 示例: 特点二:字符串化 字符串类型的值使用字符串数组形式替换,属性访问从 c.m 改为 c['m'] 形式。 示例: 特点三:逻辑简化 主要对 else 语句块和 return 语句进行简化。 示例1:if-else简化 示例2:return语句简化 字符串数组取值机制 字符串数组的取值过程由三个主要函数构成: 1. 字符串数组定义函数 2. 数组索引复位并取值函数 3. 字符串数组"洗牌"函数 这个函数通过不断将数组首项移到末项来打乱数组顺序,直到特定数学条件满足为止。其中包含的如 2133236AnmzGt 等特殊字符串用于作为数组位移操作停止的条件项。 参考 JavaScript Obfuscator Tool (v2.9.0)