JavaScript 混淆逆向:把 obfuscator.io 这类混淆的前端源码还原、并安全地自部署的方法论。核心认知:混淆代码运行时自己会解码,原样就能跑——自部署 ≠ 还原成人类可读源码。反混淆只在需要看懂/修改某段逻辑、或拆掉防篡改/防自部署"炸弹"时才必要。

obfuscator.io 混淆的固定结构(识别特征)

var _0x34a696 = _0xd833;                       // 别名
function _0xd833(idx) { idx -= 0x1cf; return _0x441b()[idx]; }  // 解码函数 = 数组查表
function _0x441b(){ var a = ['str1','str2',...]; ...; return a; } // 字符串数组
(function(arr, target){                          // 旋转器:while(!![]){parseInt(...)} 校验和
  while(!![]){ try { if (calc === target) break; else arr.push(arr.shift()); } catch { ... } }
}(_0x441b, 0xc5b96));
  • 十六进制变量名 _0x[hex]、字符串数组、旋转器、解码函数 = obfuscator.io 指纹
  • 关键字符串(域名、URL、debugger 等)还会被字符类 .replace(/[...]/g,"") 二次加密,所以解码函数返回的仍是密文

反混淆的三种策略(按可靠性排序)

1. 运行时解码映射(最稳):解码函数 _0xd833(idx) 本质是纯函数查表。把"字符串数组函数 + 旋转器 IIFE + 解码函数"这三段从源文件提出,在 Node 里真实运行(旋转器会完成数组旋转),然后对全文件所有 _0x[hex](0x[hex]) 调用建 index → 字符串 映射表,再批量替换。正则方案想模拟旋转器是不稳定的——直接用运行时最可靠。项目脚本:toolonline.net/_deobf/decode2.js(自动检测解码函数与数组函数名)。

2. webcrack(全自动 AST 反混淆):npx webcrack file.js -o out/,自动处理字符串数组、旋转器、死代码、控制流扁平化、self-defending/debug-protection。输出可读且能运行。对 HTML 的内联脚本需先抽出 <script> 内容再跑(webcrack 直接吃 HTML 会报错)。

3. 正则 DIY(只适合最简场景,危险):对简单数组替换可用,但会把字符串数组值内联进旋转器的校验和计算里,把 parseInt(_0xbec1(0x100)) 变成 parseInt('createApp') → NaN → 死循环。见坑位笔记。

自部署完整工作流

  1. 只保留要动的东西,其余原样运行:混淆 JS 直接跑没问题
  2. 找硬依赖:是否调后端 API(webApiSiteHost)?纯前端工具可本地跑,有后端功能的要么重写要么砍掉
  3. 拆自托管炸弹:domainLock 跳转、debugProtection、cookie 域名锁、防 iframe(见防盗链_防克隆跳转机制 (preventCheat))
  4. 替换域名引用:全局 webApiSiteHost/staticSiteHost/mainWebSiteHost、静态资源 CDN 地址
  5. 验证:本地起静态服务器,用 Playwright 钩子拦 location 跳转并抓栈(toolonline.net/_deobf/hook_redirect_test.py),确认不跳转、无 JS 报错

关键教训

  • 永远从服务器重新下载 fresh 原始文件再做后续处理——本地被反混淆改过的文件不可当"原始版"
  • 反混淆要先做全量 node --check 语法校验再上线
  • 页面"卡死"先用 document.readyState / page.evaluate 判断是主线程死循环还是仅加载慢,别盲目归因

相关


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Web