关于 js:6. 网络与加密模块
一、AJAX
AJAX(Asynchronous JavaScript And XML) = 异步 JavaScript 与 XML(现在多为 JSON)
它允许网页在不重新加载整个页面的情况下,从服务器请求数据并更新页面内容。
主要用途:
-  
提交表单时无需刷新页面。
 -  
动态加载评论、搜索建议、分页数据。
 -  
网页内容分块加载,提高用户体验。
 
1. AJAX 的核心实现方式:XMLHttpRequest
 
这是最基础、兼容性最强的 AJAX 实现方式(Fetch 是后来的替代方案)。
基本用法:
let xhr = new XMLHttpRequest();
xhr.open("POST", "https://example.com/api", true);
xhr.setRequestHeader("Content-Type", "application/json");xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {console.log("响应结果:", xhr.responseText);}
};xhr.send(JSON.stringify({ username: "admin", password: "123" }));
 
🔹 let xhr = new XMLHttpRequest();
  创建一个新的 XMLHttpRequest 实例对象,用于发起 HTTP 请求(就是 AJAX 请求的载体)。
🔹 xhr.open("POST", "https://example.com/api", true);
  open() 不会发起请求,只是初始化。
-  
请求方法是
POST(表示发送数据) -  
请求地址是
https://example.com/api -  
第三个参数
true表示 异步请求(常见做法,浏览器默认) 
🔹 xhr.setRequestHeader("Content-Type", "application/json");
设置请求头。告诉服务器:我发送的是 JSON 数据。
🔹 xhr.onreadystatechange = function () { ... }
注册一个回调函数,监听请求的状态变化。
  xhr.readyState 会变化,值可能是:
| 值 | 含义 | 
|---|---|
| 0 | 请求未初始化(尚未调用 open) | 
| 1 | 请求已建立(调用了 open) | 
| 2 | 服务器收到请求头 | 
| 3 | 响应体正在下载 | 
| 4 | 完整响应已收到(最重要) | 
🔹 xhr.send(JSON.stringify({ username: "admin", password: "123" }));
-  
发起真正的请求,并把数据发给服务器。
 -  
send()参数必须是字符串(除非是FormData等对象)。 -  
JSON.stringify(...)将对象转换为 JSON 字符串。 
2. 浏览器和服务器交互
[浏览器 JS]
↓
构造参数(如用户名、密码)
↓
执行加密函数(如 AES、MD5、签名等)
↓
xhr.send(JSON.stringify(加密后的参数))
↓
[HTTP 请求:发送到服务器]
↓
[服务器]
接收密文 → 解密/验签 → 校验身份 → 返回数据
 
3. 重要属性与方法
| 属性 / 方法 | 作用说明 | 
|---|---|
open(method, url) | 初始化请求:设置请求方法和 URL | 
setRequestHeader() | 设置请求头(如 Content-Type、Token) | 
send(body) | 发起请求,可传请求体 | 
readyState | 请求状态:0-4(4为完成) | 
status | 响应状态码(如 200) | 
responseText | 响应体(字符串) | 
onreadystatechange | 监听状态变化 | 
onload | 请求成功触发 | 
onerror | 请求失败触发 | 
4. XHR 请求完整生命周期(readyState)
 
| readyState | 含义 | 
|---|---|
| 0 | UNSENT:未调用 open() | 
| 1 | OPENED:已调用 open() | 
| 2 | HEADERS_RECEIVED:收到响应头 | 
| 3 | LOADING:下载中 | 
| 4 | DONE:完成(可访问响应数据) | 
5. 逆向分析中的关键 Hook 点
在逆向中,XHR 经常是参数加密函数的入口。可以这样监听它的行为:
Hook open 和 send 方法:
// Hook open 方法
const rawOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async) {this._url = url; // 保存请求 URLreturn rawOpen.apply(this, arguments);
};// Hook send 方法
const rawSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (body) {console.log("拦截到 XHR 请求:");console.log("URL:", this._url);console.log("Body:", body);debugger; // 可在浏览器中断点分析调用堆栈return rawSend.call(this, body);
};
 
补充监听 setRequestHeader
const rawSetHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function (key, value) {console.log("Header:", key, value);return rawSetHeader.call(this, key, value);
};
 
总结
| 关键点 | 逆向目的 | 
|---|---|
open(url) | 获取接口地址 | 
send(body) | 拦截加密前参数 | 
setRequestHeader | 获取 token、sign | 
responseText | 如果返回数据加密,这里是解密入口 | 
| DevTools Initiator | 定位加密函数栈 | 
| Hook 方法 | 精准抓取请求参数与加密逻辑 | 
二、Fetch
Fetch 是现代浏览器提供的基于 Promise 的 HTTP 请求接口,它是 XMLHttpRequest 的替代方案,更简洁、灵活。
1. 基本用法(发起 GET/POST 请求)
GET 请求:
fetch("https://example.com/api/user?id=123").then(response => response.json()).then(data => console.log(data));
 
POST 请求:
fetch("https://example.com/api/login", {method: "POST",headers: {"Content-Type": "application/json"},body: JSON.stringify({username: "admin",password: "123"})
})
.then(response => response.json())
.then(data => console.log(data));
 
2. Fetch 请求参数结构
fetch(url, {method: "POST",                 // 请求方法headers: {                      // 请求头"Content-Type": "application/json"},body: JSON.stringify(data),     // 请求体credentials: "include",         // 是否携带 cookiemode: "cors"                    // 跨域策略
});
 
| 参数 | 说明 | 
|---|---|
method | 请求方法,如 GET/POST | 
headers | 请求头,如 token、Content-Type | 
body | 请求体(不能用于 GET) | 
credentials | 控制是否发送 Cookie:omit / same-origin / include | 
mode | 跨域策略:cors / same-origin / no-cors | 
3. 浏览器监听 Fetch 请求的方法
fetch 是浏览器原生函数,可以被重写 hook,这是分析参数加密的最佳入口之一。
Hook fetch 方法:
const rawFetch = window.fetch;
window.fetch = async function (...args) {console.log("拦截到 Fetch 请求:");console.log("URL:", args[0]);console.log("配置:", args[1]);debugger;  // 断点观察调用栈return rawFetch.apply(this, args);
};
 
这段代码可以:
-  
查看请求地址和加密后的 body
 -  
定位调用
fetch前的参数构造函数 -  
逆推出加密函数
 
4. Fetch 请求中的常见参数构造方式
在逆向中,最常见的参数类型包括:
1)JSON.stringify(data)(大多数接口使用)
body: JSON.stringify({ id: 123, sign: "xxxxx" })
 
2)FormData(用于表单上传、模拟 POST)
const form = new FormData();
form.append("username", "admin");
form.append("password", "123");
fetch(url, {method: "POST",body: form
});
 
3)URLSearchParams(urlencoded 表单格式)
const params = new URLSearchParams();
params.append("username", "admin");
params.append("password", "123");fetch(url, {method: "POST",headers: { "Content-Type": "application/x-www-form-urlencoded" },body: params
});
 
4)自定义加密参数
let sign = getSign(username, password, timestamp);  // 加密逻辑
body: JSON.stringify({username,password,sign
});
 
5. Fetch 的响应处理
fetch(url).then(res => res.text())       // 获取原始文本.then(text => {// 有些接口返回加密数据,这里可能是 base64、hex、AES密文console.log("原始响应:", text);});
 
如果发现响应是乱码或密文,说明:
-  
前端还会解密响应数据
 -  
需要
hook解密函数或继续抓栈定位 
总结
| 关键点 | 逆向目的 | 
|---|---|
fetch(url, config) | 拦截请求地址与参数配置 | 
body | 发现加密参数、定位加密函数 | 
headers | 检查 Token、Content-Type | 
debugger + Initiator | 精确找到构造请求的函数 | 
fetch hook | 实时监听,抓取请求参数 | 
三、XHR
-  
AJAX 是一种技术思想,用于“在网页不刷新的情况下与服务器通信”。
 -  
XHR(XMLHttpRequest) 是实现 AJAX 的核心 API(工具)。
 
AJAX 是什么?AJAX = Asynchronous JavaScript And XML
 中文叫“异步 JavaScript 与 XML”,但其实不只支持 XML,也可以传 JSON、HTML、纯文本。
本质上:AJAX 是一种在网页中异步请求数据,不刷新整个页面的技术方式。
它能实现的效果:
-  
用户点击按钮,页面不刷新,但后台偷偷向服务器发请求。
 -  
比如搜索框自动补全、无限滚动、点赞、评论提交等。
 
XHR 是什么?XHR(XMLHttpRequest) 是浏览器提供的一个内建对象,用来发 AJAX 请求。
可以用它来:
-  
发起 GET、POST 请求
 -  
设置请求头
 -  
监听响应
 -  
获取服务器返回的结果
 
示例代码:
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/data", true);
xhr.onreadystatechange = function () {if (xhr.readyState === 4 && xhr.status === 200) {console.log(xhr.responseText); // 响应内容}
};
xhr.send(); // 发起请求
 
XHR 与 fetch 的区别
| 对比点 | XHR | fetch | 
|---|---|---|
| 是否支持 Promise | 不支持 | 支持 | 
| 是否支持 Stream | 不支持 | 支持 | 
| 请求拦截难度 | 易 hook | 易 hook | 
| 跨域支持 |  支持设置 withCredentials |  使用 credentials | 
| 前端项目中使用频率 | 老项目中多见 | 现代项目主流 | 
四、请求监听方式
请求监听(Request Interception) 指的是:我们在浏览器中监听网页内部的网络请求,查看请求内容、加密参数、调用栈,甚至插桩劫持请求逻辑。
逆向的时候,往往看到的是:
xhr.send(加密参数);
 
但不知道这个加密参数是怎么来的。
监听请求就能:
-  
看到最终加密参数的结构
 -  
追踪调用栈,找到加密函数入口
 -  
判断是 XHR 还是 Fetch,决定 hook 方案
 -  
动态打断点、劫持参数、还原加密
 
方式 1:Chrome DevTools → Network 面板监听
功能:
-  
抓包请求(XHR / Fetch / WebSocket)
 -  
查看请求地址、请求体(payload)、响应内容
 -  
查看请求是通过什么方式发起的(XHR、Fetch、FormData)
 
操作:
-  
F12 打开控制台
 -  
切换到 Network
 -  
刷新页面或触发行为(比如点击按钮)
 -  
找到类型为
XHR或Fetch的请求 -  
查看:
-  
Request Payload(加密数据)
 -  
Headers(请求头)
 -  
Preview / Response(返回数据)
 -  
Initiator(调用栈,追到加密函数)
 
 -  
 
进阶:
-  
右键 → Break on request(触发断点)
 -  
Initiator → 点击函数跳转到源码
 
方式 2:Chrome DevTools → Hook XHR / Fetch
Hook XMLHttpRequest:
let open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async) {console.log("拦截请求地址:", method, url);this._url = url; // 保存地址,后面 send 中用return open.apply(this, arguments);
};let send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(body) {console.log("拦截请求体:", this._url, body);debugger; // 打断点分析 bodyreturn send.apply(this, arguments);
};
 
Hook Fetch:
let realFetch = window.fetch;
window.fetch = function(...args) {console.log("拦截 fetch 请求:", args);debugger;return realFetch.apply(this, args);
};
 
打开控制台,粘贴运行,然后执行页面动作就能抓到所有请求和加密参数。
方式 3:DevTools → XHR/Fetch 断点监听
操作步骤:
-  
打开 DevTools
 -  
切到 Sources → XHR/Fetch Breakpoints
 -  
点击右侧的 “Fetch/XHR”
 -  
执行页面操作,请求发出时自动断点
 -  
查看调用栈 / 参数加密点 / 函数名
 
五、FormData
FormData 是浏览器提供的一个类,用来构造 表单格式的请求体,可以模拟 HTML <form> 的提交行为,特别适合文件上传、键值对参数提交。
1. 本质理解:
执行:
let form = new FormData();
form.append("username", "admin");
form.append("password", "123456");
 
最终发送到服务器的内容是类似于:
Content-Type: multipart/form-data; boundary=----xxx------xxx
Content-Disposition: form-data; name="username"admin
------xxx
Content-Disposition: form-data; name="password"123456
------xxx--
 
它不是 JSON,不是纯字符串,是“表单格式”,带有 Content-Disposition 和 boundary 边界。
2. 使用方式
基本用法:
let form = new FormData();
form.append("key1", "value1");
form.append("key2", "value2");fetch("https://example.com/api", {method: "POST",body: form
});
 
上传文件:
let fileInput = document.querySelector("input[type=file]");
let form = new FormData();
form.append("username", "admin");
form.append("avatar", fileInput.files[0]); // 上传文件xhr.send(form); // 以 multipart/form-data 格式发送
 
3. 如何逆向分析 FormData 参数?
Step 1:在 DevTools → Network → 找到请求类型为 multipart/form-data
会看到:
------WebKitFormBoundary...
Content-Disposition: form-data; name="username"admin
 
Step 2:Sources 或 Console 中 Hook 或打断点
可以这样 hook:
let append = FormData.prototype.append;
FormData.prototype.append = function(key, value) {console.log("添加参数:", key, value);return append.apply(this, arguments);
};
 
这段代码会拦截所有 form.append(...) 的行为,打印每个参数,用于分析是否存在加密值或隐藏参数。
总结
| 特性 | 说明 | 
|---|---|
 自动设置 Content-Type | 浏览器自动添加 boundary,不能手动设置 Content-Type | 
| 支持文件上传 | 可以 .append("file", File对象) | 
 可用于 XHR 和 fetch | 都可以传入 form 对象作为 body | 
| 不能直接查看字符串内容 | form 不是普通对象,不能 JSON.stringify() | 
和 JSON 对比:
| 项目 | FormData | JSON | 
|---|---|---|
| 内容格式 | multipart/form-data | application/json | 
| 是否能传文件 | 是 | 否 | 
| 参数格式 | 键值对(string, Blob) | 任意 JS 对象 | 
能否直接 console.log | 否(需遍历) | 是 | 
六、URLSearchParams
URLSearchParams 是浏览器提供的内建类,用来构造、解析、修改 URL 查询字符串(?a=1&b=2 这样的格式)。
1. 使用场景
| 场景 | 示例 | 
|---|---|
| 构造请求参数 | POST 请求参数体是 application/x-www-form-urlencoded | 
| 解析 URL | 拿到 location.search 中的参数 | 
| 拼接参数 | 动态生成接口调用的 query 参数 | 
示例 1:构造 POST 请求体
let params = new URLSearchParams();
params.append("username", "admin");
params.append("password", "123456");fetch("https://example.com/api", {method: "POST",headers: {"Content-Type": "application/x-www-form-urlencoded"},body: params.toString()  // username=admin&password=123456
});
 
params.toString() 会自动序列化成表单形式的字符串,非常常见于登录、注册、搜索接口。
示例 2:解析 URL 参数
let url = "https://example.com/page?uid=1001&token=abcd123";
let searchParams = new URLSearchParams(new URL(url).search);console.log(searchParams.get("uid"));   // "1001"
console.log(searchParams.get("token")); // "abcd123"
 
2. 常用 API 一览
| 方法 | 说明 | 示例 | 
|---|---|---|
append(key, val) | 添加参数 | params.append("a", "1") | 
get(key) | 获取参数 | params.get("a") | 
set(key, val) | 修改/替换 | params.set("a", "2") | 
delete(key) | 删除参数 | params.delete("a") | 
has(key) | 是否存在参数 | params.has("a") | 
toString() | 序列化字符串 | a=1&b=2 | 
for...of 遍历 | 遍历所有参数 | for ([k,v] of params) | 
3. 和 FormData 对比
 
| 项目 | FormData | URLSearchParams | 
|---|---|---|
| 内容类型 | multipart/form-data | application/x-www-form-urlencoded | 
| 是否能传文件 | 是 | 否(只能字符串) | 
| 用于哪类请求 | 上传类 / 文件 / 图片 | 登录、搜索、加密参数 | 
能否用 toString() | 否(不能) | 是(生成 query 字符串) | 
4. 逆向中如何利用?
在 DevTools 中抓包时会看到:
POST https://example.com/api
Content-Type: application/x-www-form-urlencoded
Request Payload:
username=admin&password=123456&sign=ab12cd34
 
那么 80% 的概率就是代码里用了:
let p = new URLSearchParams();
p.append("username", "admin");
p.append("password", "123456");
p.append("sign", sign_func("admin123456"));
 
关键:hook append(),追踪 sign 来源!
Hook 分析技巧(追踪加密参数):
const _append = URLSearchParams.prototype.append;
URLSearchParams.prototype.append = function(key, value) {console.log("拦截 append 参数:", key, value);debugger; // 查看调用栈,追踪加密return _append.apply(this, arguments);
};
 
把这段代码贴到控制台后再执行请求,就能看到哪个参数是加密的,再反向分析加密函数。
实战例子:
let token = genToken(username, timestamp);
let params = new URLSearchParams();
params.append("username", username);
params.append("ts", timestamp);
params.append("token", token); // 加密生成xhr.send(params.toString());
 
抓包看到 token 是 asdu23ask1d21,就可以在 hook 中抓到 token 的生成值,逆向 genToken() 函数逻辑。
总结
| 点 | 内容 | 
|---|---|
| 用途 | 构造 query 参数字符串(a=1&b=2),用于请求体或 URL | 
| 类型 | application/x-www-form-urlencoded | 
| 特点 | 轻量、易用、可序列化、适合加密拼参 | 
| 逆向核心 | 通过 hook .append() 找加密参数的来源 | 
七、Headers 构造方式
Headers 是浏览器提供的接口,用来构造和操作 HTTP 请求头部(Headers),它决定了请求的“身份”、“类型”、“来源”等重要信息。
常见使用场景
-  
使用
fetch或XHR发起请求时,设置请求头 -  
模拟浏览器行为(如添加
User-Agent,Referer) -  
设置登录凭证(如
Authorization,Cookie) -  
模拟
Content-Type(用于表单、JSON、加密数据) 
1. 基本用法
1)创建 Headers 对象
let headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", "Bearer token_xyz");
 
2)传入 fetch 请求
fetch("https://example.com/api", {method: "POST",headers: headers,body: JSON.stringify({ username: "admin", password: "123" })
});
 
或者简写成:
fetch("https://example.com/api", {method: "POST",headers: {"Content-Type": "application/json","Authorization": "Bearer token_xyz"},body: JSON.stringify({ username: "admin", password: "123" })
});
 
两种写法等价,第二种是对象字面量自动转 Headers。
常用请求头字段
| Header | 说明 | 示例 | 
|---|---|---|
Content-Type | 请求体格式 | application/json、application/x-www-form-urlencoded、multipart/form-data | 
User-Agent | 浏览器身份 | 模拟浏览器 UA | 
Referer | 来源页面 | 某些接口必须带上指定页面 | 
Cookie | 携带登录信息 | 模拟登录态 | 
Authorization | token 或 Basic 认证 | Bearer token_xxx 或 Basic xxxxx== | 
X-Requested-With | 区分 AJAX | XMLHttpRequest | 
Headers API 方法
| 方法 | 说明 | 
|---|---|
append(name, value) | 添加头部 | 
set(name, value) | 设置或覆盖头部 | 
get(name) | 获取某个头部值 | 
has(name) | 判断是否存在 | 
delete(name) | 删除某个头部 | 
forEach() | 遍历所有头部 | 
示例:
headers.forEach((value, name) => {console.log(name + ": " + value);
});
 
和 XMLHttpRequest 配合
let xhr = new XMLHttpRequest();
xhr.open("POST", "/api", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", "Bearer token_xyz");
xhr.send(JSON.stringify({ data: 123 }));
 
2. 请求头伪造与逆向中作用
在 JS 逆向/爬虫过程中,经常要模拟浏览器请求头,否则接口会返回 403 或提示“非法请求”:
-  
User-Agent→ 模拟浏览器设备 -  
Referer→ 验证页面来源,防爬关键头部 -  
X-Requested-With: XMLHttpRequest→ AJAX 请求标识 -  
Token / Sign / Timestamp→ 加密参数中放头部 
3. 实战逆向技巧
1)DevTools 抓包对比
在 Chrome Network 面板中,点击请求 → Headers → Request Headers:
找到与正常请求差异的头部,例如:
X-Sign: ad83b23a3a      自定义加密头部
X-Time: 17123423452     时间戳
 
这类参数很可能是 JS 中加密生成的,需要你找到:
headers.append("X-Sign", genSign(data, ts));
 
2)Hook Headers.append
const oldAppend = Headers.prototype.append;
Headers.prototype.append = function(key, val) {console.log("Header添加:", key, val);debugger; // 查看调用栈,追踪生成过程return oldAppend.apply(this, arguments);
};
 
拦截加密头部(如 token、sign),反向追踪函数来源。
4. 项目案例
let headers = new Headers();
headers.append("X-Token", encryptToken(username, ts, key));
headers.append("X-Time", ts);
headers.append("Content-Type", "application/json");fetch("/api/protected", {method: "POST",headers: headers,body: JSON.stringify({ username, password })
});
 
请求被加密了,抓包看到 X-Token 是混淆值,就去找 encryptToken() 的源码,通常是 md5、AES、Base64 自定义函数。
总结
| 项目 | 内容 | 
|---|---|
| 用途 | 构造 HTTP 请求头,设置内容格式、认证、token 等 | 
| 类型 | JS 内建对象,可单独构造,也可直接传对象 | 
| 逆向重点 | 观察 Authorization / X-* 自定义字段是否加密 | 
| 调试技巧 | DevTools 抓包对比 + Hook append() | 
八、WebSocket 通信机制与劫持调试
WebSocket 是一种持久化的双向通信协议,适合实时数据交换(如聊天、弹幕、验证码行为上传等),逆向难点在于它绕过了传统 HTTP 抓包,必须使用调试和劫持手段来追踪。
1. WebSocket 与 HTTP 的区别
| 对比项 | HTTP | WebSocket | 
|---|---|---|
| 连接方式 | 请求一次就断开 | 一次连接,持续通信 | 
| 通信方向 | 客户端主动请求 | 双向,服务端也可主动发消息 | 
| 适合场景 | 表单提交、接口调用 | 聊天、弹幕、验证码行为上报 | 
| 请求协议 | http:// / https:// | ws:// / wss:// | 
| 抓包方式 | 普通 HTTP 抓包 | DevTools 或专用代理工具 | 
2. WebSocket 使用机制
JS 创建连接
let ws = new WebSocket("wss://example.com/ws");ws.onopen = () => {console.log("连接成功");ws.send(JSON.stringify({ action: "auth", token: "abcd1234" }));
};ws.onmessage = (e) => {console.log("接收到消息:", e.data);
};ws.onerror = (e) => {console.error("发生错误", e);
};ws.onclose = () => {console.log("连接关闭");
};
 
常见的 WebSocket 用途:
| 场景 | 数据内容 | 分析目标 | 
|---|---|---|
| 验证码行为上传 | 滑动轨迹、坐标、行为时间等 | 找到加密逻辑、格式、字段名 | 
| 聊天通信 | msg、uid、room | 伪造用户发送消息 | 
| 登录验证 | token / device_id | 模拟身份登录 | 
3. 如何抓包与调试 WebSocket?
方法一:Chrome DevTools → Network 面板 → WS
-  
打开网页
 -  
按 F12 打开 DevTools → Network → WS(WebSocket)
 -  
刷新页面
 -  
找到连接名(一般是
/ws或/socket) -  
点击查看:
 
-  
Messages:每一条收发的消息(原始/解码后)
 -  
Frames:查看 WebSocket 的原始数据包
 -  
Payload:可能是字符串、JSON、加密串
 
方法二:控制台劫持 WebSocket
拦截发送数据(hook send 方法):
const originSend = WebSocket.prototype.send;
WebSocket.prototype.send = function(data) {console.log("[WS 拦截] 发送内容:", data);debugger; // 追踪发送数据的调用栈return originSend.apply(this, arguments);
};
 
拦截接收数据(hook onmessage):
Object.defineProperty(WebSocket.prototype, 'onmessage', {set(fn) {const wrapper = function(event) {console.log("[WS 拦截] 收到消息:", event.data);debugger; // 查看接收的数据结构return fn.call(this, event);};this._onmessage = wrapper;return wrapper;},get() {return this._onmessage;}
});
 
4. 逆向分析技巧
Step1:确认连接地址和格式
let ws = new WebSocket("wss://xxx.com/socket?token=abc");
 
有些时候连接参数里就藏着 sign 或者 timestamp,是加密生成的!
Step2:拦截 send 内容
ws.send(JSON.stringify({type: "track",data: encrypt(轨迹数组)
}));
 
可以通过 send hook 抓到:
{"type": "track","data": "ajb19nASDAWJq1da=="
}
 
然后去找这个加密 encrypt() 函数。
Step3:解构收到的数据(解密)
ws.onmessage = (e) => {let msg = JSON.parse(e.data);let content = decrypt(msg.payload); // 模拟服务端逻辑
};
 
有些网站是前端解密数据展示,hook onmessage 可以观察服务端主动推送内容。
总结
| 项目 | 内容 | 
|---|---|
| 创建方式 | new WebSocket("wss://xxx") | 
| 通信方式 | send() 发送,onmessage 接收 | 
| 抓包方式 | Chrome DevTools → Network → WS | 
| 劫持方式 | Hook send() / onmessage | 
| 逆向重点 | 抓取并分析发送的数据结构与加密函数 | 
九、btoa / atob
 
| 函数 | 全称 | 作用 | 
|---|---|---|
btoa() | binary to ASCII | 把字符串 → Base64 编码 | 
atob() | ASCII to binary | 把 Base64 → 原始字符串 | 
Base64 是一种将二进制数据用 ASCII 字符表示的方法,用于:
-  
在网络上传输二进制数据(如图片、密钥)时变为字符串
 -  
编码数据避免乱码或安全字符(如 POST body、URL 参数)
 
1. 基本语法
btoa()
let encoded = btoa("hello");
console.log(encoded); // "aGVsbG8="
 
atob()
let decoded = atob("aGVsbG8=");
console.log(decoded); // "hello"
 
2. 实际用途举例
示例1:前端加密参数
let data = JSON.stringify({ username: "admin", password: "123" });
let encrypted = btoa(data); // 模拟加密
xhr.send(encrypted);        // 加密参数传输
 
示例2:反爬遇到参数是 Base64
let encoded = "eyJ1c2VybmFtZSI6ImFkbWluIiwicCI6IjEyMyJ9"; // 看起来像一串 base64
let decoded = atob(encoded); 
console.log(decoded); // {"username":"admin","p":"123"}
 
3. 注意事项
| 限制项 | 说明 | 
|---|---|
| 字符集限制 | 只能对 Latin1 范围(0~255)的字符使用,不能直接用于 Unicode 字符(中文) | 
| 中文处理 | 需要用 encodeURIComponent 和 decodeURIComponent 包裹,避免乱码 | 
处理中文的写法
// 正确加密中文
function encodeBase64(str) {return btoa(unescape(encodeURIComponent(str)));
}// 正确解密中文
function decodeBase64(base64) {return decodeURIComponent(escape(atob(base64)));
}let encrypted = encodeBase64("你好");
console.log(encrypted); // 5L2g5aW9
console.log(decodeBase64(encrypted)); // 你好
 
1)encodeURIComponent(str)
- 把 Unicode 字符串编码成 UTF-8 后,以 %XX 形式表示(URL 编码)。
 
示例:
encodeURIComponent("你好")  
// 输出:"%E4%BD%A0%E5%A5%BD"
 
"你好" → UTF-8 编码 → 字节:[0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD]
2)unescape(...)
-  
把
%XX表示的字节还原成 JS 字符串(每字符代表一个字节); -  
结果是一个 binary-like 字符串,符合
btoa()要求。 
unescape("%E4%BD%A0%E5%A5%BD")  
// 输出:乱码样式的字符串,每个字符的 charCode 在 0~255
 
3)btoa(...)
- 现在就可以 Base64 编码这个“binary-like”的字符串。
 
btoa(unescape(encodeURIComponent("你好")));  
// 输出:"5L2g5aW9"
 
这个字符串 "5L2g5aW9" 就是 "你好" 的 UTF-8 → Base64 编码。
总结
| 方法 | 含义 | 示例 | 
|---|---|---|
btoa(str) | 字符串 → base64 编码 | btoa('abc') → YWJj | 
atob(base64) | base64 → 解码字符串 | atob('YWJj') → abc | 
| 中文处理 | 必须先转 Unicode | encodeURIComponent + unescape | 
十、window.crypto
 
window.crypto(全名 Web Crypto API)是浏览器提供的原生加密接口,用于执行安全性更高的加密操作,包括:
-  
生成随机数(
crypto.getRandomValues) -  
加密/解密(AES、RSA)
 -  
哈希(SHA-256 等)
 -  
密钥导入、导出、生成
 
1. 基本结构
window.crypto.subtle // 提供加密函数
window.crypto.getRandomValues() // 安全随机数生成
 
-  
window.crypto.getRandomValues():安全随机数(强于 Math.random) -  
window.crypto.subtle:加密核心接口(SubtleCrypto) 
2. 核心用途场景
| 功能 | 方法 | 
|---|---|
| 加密 / 解密 | encrypt, decrypt | 
| 摘要(哈希) | digest | 
| 密钥生成 | generateKey | 
| 密钥导入 / 导出 | importKey, exportKey | 
| 签名 / 验签 | sign, verify | 
3. 常用函数详解
1)安全随机数(爬虫滑动轨迹等)
const array = new Uint8Array(10); // 创建一个长度为 10 的 Uint8Array 数组
crypto.getRandomValues(array); // 生成安全随机数并填充数组
console.log(array); // 打印安全随机数数组 
2)SHA-256 哈希(常见签名算法)
const data = new TextEncoder().encode("abc123");  
// 用 TextEncoder 把字符串 "abc123" 编码成 UTF-8 字节数组(Uint8Array)
// 输出类似:Uint8Array [97, 98, 99, 49, 50, 51]crypto.subtle.digest("SHA-256", data).then(hashBuffer => {  
// 使用 Web Crypto API 的 subtle.digest 方法对字节数组进行 SHA-256 哈希计算
// 结果是一个 ArrayBuffer(二进制缓冲区),表示哈希后的原始数据const hashArray = Array.from(new Uint8Array(hashBuffer));  // 把 ArrayBuffer 转换成 Uint8Array,再转成普通数组// 每个元素是一个 0~255 的整数,代表哈希值的每个字节const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');  // 把每个字节转成两位十六进制字符串,不足两位的用 '0' 补齐// 然后把所有十六进制字符串拼接成一个完整的哈希字符串(长度为64)console.log(hashHex);  // 打印最终的 SHA-256 哈希值,格式为 64 位十六进制字符串
}); 
3)AES-GCM 加密/解密(重点!常用于前端数据加密)
// 生成密钥
const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 },  // 指定生成 AES-GCM 密钥,密钥长度为 256 位true,                             // 密钥是否可以导出(true 表示可以)["encrypt", "decrypt"]           // 密钥的用途是加密和解密
);// 加密数据
const iv = crypto.getRandomValues(new Uint8Array(12)); // 12字节IV
const encoded = new TextEncoder().encode("hello world");const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv },          // 使用 AES-GCM 加密算法和生成的 IVkey,                              // 用之前生成的密钥进行加密encoded                           // 待加密的明文字节数组("hello world")
);// 解密数据
const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv },          // 使用 AES-GCM 解密算法和相同的 IVkey,                              // 使用之前生成的密钥进行解密ciphertext                        // 加密后的密文
);console.log(new TextDecoder().decode(decrypted)); // "hello world" 
4. 注意事项
| 限制 | 描述 | 
|---|---|
| 只能异步操作 | 所有方法都返回 Promise | 
| 二进制为主 | 输入输出都为 ArrayBuffer 类型 | 
| 无法直接处理字符串 | 需配合 TextEncoder 和 TextDecoder | 
5. JS逆向中出现的表现形式
经常会在网站中看到这样的代码片段:
await window.crypto.subtle.digest("SHA-256", data);
await window.crypto.subtle.encrypt({name: "AES-GCM", iv}, key, plaintext);
 
或者压缩/混淆后是:
e = window.crypto.subtle;
t = e.encrypt({name:"AES-GCM",iv:a}, n, r)
 
这些往往是在加密签名参数 w、payload、token 前执行的过程,必须跟踪输入输出、密钥、IV(偏移量)来逆推加密逻辑。
总结
| 功能 | 方法 | 用途 | 
|---|---|---|
| 随机数 | getRandomValues | 滑动轨迹、IV 生成 | 
| 哈希 | digest | 签名摘要,如 SHA-256 | 
| 加密 | encrypt | AES / RSA 加密请求参数 | 
| 解密 | decrypt | 解密服务器回传数据 | 
| 密钥导入 | importKey | 字符串密钥导入使用 | 
| 密钥生成 | generateKey | 通信过程生成密钥 | 
十一、自定义 base64 / md5 / AES / RSA 实现
1. 自定义 base64 编码/解码
 
base64 编码实现
function encodeBase64(str) {let charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  // 创建 Base64 编码字符集:包含大写字母、小写字母、数字和 "+"、"/" 两个符号。// 这个字符集是 Base64 编码中使用的标准字符集。let binaryStr = "";  // 用于存储将字符串转换成的二进制表示的变量。// 转为二进制字符串for (let i = 0; i < str.length; i++) {binaryStr += str.charCodeAt(i).toString(2).padStart(8, '0');}// 遍历输入字符串的每个字符// `charCodeAt(i)` 获取字符的 Unicode 编码(整数),然后用 `.toString(2)` 转换为二进制字符串// `padStart(8, '0')` 确保每个二进制字符串都是 8 位,不足的补充 '0'// 例如:'h' -> 104 (charCodeAt),转为 '01101000' (8 位)let base64Str = "";  // 用于存储最终的 Base64 编码结果。// 将二进制字符串按 6 位一组进行处理for (let i = 0; i < binaryStr.length; i += 6) {let chunk = binaryStr.slice(i, i + 6);  // 每次取 6 位二进制// 例如:'011010'、'000110'、'101100',等let index = parseInt(chunk, 2);  // 将 6 位二进制字符串转换为十进制整数// 例如:'011010' -> 26(十进制)base64Str += charSet.charAt(index);  // 使用得到的索引在 Base64 字符集(charSet)中找到相应字符// 例如:26 -> 'a'(字符集中的第 27 个字符)}// 添加 "=" 补齐while (base64Str.length % 4) {base64Str += "=";}// Base64 编码的最终结果长度必须是 4 的倍数,若不足,使用 '=' 字符进行补齐。// 例如:如果最后的编码长度是 6,则需要补充 2 个 '=',长度变为 8。return base64Str;  // 返回最终的 Base64 编码字符串
}console.log(encodeBase64("hello")); // "aGVsbG8="
 
base64 解码实现
function decodeBase64(base64Str) {let charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  // 创建 Base64 字符集,包含大写字母、小写字母、数字以及 '+' 和 '/' 两个符号。// 这是用于解码 Base64 字符串时的标准字符集。let binaryStr = "";  // 用于存储将 Base64 字符串转换成的二进制字符串。// 去掉补充的 "="base64Str = base64Str.replace(/=/g, "");  // 去掉字符串中的 '=' 补齐符号,因为它们只是为了保证 Base64 字符串的长度为 4 的倍数。// `replace(/=/g, "")` 会去除所有的 '='。// 转回二进制字符串for (let i = 0; i < base64Str.length; i++) {let index = charSet.indexOf(base64Str.charAt(i));  // `charSet.indexOf()` 获取当前 Base64 字符在字符集中的索引位置。// 例如:'a' 在字符集中的索引是 0,'b' 是 1,依此类推。binaryStr += index.toString(2).padStart(6, '0');  // 将索引转换为二进制字符串,并通过 `padStart(6, '0')` 确保每个二进制字符串是 6 位(不足补 '0')。// 例如:索引 0 转换为二进制是 '000000',索引 1 转换为二进制是 '000001',等等。}let decodedStr = "";  // 用于存储最终解码得到的原始字符串。// 将二进制字符串按 8 位一组解码for (let i = 0; i < binaryStr.length; i += 8) {let chunk = binaryStr.slice(i, i + 8);  // 每次取 8 位二进制(一个字节)// 例如:'01101000','01100101' 等decodedStr += String.fromCharCode(parseInt(chunk, 2));  // `parseInt(chunk, 2)` 将 8 位二进制转换成十进制整数。// `String.fromCharCode()` 将十进制整数转换为字符。// 例如:'01101000' -> 104 -> 'h','01100101' -> 101 -> 'e',等等。}return decodedStr;  // 返回解码后的原始字符串
}console.log(decodeBase64("aGVsbG8=")); // "hello"
 
2. 自定义 MD5 实现
 
MD5(消息摘要算法)是常用于数据完整性校验的哈希算法,通常用于生成数据的唯一标识符。
MD5 算法实现
function md5(str) {// 定义常量和初始化变量const hex_chars = "0123456789abcdef";let K = [];let s = [];let i = 0;let x = [];for (i = 0; i < 64; i++) {if (i < 16) K[i] = Math.pow(2, 32 - i) - 1;else K[i] = Math.pow(2, 64 - i) - 1;}// 对数据进行填充和分块处理str = str + String.fromCharCode(0x80);return str;
}
 
注意:  MD5 的实现较为复杂,这只是一个简化的框架。完整的 MD5 算法需要处理数据填充、分块、初始哈希值等多个步骤,通常我们会用现成的库如 CryptoJS 来实现 MD5。
3. 自定义 AES 加密/解密
 
AES(高级加密标准)是一种对称加密算法,用于加密和解密数据。你可以使用 JavaScript 提供的 Web Crypto API 进行 AES 加密,但也可以自定义实现。下面是自定义 AES 的实现方式:
AES 加密与解密实现(使用 crypto-js 库)
// 引入 CryptoJS 库
// <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1-crypto-js.js"></script>// AES 加密
function encryptAES(plaintext, key) {let encrypted = CryptoJS.AES.encrypt(plaintext, key);return encrypted.toString();
}// AES 解密
function decryptAES(ciphertext, key) {let bytes = CryptoJS.AES.decrypt(ciphertext, key);return bytes.toString(CryptoJS.enc.Utf8);
}let key = "mysecretkey12345";  // 密钥
let plaintext = "hello world"; // 明文let encrypted = encryptAES(plaintext, key);
console.log("Encrypted: ", encrypted);let decrypted = decryptAES(encrypted, key);
console.log("Decrypted: ", decrypted); // 输出: hello world
 
4. 自定义 RSA 加密/解密
 
RSA 是一种非对称加密算法,通常用于安全的密钥交换。与对称加密算法(如 AES)不同,RSA 使用一对密钥(公钥和私钥)。下面是 RSA 加密的实现:
RSA 加密/解密实现(使用 jsrsasign 库)
由于 RSA 算法涉及复杂的数学运算,JavaScript 并没有原生的实现。通常我们使用如 jsrsasign 等库来实现。
<script src="https://cdn.rawgit.com/kjur/jsrsasign/master/jsrsasign-all-min.js"></script><script>// 生成 RSA 密钥对var rsa = new RSAKey();rsa.generate(1024, '10001'); // 生成 1024 位的 RSA 密钥对// 使用公钥加密var encrypted = rsa.encrypt("hello world");console.log("Encrypted: ", encrypted);// 使用私钥解密var decrypted = rsa.decrypt(encrypted);console.log("Decrypted: ", decrypted); // 输出: hello world
</script>
 
总结
1)base64 编码/解码
-  
将二进制数据转换为 ASCII 字符串,常用于 HTTP 请求、数据传输中。
 -  
编码和解码都基于字符集,并通过二进制和 Base64 字符集的转换来实现。
 
2)MD5 哈希
-  
用于生成数据的唯一标识符(哈希值),通常用于校验数据完整性。
 -  
MD5会将输入数据转为 128 位哈希值,但具有碰撞漏洞,已经不再推荐用于安全应用。 
3)AES 加密/解密
-  
对称加密算法,广泛应用于数据保护。使用同一个密钥进行加密和解密。
 -  
实现时通常需要选择加密模式(如 CBC、GCM 等)。
 
4)RSA 加密/解密
-  
非对称加密算法,公钥用于加密,私钥用于解密。用于保护数据传输或密钥交换。
 -  
实现时涉及较为复杂的数学运算,常常依赖现成库(如
jsrsasign)。 
十二、流量还原与加密函数逆推
流量还原与加密函数逆推,其目标是分析网页或 APP 的加密请求数据是如何构造出来的,并通过调试还原出参数生成的全过程,以便模拟请求爬虫数据。
1. 目标与关键点
目标:
 通过抓包和调试,找出:
-  
哪些参数是加密的(如:
sign,token,w,encrypt_data等) -  
参数是如何加密的(逻辑路径、加密算法、依赖变量)
 -  
还原整个 JS 加密函数(逆向核心)
 
2. 核心步骤详解
步骤 1:打开 DevTools,抓取目标请求
-  
F12 打开 DevTools → 进入「Network」面板
 -  
找到目标接口(通常是 XHR 或 Fetch)
 -  
查看请求参数,判断哪些可能是加密参数
 
例子:
POST https://api.example.com/data
Form Data:
{"username": "admin","timestamp": 1715100000,"sign": "abc1234fdasf1adf3q1rq" ← 可疑(加密)
}
 
步骤 2:关键点定位:打断点或 console.log
重点跟踪:
-  
XMLHttpRequest.prototype.send -  
fetch -  
参数构造位置(FormData / JSON.stringify)
 -  
CryptoJS.MD5(),btoa(),AES.encrypt(),window.crypto等调用 
方法一:XHR 断点(推荐)
-  
在 DevTools → Source 面板,按快捷键
Ctrl+Shift+F -  
搜索关键词:
-  
send( -  
fetch( -  
XMLHttpRequest -  
sign,token,encrypt,AES,MD5,Base64 
 -  
 -  
选中一处调用 → 右键 "Add breakpoint"
 
方法二:Breakpoint → XHR/DOM 改写 hook(高级)
// 拦截 send 函数
const oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (body) {console.log("请求参数:", body); // 找加密入口debugger; // 打断点return oldSend.apply(this, arguments);
};
 
步骤 3:追踪加密函数
目标:从 send(body) 的 body 里,找到加密字段的生成路径,逐步往上追。
常见路径示例:
let params = {username: "admin",timestamp: Date.now(),
};
params.sign = md5(params.username + params.timestamp + "secret_key");xhr.send(JSON.stringify(params));
 
要逐步分析:
-  
sign 是怎么计算的?
 -  
有没有 secret_key?在哪里?
 -  
是 md5、AES、RSA 还是自定义加密?
 
步骤 4:控制台调试还原逻辑
在控制台里调用关键函数,手动试验参数是否能得到一致结果:
// 控制台验证
md5("admin1715100000secret_key") === 请求中 sign
 
验证成功 → 说明已经逆向出加密逻辑
步骤 5:封装爬虫逻辑
# 用 Python 模拟构造请求参数
import hashlib, time, requeststimestamp = str(int(time.time() * 1000))
sign = hashlib.md5(("admin" + timestamp + "secret_key").encode()).hexdigest()data = {"username": "admin","timestamp": timestamp,"sign": sign,
}res = requests.post("https://api.example.com/data", json=data)
print(res.text)
 
3. 实战补充
1)美化/格式化混淆代码
-  
页面压缩严重时,使用
Ctrl + P→ 搜索.js -  
使用 Prettify (右键 → "格式化源代码") 重排代码结构
 
 2)使用 debugger 动态插入断点
if (param.key === "target") {debugger;
}
 
3)Hook 常用加密函数
// Hook CryptoJS.MD5
CryptoJS.MD5 = (function (oldFn) {return function (input) {console.log("MD5 输入:", input);const res = oldFn(input);console.log("MD5 输出:", res.toString());return res;};
})(CryptoJS.MD5);
 
4. 常见加密参数入口点
| 参数名 | 常见算法 | 来源字段 | 
|---|---|---|
sign | md5 / hmac | token + 时间戳 + secret | 
w | AES+Base64 | 行为参数、滑动轨迹 | 
token | RSA / AES | 登录时保存的密钥 | 
x-token | window.crypto | WebCrypto 生成非对称密钥 | 
5. 总结流程图
DevTools - Network 抓包↓
找出加密参数(如 sign)↓
Sources 中全局搜索 send / fetch / sign↓
打断点 + 控制台 log 参数↓
分析加密过程(追函数 + 算法 + 依赖变量)↓
用 Python 模拟复现(MD5/AES/RSA 等)
