一、引言:混淆的本质与分类
在前端安全与反爬对抗的战场上,JavaScript 代码混淆是一道常见的护城河。简单来说,混淆通过变换代码结构、隐藏逻辑来增加逆向难度。目前主流的混淆手段可分为三类:
- 定制类混淆:各大安全厂商或团队自研方案,如某验、某盟,通性少、难度高,依赖具体场景分析。
- ob 系加壳器:基于开源框架obfuscator.io二次开发,通过配置项组合实现多样化混淆,是本文重点。
- Function 系列:如 AAencode、jjencode、jsfuck 等 “远古” 方案,通过奇葩语法将代码压缩为单函数或符号堆砌,现已逐渐淘汰。
二、obfuscator.io:开源混淆框架的集大成者
2.1 核心功能与配置项
obfuscator.io 提供了 30 + 可配置选项,覆盖变量、控制流、代码结构等多个维度:
-
变量与字符串混淆
stringArray
:将字符串字面量提取到数组,如"hello"
变为_0x1234[0x1]
。identifierNamesGenerator
:生成十六进制(_0xa1b2c3
)、混淆短名(a/b/c
)等变量名。transformObjectKeys
:混淆对象属性名,如obj.prop
变为obj['_0x456d']
。
-
控制流与结构混淆
controlFlowFlattening
:控制流平坦化,将条件分支转化为状态机,示例如下:javascript
// 原始代码 if (a > b) doA(); else doB(); // 混淆后 while (!![]) { switch (_0x1a2b3c[++_0xindex]) { case '0': doA(); continue; case '1': doB(); continue; } break; }
deadCodeInjection
:注入随机死代码块,增加代码体积(最多 200%)和分析噪音。
-
防御性混淆
debugProtection
:干扰调试工具,触发开发者工具卡顿或异常。selfDefending
:使混淆代码对格式化、反混淆工具敏感,轻微修改即失效。
2.2 预设配置:一键切换混淆强度
框架提供四种预设,快速适配不同需求:
预设 | 特点 | 适用场景 |
---|---|---|
default | 压缩代码,基础字符串混淆 | 日常代码保护 |
low | 轻量混淆,保留性能 | 对速度敏感的场景 |
medium | 控制流平坦化 + 死代码注入 | 中等安全需求 |
high | 全功能混淆,牺牲性能最大化安全 | 核心逻辑防护 |
示例: 高混淆配置下,stringArray
结合rc4
编码与多层包装器,字符串还原难度激增:
javascript
// 原始字符串
var msg = "secret";
// 高混淆后
var _0x123 = ['YmFzZTY0', 'rc4', ...]; // 混合编码
function _0x456(_0xarg) {
return _0xdecrypt(_0x123[_0xarg ^ 0x1], _0xkey); // 多层函数包装
}
console[_0x456(0x2)](_0x456(0x3));
三、动态网页中的 ob 混淆实战
3.1 场景分析:Cookie 反爬与动态生成
页面通过混淆后的 JS 生成加密 Cookie(如sign
),请求携带该 Cookie 才能正常访问。抓包发现 JS 文件经 ob 混淆处理,特征包括:
- 大量十六进制变量名(
_0x3bfa
、_0x4c2d
) - 字符串数组与多层函数嵌套
- 控制流平坦化导致调试困难
3.2 逆向四步曲
1. 初步解混淆:格式化与断点定位
- 使用在线工具(如JS Beautifier)格式化代码,识别关键函数(如
generateSign
)。 - 在
document.cookie
赋值处设置断点,追踪 Cookie 生成逻辑。
2. 字符串数组还原
通过stringArrayIndexShift
和stringArrayRotate
参数分析,编写脚本还原字符串数组:
javascript
// 提取混淆数组与索引逻辑
const _0xarray = ['a', 'b', 'c']; // 实际为混淆后的数组
const _0xshift = 0x2; // 索引偏移量
function getString(index) {
return _0xarray[(index - _0xshift) % _0xarray.length];
}
3. 控制流逆推
针对平坦化的switch
结构,绘制状态转移图,将线性代码还原为分支逻辑:
javascript
// 混淆后的控制流
while (!![]) {
switch (_0xstate++) {
case 0: _0xdoA(); break;
case 1: _0xdoB(); break;
}
}
// 还原后
if (condition) _0xdoA(); else _0xdoB();
4. 模拟执行与验证
使用 Node.js 环境模拟 JS 执行,补全缺失的浏览器对象(如window
、document
):
python
import js2py
ctx = js2py.EvalJs()
ctx.execute(obfuscated_code)
sign = ctx.generateSign(data) # 调用混淆后的生成函数
四、处理动态代码的核心概念
4.1 Cookie 反爬的常见形式
- 时效性 Cookie:每次请求生成唯一标识(如
uuid
+ 时间戳) - 签名 Cookie:通过
HMAC-SHA256
等算法对请求参数签名 - 防御性 Cookie:混淆生成逻辑,配合调试检测(如
debugger
关键词拦截)
4.2 动态性的本质
动态代码的核心特征是逻辑随时间 / 上下文变化,常见手段包括:
- 运行时生成代码(
eval
、new Function
) - 基于环境检测的差异化逻辑(UA、屏幕尺寸、浏览器指纹)
4.3 跟断技巧与工具
- 分阶段调试:先定位关键函数,再逐层深入内部逻辑。
- 工具链组合:
- Chrome DevTools:断点调试、Scope 变量监控
- js-beautify:代码格式化
- python-js2py/Node.js:无头环境模拟执行
- 特征匹配:通过
setInterval
、Math.random
等关键词快速定位混淆逻辑入口。
五、避坑指南与最佳实践
- 避免硬编码逆向:优先分析混淆规律(如字符串数组偏移),而非逐行翻译。
- 利用框架特性:obfuscator.io 的
identifierNamesCache
可复用变量名,便于多文件统一混淆。 - 性能与安全平衡:生产环境建议采用
medium
预设,在安全与加载速度间取舍。 - 合规性提醒:逆向他人代码需遵守《网络安全法》,仅限合法场景使用。
六、总结
obfuscator.io 作为开源混淆的标杆,通过丰富的配置项提供了从代码压缩到深度防护的全链条能力。在动态网页处理中,掌握其核心混淆机制(如字符串数组、控制流平坦化)是突破反爬的关键。未来,随着 WebAssembly 与动态编译技术的普及,混淆与反混淆的对抗将更加激烈,但核心思路始终围绕模式识别与环境模拟展开。