浅析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等特殊字符串用于作为数组位移操作停止的条件项。