第一章 哪里可以加密
第一节 每次请求可以携带什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| > 回顾网站数据交换的过程(假设高加密的网址): ①页面收集数据: pageindex, total, searchkey,...,时间戳,特定**鉴权信息**[加密A] °页面发送数据给==>后端° ②后端处理数据: pageindex,...,对鉴权信息`解密A`并验证===>**返回数据**[加密B] °后端返回数据给==>前端° ③页面拿到数据: 对后端给的数据`解密B`(抓包看到的),并渲染到页面上
> 每次请求可以携带什么[后面为在开发者工具的具体位置]**加密A** 1.正常请求参数(page, total,keyVale..)--->请求参数[载荷] 2.用户信息(username,userpassword...)--->Cookie[标头.Cookie/浏览器.内存.存储.Cookie] 3.用户浏览器信息(UA,Sign,TimeStamp..)--->请求头[标头.请求标头] > 服务器返回什么[后面为在开发者工具的具体位置]**加密B** 4.响应数据(data,statecode..)--->[响应]
> 由此推出 也就是上述这4个地方可以加密,我们就只需要观察这4个地方是否有加密,有我们找JS解密,即可模拟发送请求
|
第二节 可以加密的地方
1.2.1 误区纠正GET和POST
- 查询字符串参数: 就是拼接在URL?后面的内容, 只是浏览器为了展示在, “载荷”一栏中展示, 不要错认为是表单数据了!!!
- GET请求: 点击”载荷”一栏后看到的是 “查询字符串参数” ; 这个参数是拼接在网址后面的, 在 link
- POST请求: 点击”载荷”一栏后看到的是 “表单数据” ; 也可以有 “查询字符串参数” link
1.2.2 四大加密位置




第二章 定位加密—DOM/XHR
第一节 DOM断点
2.1.1 概念解析
1
| **用户在页面触发事件** --> 构造请求对象 --> 请求拦截器 --> [加密数据] --> 发送请求 --> 返回数据 --> 响应拦截器 --> 请求成功的回调 --> 展示到开发者工具包中
|
- $ 概念: 使用 DOM事件断点,你可以指定在哪些 DOM 事件上设置断点,比如点击(click)、改变(change)、加载(load)等。一旦设置了 DOM 事件断点,当相应的事件被触发时,浏览器会自动在触发事件的 JavaScript 代码行上暂停执行
- $ 特点: 执行的比较靠前, 往后找, 距离加密函数比较远
2.1.2 实战DOM断点调试
需求: 找到 link 加密的位置
- 选中需要要定位的元素, 右侧 “事件监听器” 选中click 下的文件一个个进去下断点
- click下可能不止一个文件
- 文件不一定是JS, 如下就是HTML, 但调试下一步, 就会进到
Passprt.Login()方法


第二节 XHR断点
XHR可能不是最快的, 但是一定能定位到加密的方法
2.2.1 概念解析
1
| 用户在页面触发事件 --> 构造请求对象 --> 请求拦截器 --> **发送请求** --> 返回数据 --> 响应拦截器 --> 请求成功的回调 --> 展示到开发者工具包中
|
- $ 概念: XMLHttpRequest, 浏览器自动帮你拦截 “指定URL” 发请求的 “send方法”
- $ 特点: 往前找, 栈底找
- & 说明:
- ①XHR(XMLHttpRequest)是浏览器原生API, 底层通信协议实现
- ②无论是ajax还是axios等其底层都是用的XHR发请求, 浏览器中发请求90%都是XHR
- ③因为网址用的是ajax/axios, 所以他们的开发人员调用的是
$.get()方法, 但是底层都会到XMLHttpRequest.send()方法去真正发请求
- ! 注意: 浏览器自带的xhr拦截的是
XMLHttpRequest.send()方法,
2.2.2 XHR完整请求流程
- $ 语法: ①
xhr.open()这个方法初始化请求 ②xhr.send()真正的发送请求 ③xhr.onreadystatechange() 成功/失败的回调
ins1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Authorization', 'Bearer token123');
xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status >= 200 && xhr.status < 300) { console.log('成功响应:', xhr.responseText); } else { console.error('请求失败:', xhr.status); } } };
xhr.addEventListener('load', function() { console.log('请求完成'); }); xhr.addEventListener('error', function() { console.error('网络错误'); }); xhr.addEventListener('timeout', function() { console.error('请求超时'); });
xhr.timeout = 5000;
xhr.send();
|
2.2.3 axios拦截器
- & 说明:
- ①基于xhr的有
ajax/$, axios, Fetch API, umi-request这些都可以发请求, 底层用的都是XHR - ②并且每个都可以实现 “请求拦截“, 和 “响应拦截“
- ③而在 拦截方法 里面就有可能存在加密/解密, 由此就可以定位 (但需要注意不是所有的网址都用拦截器)
这里就展示axios的拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| axios = require('axios')
axios.interceptors.request.use(function (config) { console.log('请求拦截器 成功') config.headers['sign'] = 'lili' return config; }, function (error) { console.log('请求拦截器 失败') return Promise.reject(error); });
axios.interceptors.response.use(function (response) { console.log('响应拦截器 成功') console.log('调解密函数进行解密数据') return response.data; }, function (error) { console.log('响应拦截器 失败') return Promise.reject(error); });
axios.get('http://httpbin.org/get').then(res=>console.log(res))
|
2.2.4 实战XHR断点调试
需求: 找到 link 加密的位置
- 抓包, 找到需要卡的网址, 然后 “掐头去尾“ 复制 “路径“ 部分
1 2 3
| https://oauth.d.cn/auth/login?query_parameters...#section1 └──┬─┘ └───┬──────┘└───┬─────┘└─────┬─────┘ └───┬───┘ 协议 域名 路径 查询参数 片段(不发给服务器, 只供客户端用)
|
- 粘贴到如下位置, 并打钩, 然后所有的包含这个 “路径” 的请求都会断住
- ! 注意: 这个是断在
xhr.send()位置

2.2.5 XHR进阶技巧❗
- $ 技巧一: 对栈进行二分 去看
- & 举例: 例如栈为A, B, C, D(D为栈底); 如果A加密, 看D; 如果D没加密, 看B; 如果B没加密, C加密===>加密在C; 二分时间O(nlogn)
- $ 技巧二: 在异步的回调中加密
- & 说明:
n.then(成功的回调, 失败的回调)这里的n.then是在一个for循环里面的, 回调函数是放在一个 “长度为6的数组” 中的, 那其中0, 2, 4就是成功的回调(t.shift()方法是删除数组第0个元素, 并返回) 那加密就是在0, 2, 4里面去找

- $ 技巧三: 栈突然往前跳!
- & 说明: 还记得拦截器吗? 服务器返回数据 –> 拦截器 –> 成功的回调; 如果拦截器写在前面的栈里面, 当调用成功的回调时就会前跳

第三节 搜索关键字
搜索关键字的技巧比较多, 也是最快的, 常见的有以下几种
- 同一个”请求参数”中比较明显的参数进行搜索,
- 和启动器结合, 搜索关键中, 在启动器方法里面的
- 精确搜索, (比如要pass 排除password等), pass: pass=…

第三章 定位加密—Hook
link: https://www.cnblogs.com/xiaoweigege/p/14954648.html#%E8%BF%87debugger-%E9%98%BF%E5%B8%83%E7%89%9B%E9%80%BC
第一节 Hook JSON.parse
1
| 用户在页面触发事件 --> 构造请求对象 --> 请求拦截器 --> 发送请求 --> **返回数据** --> 响应拦截器 --> 请求成功的回调 --> 展示到开发者工具包中
|
| 请求头 | 请求参数 | Cookie | 响应数据 |
|---|
| JSON.parse | | √ | | √ |
3.1.1 适用场景
1 2
| js对象(字典/表单) ---JSON.stringify()--->JSON字符串 <--JSON.parse()--------
|
- @ 问题: 所以该Hook能用在哪里?
- & 说明: 有表单的位置, ①请求参数 ②响应数据; 只要这两个地方出现加密, 都可以考虑用这个Hook
1 2 3 4 5 6 7 8
| (function() { var _parse = JSON.parse; JSON.parse = function(ps) { console.log("Hook JSON.parse ——> ", ps); debugger; return _parse(ps); } })();
|
3.1.2 实战【响应数据加密】
需求: 找到 link 解密翻译结果的方法
- 确定需要 JSON.parse进行Hook
- 在调试前注入Hook代码
- ! 注意: Hook代码一定要在调试开始前执行



第二节 Hook Cookie
| 请求头 | 请求参数 | Cookie | 响应数据 |
|---|
| JSON.parse | | | √ | |
3.2.1 适用场景
Object.definProperty 是ES5支持的代理, ES6变为了Proxy 视频讲解Proxy
- $ 语法: 记得hook cookie前, 先清除下cookie [[1.JS基础与浏览器开发工具#7. 拦截对象的get和set方法]] link
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| (function () { cookieTemp = document.cookie; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.indexOf('需要捕获的Cookie名称') != -1) { debugger; } console.log('Hook捕获到cookie设置->', val); cookieTemp = val; }, get: function () { return cookieTemp; }, }); })();
|
3.2.2 实战 【Cookie加密】
需求: 还没找到网站….
3.2.3 注意事项
cookie一般都需要刷新网页, 而刷新网页会导致hook的代码失效, 所以提供一下两种方法去解决
- 脚本断点: 等到第一个网页的js文件加载的时候 注入hook代码
- 油猴: 教程 油猴可以实现, 在网页加载(前/body加前等)时机进行hook; 如下给一个油猴hook的模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
(function () { var cookieTemp = document.cookie; Object.defineProperty(document, 'cookie', { set: function (val) { if (val.indexOf('__jsl_clearance_s') != -1) { debugger; } console.log('Hook捕获到cookie设置->', val); cookieTemp = val; }, get: function () { return cookieTemp; }, }); })();
|
第三节 Hook XHR请求
1
| 用户在页面触发事件 --> 构造请求对象 --> 请求拦截器 --> **发送请求** --> 返回数据 --> 响应拦截器 --> 请求成功的回调 --> 展示到开发者工具包中
|
3.3.1 适用场景
- & 什么讲的原生XHR中, 有
XMLHttpRequest.open()(初始化请求) ; 和XMLHttpRequest.send()(真正发请求), 浏览器的xhr断点是断send, 我们可以手段断open(), 因为open()离发送请求的地方较近
1 2 3 4 5 6 7 8 9 10 11 12
|
(function () { var open = window.XMLHttpRequest.prototype.open; window.XMLHttpRequest.prototype.open = function (method, url, async) { if (url.indexOf("analysis") != -1) { debugger; } return open.apply(this, arguments); }; })();
|
3.3.2 实战【请求参数加密】
需求: 找到 七麦数据 请求参数加密位置


进过一番查找, 因为这里是异步的, 很难找; 但总的思路还是在的, 因为XHR open 就准备发送了, 加密一定往前找, 顶多就是多看几个栈


这里和原生的 浏览器 xhr断点 做一个对比: 有总比没有好

第四章 B/S的通讯
❗为什么要单独整理这个??? 因为只有知道别人请求是怎么发送的, 你XHR断点才有目标, 否则就算找到也是碰运气!
第一节 名词辨析
- & 问题: XHR, Fetch, ajax, Jquery , Axios, Promise, async 这些都是啥😰 看完下面这4点, 就能明白, 耐心点!
- 概念: Ajax(Asynchronous JavaScript + XMLHttpRequest)①是一个概念, 支持异步发送请求, ②最原来浏览器渲染是不支持异步的, 只能通过 form/链接 给后端发送请求, 而如果想更新页面数据, 就必须刷新页面, ③直到有了Ajax这个技术, 才支持异步模式(不刷新网页, 也能更新数据)
- 浏览器API层: ①XHR(2005)和Fetch(2015)都是浏览器原生的API, ②都是基于Ajax这个概念, 支持和后端进行异步通讯
- 封装层: ①因为XHR和Fetch写起来很麻烦(原生的都很麻烦) ②才有了诸如, jquery, axios等工具库 ③✔️, 他们是js的工具库 ④其中jquery ajax和ajax是不同的概念, 前者是工具库,jquery中除了有ajax来发送请求, 还有可以获取页面元素的”语法糖”, 是一个js工具库; 后者单独的ajax是一个模式/技术, 是异步技术;
- JS语法: ①Promise和async可以理解为JS的语法 ②配合XHR/Fetch才能实现请求方发送
1 2 3 4
| 经历的阶段: 3. 传统模式:通过浏览器默认导航行为(表单form/链接href)的**同步页面跳转** [必须刷新才能更新数据] 4. AJAX模式:使用**XHR/Fetch**等技术实现的**异步HTTP请求** [不刷新也能更新数据] 5. WebSocket:独立实现的**双向实时通信**方案(不属于AJAX体系)[不关注]
|
1 2 3 4 5 6 7 8 9
| 浏览器可以给后端发请求的相关概念 ├─ 浏览器API层 │ ├─ XHR (XMLHttpRequest) 【原生Ajax; Ajax模式(异步模式)】 │ ├─ Fetch API 【Ajax模式(异步模式)】 │ └─ WebSocket API 【不关注】 │ └─ 封装层 ├─ jQuery AJAX(XHR封装) └─ axios(基于XHR的Promise封装)
|
第二节 XHR发送请求
- $ 语法: 原生的XHR没有 “请求拦截器” or “响应拦截器”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XHR GET实现</title> </head> <body>
<button id="sendBtn">发送XHR GET请求</button>
<script> function sendRequest() { const xhr = new XMLHttpRequest();
const params = new URLSearchParams({ ts: '2132312', sign: '321321' });
xhr.open('GET', `http://localhost:8080/get?${params}`);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Authorization', 'Bearer token123');
xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log('响应数据:', JSON.parse(xhr.responseText)); } else { console.error('请求失败:', xhr.status); } } }
xhr.send(); }
document.getElementById('sendBtn').addEventListener('click', function(e) { e.preventDefault(); console.log('开始发送请求...'); sendRequest(); }); </script> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>XHR POST实现</title> </head> <body> <button id="sendBtn">发送XHR POST请求</button>
<script> function sendRequest() { const xhr = new XMLHttpRequest(); xhr.open('POST', 'http://localhost:8080/post'); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Authorization', 'Bearer token123');
const urlParams = new URLSearchParams({ ts: '2132312', sign: '321321' }); const postData = { page: '1', content: 'test' };
xhr.onreadystatechange = function() { if (xhr.readyState === 4) { console.log(`状态码: ${xhr.status}`, xhr.responseText); } }
xhr.send(JSON.stringify(postData)); }
document.getElementById('sendBtn').addEventListener('click', function(e) { e.preventDefault(); console.log('开始POST请求...'); sendRequest(); }); </script> </body> </html>
|
第三节 Fetch发送请求
视频: https://www.bilibili.com/video/BV1sT41197bV/?spm_id_from=333.337.search-card.all.click&vd_source=a2208fcbd48ebf44fafb42e682e09f82
第四节 JQuery发送请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery GET实现</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <button id="sendBtn">发送jQuery GET请求</button>
<script> $.ajaxSetup({ beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Bearer token123'); this.url += `?ts=${Date.now()}&sign=${btoa('secret')}`; } });
function sendGet() { $.ajax({ url: 'http://localhost:8080/get', method: 'GET', headers: { 'X-Custom-Header': 'jquery-demo' }, data: { page: 1, size: 20 }, success: function(response) { console.log('解密后的响应:', decrypt(response)); }, error: function(xhr) { console.error('请求失败:', xhr.status); } }); }
$('#sendBtn').click(function(e) { e.preventDefault(); console.log('jQuery GET请求启动'); sendGet(); }); </script> </body> </html>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery POST实现</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <button id="sendBtn">发送jQuery POST请求</button>
<script> $.ajaxPrefilter(function(options) { if (options.method === 'POST') { options.data = JSON.stringify(encryptData(options.data)); options.contentType = 'application/json'; } });
function sendPost() { $.ajax({ url: 'http://localhost:8080/post', method: 'POST', headers: { 'X-Request-Source': 'jquery' }, data: { username: 'admin', password: 'encrypted123' }, success: function(response) { console.log('响应处理:', response); } }); }
$('#sendBtn').click(function(e) { e.preventDefault(); console.log('jQuery POST请求启动'); sendPost(); }); </script> </body> </html>
|
第五节 axios发送请求
第五章 不同位置的加密怎么找
这里都是针对 XHR 的!!!
第一节 请求头加密
你XHR都是定位到send的位置, 设置请求头, 在发送请求前的任何位置都有可能
第二节 请求参数加密
第三节 载荷加密
第四节 响应体加密
第五节 Cookie加密