当前位置: 首页 > news >正文

关于 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含义
0UNSENT:未调用 open()
1OPENED:已调用 open()
2HEADERS_RECEIVED:收到响应头
3LOADING:下载中
4DONE:完成(可访问响应数据)

5. 逆向分析中的关键 Hook 点

在逆向中,XHR 经常是参数加密函数的入口。可以这样监听它的行为:

Hook opensend 方法:

// 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 的区别

对比点XHRfetch
是否支持 Promise 不支持 支持
是否支持 Stream 不支持 支持
请求拦截难度 易 hook 易 hook
跨域支持 支持设置 withCredentials 使用 credentials
前端项目中使用频率老项目中多见现代项目主流

四、请求监听方式

请求监听(Request Interception) 指的是:我们在浏览器中监听网页内部的网络请求,查看请求内容、加密参数、调用栈,甚至插桩劫持请求逻辑。

逆向的时候,往往看到的是:

xhr.send(加密参数);

但不知道这个加密参数是怎么来的。

监听请求就能:

  • 看到最终加密参数的结构

  • 追踪调用栈,找到加密函数入口

  • 判断是 XHR 还是 Fetch,决定 hook 方案

  • 动态打断点、劫持参数、还原加密

方式 1:Chrome DevTools → Network 面板监听

功能:

  • 抓包请求(XHR / Fetch / WebSocket)

  • 查看请求地址、请求体(payload)、响应内容

  • 查看请求是通过什么方式发起的(XHR、Fetch、FormData)

操作:

  1. F12 打开控制台

  2. 切换到 Network

  3. 刷新页面或触发行为(比如点击按钮)

  4. 找到类型为 XHRFetch 的请求

  5. 查看:

    • 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 断点监听

操作步骤:

  1. 打开 DevTools

  2. 切到 SourcesXHR/Fetch Breakpoints

  3. 点击右侧的 “Fetch/XHR”

  4. 执行页面操作,请求发出时自动断点

  5. 查看调用栈 / 参数加密点 / 函数名


五、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-Dispositionboundary 边界。

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对象)
 可用于 XHRfetch都可以传入 form 对象作为 body
 不能直接查看字符串内容form 不是普通对象,不能 JSON.stringify()

和 JSON 对比:

项目FormDataJSON
内容格式multipart/form-dataapplication/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 对比

项目FormDataURLSearchParams
内容类型multipart/form-dataapplication/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),它决定了请求的“身份”、“类型”、“来源”等重要信息。

常见使用场景

  • 使用 fetchXHR 发起请求时,设置请求头

  • 模拟浏览器行为(如添加 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/jsonapplication/x-www-form-urlencodedmultipart/form-data
User-Agent浏览器身份模拟浏览器 UA
Referer来源页面某些接口必须带上指定页面
Cookie携带登录信息模拟登录态
Authorizationtoken 或 Basic 认证Bearer token_xxxBasic xxxxx==
X-Requested-With区分 AJAXXMLHttpRequest

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 的区别

对比项HTTPWebSocket
连接方式请求一次就断开一次连接,持续通信
通信方向客户端主动请求双向,服务端也可主动发消息
适合场景表单提交、接口调用聊天、弹幕、验证码行为上报
请求协议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

  1. 打开网页

  2. 按 F12 打开 DevTools → Network → WS(WebSocket)

  3. 刷新页面

  4. 找到连接名(一般是 /ws/socket

  5. 点击查看:

  • 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 字符(中文)
中文处理需要用 encodeURIComponentdecodeURIComponent 包裹,避免乱码

处理中文的写法

// 正确加密中文
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
中文处理必须先转 UnicodeencodeURIComponent + 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 类型
无法直接处理字符串需配合 TextEncoderTextDecoder

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
加密encryptAES / 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,抓取目标请求

  1. F12 打开 DevTools → 进入「Network」面板

  2. 找到目标接口(通常是 XHR 或 Fetch)

  3. 查看请求参数,判断哪些可能是加密参数

例子:

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 断点(推荐)

  1. 在 DevTools → Source 面板,按快捷键 Ctrl+Shift+F

  2. 搜索关键词:

    • send(

    • fetch(

    • XMLHttpRequest

    • sign, token, encrypt, AES, MD5, Base64

  3. 选中一处调用 → 右键 "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. 常见加密参数入口点

参数名常见算法来源字段
signmd5 / hmactoken + 时间戳 + secret
wAES+Base64行为参数、滑动轨迹
tokenRSA / AES登录时保存的密钥
x-tokenwindow.cryptoWebCrypto 生成非对称密钥

5. 总结流程图

DevTools - Network 抓包↓
找出加密参数(如 sign)↓
Sources 中全局搜索 send / fetch / sign↓
打断点 + 控制台 log 参数↓
分析加密过程(追函数 + 算法 + 依赖变量)↓
用 Python 模拟复现(MD5/AES/RSA 等)

相关文章:

  • FlySecAgent:——MCP全自动AI Agent的实战利器
  • C# 通过ConfigurationManager读写配置文件App.Config
  • 场馆订 场馆预订平台 数据库设计
  • 【Tools】VScode使用CMake构建项目
  • 前端Web开发HTML5+CSS3+移动web(基础-flex)
  • 【计算机视觉】基于Python的相机标定项目Camera-Calibration深度解析
  • 学习通刷课稳定版(美化面板+完全免费)
  • 大学之大:苏黎世大学2025.5.11
  • OpenWrt开发第7篇:OpenWrt配置支持Web界面
  • GC垃圾回收
  • 「OC」源码学习—— 消息发送、动态方法解析和消息转发
  • 【RP2350】香瓜树莓派RP2350之USB虚拟串口
  • 操作系统 : 线程同步与互斥
  • 深入浅出之STL源码分析7_模版实例化与全特化
  • 「银河通用」创始人王鹤:人形机器人跳舞是预先编程,马拉松是遥控操作!
  • 【PostgreSQL系列】PostgreSQL性能优化
  • java加强 -Collection集合
  • HTML5表格语法格式详解
  • [Java实战]Spring Boot 中Starter机制与自定义Starter实战(九)
  • 端口号被占用怎么解决
  • 微软将在全球裁员6000人,目标之一为减少管理层
  • 上海市重大工程一季度开局良好,崇明线等按既定计划加快建设
  • 从这些电影与影像,看到包容开放的上海
  • 著名军旅作家、文艺评论家周政保逝世,享年77岁
  • 人民日报读者点题·共同关注:今天我们为什么还需要图书馆?
  • 上海杨浦:优秀“博主”购房最高可获200万补贴