JS 逆向完整技能图谱
一、基础知识篇
1. 浏览器开发者工具
| 功能 | 用途 |
|---|---|
| Elements | 查看/修改 DOM 结构 |
| Console | 执行 JS、查看输出 |
| Network | 抓包、分析请求参数 |
| Sources | 查看/调试 JS 代码 |
| Application | 查看 Cookie、Storage |
2. 断点调试技巧
// 常用断点类型
1. 普通断点 - 点击行号
2. 条件断点 - 右键 → Add conditional breakpoint
3. XHR 断点 - 监听特定 URL 请求
4. DOM 断点 - 监听元素变化
5. 事件断点 - 监听 click、submit 等事件
示例:XHR 断点定位加密函数
Sources → XHR/fetch Breakpoints → 添加 URL 关键词(如 "api" 或 "sign")
→ 发起请求 → 自动断住 → 查看调用栈 Call Stack
二、代码分析篇
3. 搜索定位技巧
| 方法 | 快捷键/操作 | 适用场景 |
|---|---|---|
| 全局搜索 | Ctrl+Shift+F |
搜索关键词(sign、encrypt、token) |
| 请求参数搜索 | Network → 找到参数名 | 定位参数生成位置 |
| Hook 搜索 | 注入代码拦截 | 动态定位 |
| 调用栈回溯 | 断点后看 Call Stack | 找到函数调用链 |
4. Hook 技术 ⭐
原理:拦截/替换原生函数,监控调用
// Hook JSON.stringify - 监控所有 JSON 序列化
(function() {
var stringify = JSON.stringify;
JSON.stringify = function() {
console.log('JSON.stringify 被调用:', arguments);
console.trace(); // 打印调用栈
return stringify.apply(this, arguments);
};
})();
// Hook Cookie 设置
(function() {
var cookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
Object.defineProperty(document, 'cookie', {
set: function(val) {
console.log('设置 Cookie:', val);
debugger; // 自动断点
return [cookieDesc.set.call](http://cookieDesc.set.call)(document, val);
},
get: function() {
return [cookieDesc.get.call](http://cookieDesc.get.call)(document);
}
});
})();
// Hook setInterval/setTimeout - 定位定时器
(function() {
var _setInterval = setInterval;
setInterval = function(func, delay) {
console.log('setInterval 被调用, 延迟:', delay);
return _setInterval(func, delay);
};
})();
5. 常用 Hook 注入方式
| 方式 | 工具 | 特点 |
|---|---|---|
| Console 注入 | 浏览器控制台 | 简单快速,刷新失效 |
| 油猴脚本 | Tampermonkey | 持久化,自动注入 |
| 代理注入 | Fiddler/Charles | 可修改响应内容 |
| 浏览器插件 | 自己开发 | 最灵活 |
三、代码还原篇
6. 反混淆技术
常见混淆类型:
// 1. 变量名混淆
var _0x1a2b = function(_0x3c4d) { return _0x3c4d + 1; }
// 2. 字符串加密
var str = _0x5e6f('0x1'); // 实际是 "password"
// 3. 控制流平坦化
switch(state) {
case 0: state = 3; break;
case 3: state = 1; break;
case 1: state = 2; break;
// ...
}
// 4. 死代码注入
if (false) { /* 永远不执行的垃圾代码 */ }
// 5. eval 动态执行
eval(atob('YWxlcnQoMSk=')); // 解码后是 alert(1)
反混淆工具:
| 工具 | 用途 |
|---|---|
| de4js | 在线反混淆 |
| JStillery | AST 反混淆 |
| AST Explorer | 分析语法树 |
| 手动 + Console | 最可靠的方式 |
示例:还原字符串加密
// 混淆代码中通常有个解密数组
var _0x5e6f = ['password', 'username', 'token'];
// 找到这个数组,在控制台执行
console.log(_0x5e6f('0x1')); // 输出真实字符串
7. AST(抽象语法树)还原
// 使用 Babel 解析和还原
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
// 解析混淆代码
const ast = parser.parse(obfuscatedCode);
// 遍历并修改 AST
traverse(ast, {
// 还原字符串
StringLiteral(path) {
// 自定义还原逻辑
},
// 删除死代码
IfStatement(path) {
if (path.node.test.value === false) {
path.remove();
}
}
});
// 生成还原后的代码
const output = generator(ast);
四、环境模拟篇
8. 补环境
JS逆向中的"补环境"
补环境是指在 Node.js 或其他非浏览器环境中执行从网页提取的 JavaScript 代码时,模拟浏览器原生对象和 API 的过程。
为什么需要补环境?
当你从网站逆向出加密算法的 JS 代码后,想在本地运行它,会遇到一个问题:浏览器里有很多原生对象(如 window、document、navigator 等),而 Node.js 没有这些。如果代码里用到了这些对象,直接运行就会报错。
常见需要补的环境对象
| 对象 | 说明 |
|---|---|
window |
浏览器窗口对象 |
document |
DOM 文档对象 |
navigator |
浏览器信息(userAgent 等) |
location |
URL 信息 |
localStorage / sessionStorage |
本地存储 |
XMLHttpRequest / fetch |
网络请求 |
canvas |
画布(常用于指纹检测) |
示例 1:基础补环境框架
// 补 window 和 document
var window = global;
var document = {
cookie: '',
location: {
href: 'https://example.com',
hostname: '[example.com](http://example.com)',
protocol: 'https:'
},
createElement: function(tag) {
return {};
},
getElementById: function(id) {
return null;
}
};
window.document = document;
// 补 navigator
var navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
platform: 'Win32',
language: 'zh-CN'
};
window.navigator = navigator;
// 补 location
var location = document.location;
window.location = location;
// 然后在下面粘贴网站的加密JS代码...
示例 2:代理检测(Proxy 大法)
用 Proxy 自动检测代码访问了哪些未定义的属性:
function createEnvProxy(name) {
return new Proxy({}, {
get: function(target, prop) {
console.log(`[检测] ${name}.${prop} 被访问`);
if (!(prop in target)) {
target[prop] = createEnvProxy(`${name}.${prop}`);
}
return target[prop];
},
set: function(target, prop, value) {
console.log(`[检测] ${name}.${prop} 被设置为`, value);
target[prop] = value;
return true;
}
});
}
var window = createEnvProxy('window');
var document = createEnvProxy('document');
var navigator = createEnvProxy('navigator');
// 粘贴目标JS代码,运行后查看控制台输出
// 就知道需要补哪些属性了
示例 3:补 canvas 指纹
很多网站用 canvas 做浏览器指纹识别:
document.createElement = function(tag) {
if (tag === 'canvas') {
return {
getContext: function(type) {
return {
fillRect: function() {},
fillText: function() {},
measureText: function() {
return { width: 100 };
},
getImageData: function() {
return { data: new Uint8Array(10000) };
}
};
},
toDataURL: function() {
return 'data:image/png;base64,iVBORw0KGgo...'; // 固定值
}
};
}
return {};
};
补环境的一般流程
- 提取 JS 代码 — 从网页中找到加密/签名相关的代码
- 本地运行报错 — 直接跑会提示
xxx is not defined - 逐个补齐 — 根据报错信息,依次补上缺少的对象和方法
- 使用 Proxy 辅助 — 用代理快速定位所有被访问的属性
- 验证结果 — 对比本地生成的结果和浏览器中的结果是否一致
常用工具/库
- jsdom — 在 Node.js 中模拟 DOM 环境
- vm2 — 安全的沙箱执行 JS
- puppeteer — 直接用无头浏览器(不用补环境,但更慢)
补环境是 JS 逆向中最核心的技能之一,熟练掌握后可以脱离浏览器独立运行各种加密算法 🔧
9. 浏览器指纹对抗
// Canvas 指纹
HTMLCanvasElement.prototype.toDataURL = function() {
return 'data:image/png;base64,固定值...';
};
// WebGL 指纹
WebGLRenderingContext.prototype.getParameter = function(param) {
if (param === 37445) return 'Intel Inc.'; // VENDOR
if (param === 37446) return 'Intel Iris OpenGL'; // RENDERER
return [originalGetParameter.call](http://originalGetParameter.call)(this, param);
};
// AudioContext 指纹
AudioContext.prototype.createOscillator = function() {
return { /* 伪造的返回值 */ };
};
// 字体指纹
// 通过 CSS @font-face 检测,需要模拟特定字体列表
五、算法还原篇
10. 常见加密算法识别
| 特征 | 算法 |
|---|---|
| 32位十六进制 | MD5 |
| 40位十六进制 | SHA1 |
| 64位十六进制 | SHA256 |
| Base64 格式 | 可能是 AES/DES 加密后的 |
| 很长的数字 | 可能是 RSA |
= 结尾 |
Base64 编码 |
11. 算法还原示例
// 识别 MD5
function md5(string) {
// 看到 0x67452301, 0xefcdab89 等魔数 → MD5
var a = 0x67452301;
var b = 0xefcdab89;
var c = 0x98badcfe;
var d = 0x10325476;
// ...
}
// 识别 AES
// 看到 S-box (0x63, 0x7c, 0x77, 0x7b...) → AES
var Sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5...];
// 识别 Base64
// 看到 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
12. 自定义算法分析
// 示例:某网站的签名算法
function getSign(params, timestamp) {
var str = '';
// 1. 参数排序拼接
Object.keys(params).sort().forEach(function(key) {
str += key + '=' + params[key] + '&';
});
// 2. 加上时间戳和密钥
str += 'timestamp=' + timestamp + '&key=secretKey123';
// 3. MD5
return md5(str);
}
// 逆向时需要:
// 1. 找到这个函数
// 2. 确定参数排序规则
// 3. 找到密钥 (secretKey123)
// 4. 确定 MD5 还是其他哈希
六、高级技巧篇
13. RPC(远程过程调用)
原理:直接调用浏览器里的函数,不用补环境
// 浏览器端 - 注入 WebSocket 服务
var ws = new WebSocket('ws://[localhost:8080](http://localhost:8080)');
ws.onmessage = function(e) {
var data = JSON.parse([e.data](http://e.data));
// 直接调用页面里的加密函数
var result = window.encrypt(data.params);
ws.send(JSON.stringify({id: [data.id](http://data.id), result: result}));
};
# Python 端 - 调用
import websocket
import json
ws = websocket.WebSocket()
ws.connect('ws://[localhost:8080](http://localhost:8080)')
ws.send(json.dumps({'id': 1, 'params': 'test'}))
result = json.loads(ws.recv())
print(result)
14. 无头浏览器方案
# Playwright 示例
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = [browser.new](http://browser.new)_page()
page.goto('https://example.com')
# 直接在浏览器里执行 JS
sign = page.evaluate('window.getSign("test")')
print(sign)
# 或者拦截请求
page.on('request', lambda req: print(req.url, [req.post](http://req.post)_data))
15. WASM 逆向
// 导出 WASM 函数列表
WebAssembly.instantiate(wasmCode).then(module => {
console.log(Object.keys(module.instance.exports));
});
// 工具
// - wasm2wat: 转成文本格式
// - Ghidra: 反编译 WASM
// - Chrome DevTools: 可以调试 WASM
七、实战流程总结
┌─────────────────────────────────────────────────────────┐
│ JS 逆向完整流程 │
├─────────────────────────────────────────────────────────┤
│ 1. 抓包分析 │
│ └─ 找到加密参数 (sign, token, _signature 等) │
│ ↓ │
│ 2. 定位加密函数 │
│ ├─ 全局搜索关键词 │
│ ├─ XHR 断点 + 调用栈 │
│ └─ Hook 拦截 │
│ ↓ │
│ 3. 分析加密逻辑 │
│ ├─ 单步调试 │
│ ├─ 打印中间变量 │
│ └─ 识别算法类型 │
│ ↓ │
│ 4. 提取/还原代码 │
│ ├─ 直接复制 (简单情况) │
│ ├─ 反混淆 (混淆代码) │
│ └─ 算法重写 (复杂情况) │
│ ↓ │
│ 5. 本地运行 │
│ ├─ 补环境 (Node.js) ← 最常用 │
│ ├─ RPC (浏览器直接调用) │
│ └─ 无头浏览器 (兜底方案) │
│ ↓ │
│ 6. 集成到爬虫 │
│ └─ Python 调用 Node.js / 本地服务 │
└─────────────────────────────────────────────────────────┘
八、学习资源推荐
| 类型 | 资源 |
|---|---|
| 练习平台 | 猿人学、极验 Demo、各种 CTF |
| 工具 | Fiddler、Charles、mitmproxy |
| 浏览器 | Chrome DevTools(必须精通) |
| 社区 | 吾爱破解、52pojie、看雪论坛 |
学习建议:从 Hook → 断点调试 → 补环境 这条主线入手,覆盖 80% 场景后,再学反混淆和高级技巧!
💬 评论