Files
hello_js_reverse_skill/references/protocol-analysis.md
T
白夜 fb14cf7916 refactor: 将文件从子目录提升到仓库根目录
所有文件从 hello_js_reverse_skill/ 子目录移至仓库根目录,
使项目结构更清晰,便于作为 Skill 直接引用。

Made-with: Cursor
2026-03-18 22:55:27 +08:00

270 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 协议层分析与对抗
## 概述
有些网站不仅在 JS 层面做加密保护,还在协议层面进行检测(TLS 指纹、HTTP/2 强制等)。
当算法全部还原正确但请求仍然失败时,需要考虑协议层问题。
## 1. TLS 指纹检测
### 原理
服务器通过分析 TLS Client Hello 报文中的特征来识别客户端类型:
- Cipher Suites 列表和顺序
- TLS 扩展列表和顺序
- 支持的曲线(Elliptic Curves)
- 签名算法
- ALPN 协议列表
不同客户端(Chrome、Firefox、curl、Node.js)的 TLS 指纹不同。
### 检测表现
- 请求返回 `403 Forbidden`
- 返回空响应或错误页面
- 返回 `token failed`、`access denied` 等自定义错误
- 同样的参数在浏览器中正常,在 Node.js 中失败
### 解决方案
#### 方案 A:使用浏览器自动化(最可靠)
```javascript
// 使用 chrome-devtools MCP 在真实浏览器中执行请求
// [chrome-devtools] evaluate_script
const response = await fetch('https://target.com/api/data', {
method: 'GET',
headers: { 'Cookie': cookieValue },
credentials: 'include'
});
const data = await response.json();
```
```
MCP 操作:
[chrome-devtools] evaluate_script(function="fetch(url).then(r=>r.json()).then(d=>JSON.stringify(d))")
```
#### 方案 B:curl-impersonate
```bash
# 使用 curl-impersonate 模拟 Chrome 的 TLS 指纹
curl_chrome116 https://target.com/api/data \
-H "Cookie: session=xxx" \
-H "User-Agent: Mozilla/5.0 ..."
```
Node.js 中通过子进程调用:
```javascript
const { execSync } = require('child_process');
function fetchWithChromeTLS(url, headers = {}) {
const headerArgs = Object.entries(headers)
.map(([k, v]) => `-H "${k}: ${v}"`)
.join(' ');
const result = execSync(`curl_chrome116 "${url}" ${headerArgs} -s`, {
encoding: 'utf-8',
timeout: 30000,
});
return JSON.parse(result);
}
```
#### 方案 C:自定义 TLS(高级)
使用支持自定义 TLS 指纹的库(如 `tls-client` 的 Node.js 绑定)。
## 2. HTTP/2 协议
### 检测表现
- HTTP/1.1 请求返回错误
- HTTP/2 请求正常返回数据
- 抓包可以看到服务器仅接受 h2 连接
### Node.js HTTP/2 请求
```javascript
const http2 = require('http2');
function http2Request(url, options = {}) {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const client = http2.connect(`${urlObj.protocol}//${urlObj.host}`);
client.on('error', reject);
const headers = {
':method': options.method || 'GET',
':path': urlObj.pathname + urlObj.search,
':scheme': urlObj.protocol.replace(':', ''),
':authority': urlObj.host,
'user-agent': options.userAgent || 'Mozilla/5.0 ...',
'accept': 'application/json',
...options.headers,
};
if (options.cookie) {
headers['cookie'] = options.cookie;
}
const req = client.request(headers);
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
client.close();
resolve(data);
});
req.on('error', reject);
if (options.body) {
req.write(options.body);
}
req.end();
});
}
// 使用示例
async function main() {
const data = await http2Request('https://target.com/api/data?page=1', {
headers: {
'cookie': 'm=xxx; sessionid=yyy',
'referer': 'https://target.com/',
}
});
console.log(JSON.parse(data));
}
```
## 3. User-Agent 校验
### 常见场景
- 某些页面要求特定 UA
- 不同 UA 返回不同内容
- 空 UA 或非浏览器 UA 被拒绝
### Node.js 设置
```javascript
const axios = require('axios');
const client = axios.create({
headers: {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
}
});
```
### MCP 模拟
```
[chrome-devtools] emulate(userAgent="自定义UA")
```
## 4. Referer 校验
### 常见场景
- API 接口检查 Referer 头
- 必须来自特定页面的请求
- 缺少 Referer 返回 403
### 解决方案
```javascript
headers: {
'Referer': 'https://target.com/page',
'Origin': 'https://target.com',
}
```
## 5. CORS 与跨域
### 预检请求 (Preflight)
```javascript
// 某些请求会先发 OPTIONS 预检
// 确保模拟请求时也处理 CORS 相关头
headers: {
'Origin': 'https://target.com',
'Access-Control-Request-Method': 'POST',
'Access-Control-Request-Headers': 'content-type',
}
```
## 6. 请求频率限制
### 常见策略
- IP 维度限流
- Cookie/Session 维度限流
- 请求间隔检测
- 滑动窗口计数
### 对抗方式
```javascript
// 请求间隔控制
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function fetchWithRateLimit(urls, delayMs = 1000) {
const results = [];
for (const url of urls) {
const data = await fetch(url);
results.push(data);
await sleep(delayMs + Math.random() * 500); // 随机化延迟
}
return results;
}
```
## 7. IP 封禁对抗
### 代理轮换
```javascript
const HttpsProxyAgent = require('https-proxy-agent');
const proxies = [
'http://proxy1:port',
'http://proxy2:port',
];
function getRandomProxy() {
return proxies[Math.floor(Math.random() * proxies.length)];
}
async function fetchWithProxy(url, options = {}) {
const agent = new HttpsProxyAgent(getRandomProxy());
return fetch(url, { ...options, agent });
}
```
## 诊断流程
```
请求失败?
│
├─ 检查 HTTP 状态码
│ ├─ 403 → TLS 指纹 / Referer / UA / IP 封禁
│ ├─ 412 → 前置条件失败(Cookie / Token 过期)
│ ├─ 429 → 频率限制
│ └─ 200 但数据异常 → 参数错误 / 加密不对
│
├─ 同样的请求在浏览器中成功?
│ ├─ 是 → TLS 指纹问题,切换到浏览器方案
│ └─ 否 → 参数/Cookie 确实有问题,重新分析
│
├─ curl 命令测试
│ ├─ curl 成功 → Node.js 请求头/参数构造问题
│ └─ curl 也失败 → TLS / 协议 / IP 问题
│
└─ 使用 HTTP/2?
├─ HTTP/2 成功 → 切换到 http2 模块
└─ HTTP/2 也失败 → TLS 指纹问题
```