sanitize: replace all site/vendor names with abbreviations (RS/JY/TK)
This commit is contained in:
+5
-5
@@ -13,8 +13,8 @@
|
||||
| 案例文件 | 技术特征 | 难度 | 核心方案 | 反爬类型 |
|
||||
|---------|---------|------|---------|---------|
|
||||
| [jsvmp-xhr-interceptor-env-emulation.md](jsvmp-xhr-interceptor-env-emulation.md) | JSVMP 字节码虚拟机 + XHR 拦截器 + 多层 SDK 联动 + jsdom 全量环境伪装 | ★★★★★ | jsdom 沙箱 + 58 项环境补丁 + XHR Hook 截出 a_bogus | 行为型 |
|
||||
| [jsvmp-ruishu6-cookie-412-sdenv.md](jsvmp-ruishu6-cookie-412-sdenv.md) | JSVMP 瑞数6 + Cookie 生成 + 412 挑战 + sdenv 补环境 | ★★★★★ | sdenv(魔改 jsdom + C++ V8 Addon)让瑞数 JSVMP 真实执行生成 Cookie | 签名型 |
|
||||
| [universal-vmp-source-instrumentation.md](universal-vmp-source-instrumentation.md) | **[v2.5.0 新]** 通用 VMP(瑞数 5/6、Akamai sensor_data、webmssdk、obfuscator.io)+ 首屏挑战 + 混合 Cookie 模式 | ★★★★ | **源码级插桩** + hot_keys 指纹学习 + analyze_cookie_sources 归因(**骨架模板**,由使用者按实际站点填充) | 混合(以签名型为主) |
|
||||
| [jsvmp-ruishu6-cookie-412-sdenv.md](jsvmp-ruishu6-cookie-412-sdenv.md) | JSVMP RS6 + Cookie 生成 + 412 挑战 + sdenv 补环境 | ★★★★★ | sdenv(魔改 jsdom + C++ V8 Addon)让RS JSVMP 真实执行生成 Cookie | 签名型 |
|
||||
| [universal-vmp-source-instrumentation.md](universal-vmp-source-instrumentation.md) | **[v2.5.0 新]** 通用 VMP(RS 5/6、Akamai sensor_data、webmssdk、obfuscator.io)+ 首屏挑战 + 混合 Cookie 模式 | ★★★★ | **源码级插桩** + hot_keys 指纹学习 + analyze_cookie_sources 归因(**骨架模板**,由使用者按实际站点填充) | 混合(以签名型为主) |
|
||||
| [jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox.md](jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox.md) | **[v2.7.0 新]** JSVMP 双签名(X-Bogus + X-Gnarly)+ XHR/fetch 双通道拦截 + cacheOpts 初始化 + jsdom Firefox 环境伪装 | ★★★★★ | jsdom 喂入-截出策略 + Firefox 格式 native code 伪装 + cacheOpts 路径注册 + got-scraping TLS 指纹模拟 | 行为型 |
|
||||
|
||||
## 指纹匹配快速参考
|
||||
@@ -24,7 +24,7 @@
|
||||
| `webmssdk` / `byted_acrawler` / `_SdkGlueInit` | jsvmp-xhr-interceptor-env-emulation 或 jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox | 高(需进一步区分) |
|
||||
| `cacheOpts` + `X-Gnarly` | jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox | 高(国际版双签名变体) |
|
||||
| `a_bogus` + 192字符 + 无 `cacheOpts` | jsvmp-xhr-interceptor-env-emulation | 高(国内版单签名) |
|
||||
| `sdenv` / `FuckCookie` / 412 挑战 | jsvmp-ruishu6-cookie-412-sdenv | 高(瑞数) |
|
||||
| `sdenv` / `FuckCookie` / 412 挑战 | jsvmp-ruishu6-cookie-412-sdenv | 高(RS) |
|
||||
| `while-switch` 分发循环 + 200KB+ 文件 | universal-vmp-source-instrumentation | 中(通用骨架) |
|
||||
|
||||
## 变体关系图
|
||||
@@ -35,10 +35,10 @@ JSVMP 字节码虚拟机 + 多层 SDK 联动
|
||||
│ └── → jsvmp-xhr-interceptor-env-emulation.md
|
||||
├── 海外短视频平台(国际版)变体(双签名 X-Bogus + X-Gnarly + cacheOpts)
|
||||
│ └── → jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox.md
|
||||
└── 通用 VMP 骨架(瑞数/Akamai/webmssdk/obfuscator.io)
|
||||
└── 通用 VMP 骨架(RS/Akamai/webmssdk/obfuscator.io)
|
||||
└── → universal-vmp-source-instrumentation.md
|
||||
|
||||
瑞数 JSVMP + Cookie 签名
|
||||
RS JSVMP + Cookie 签名
|
||||
└── → jsvmp-ruishu6-cookie-412-sdenv.md
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# JSVMP-瑞数6+Cookie生成+412挑战+sdenv补环境
|
||||
# JSVMP-RS6+Cookie生成+412挑战+sdenv补环境
|
||||
|
||||
> 难度:★★★★★
|
||||
> 还原方案:E: jsdom 环境模拟(sdenv 魔改 jsdom + C++ V8 Addon)
|
||||
@@ -22,7 +22,7 @@
|
||||
- [x] 首次请求返回 HTTP 412(非标准状态码),响应体为精简 HTML(含 meta + 内联 $_ts 配置 + 外部 JS 引用 + 入口函数调用)
|
||||
- [x] 412 响应的 Set-Cookie 头同时下发 `acw_tc`(会话标识)和 `XxxS`(服务端标识,HttpOnly,过期时间 10 年)
|
||||
- [x] 成功请求需携带 3 个 Cookie:`acw_tc` + `XxxS`(服务端下发)+ `XxxT`(客户端生成)
|
||||
- [x] 瑞数 JS 文件 URL 路径包含随机目录名和文件名,但版本号后缀(如 `.e17ed02.js`)在一段时间内固定
|
||||
- [x] RS JS 文件 URL 路径包含随机目录名和文件名,但版本号后缀(如 `.e17ed02.js`)在一段时间内固定
|
||||
|
||||
### 反调试特征
|
||||
- [x] JSVMP 内部使用函数表+直接调用(非 `Function.prototype.apply/call`),导致标准 JSVMP Hook 工具无法拦截
|
||||
@@ -41,7 +41,7 @@
|
||||
- list_scripts → 存在 230KB+ 文件 → 辅助确认
|
||||
- list_network_requests → 首次请求返回 412 → 高置信度
|
||||
- 检查 Set-Cookie 头 → 存在 acw_tc + XxxS 格式 Cookie → 直接定位本案例
|
||||
匹配判定:412 响应 + $_ts 配置 + _$ 前缀变量 → 瑞数6 高置信度匹配
|
||||
匹配判定:412 响应 + $_ts 配置 + _$ 前缀变量 → RS6 高置信度匹配
|
||||
```
|
||||
|
||||
---
|
||||
@@ -83,44 +83,44 @@
|
||||
|
||||
步骤 4: Cookie setter Hook 验证
|
||||
注入 document.cookie setter Hook,发现 Cookie 日志为空 —
|
||||
说明瑞数 JS 不是通过标准 document.cookie setter 写入的
|
||||
说明RS JS 不是通过标准 document.cookie setter 写入的
|
||||
(可能是通过 jsdom 内部机制或 location.replace 触发的重新请求时由浏览器自动携带)。
|
||||
|
||||
步骤 5: JSVMP 插桩尝试
|
||||
使用 hook_jsvmp_interpreter 对瑞数 JS 进行插桩,发现日志为空 —
|
||||
确认瑞数6使用内部函数表+直接调用,不经过 Function.prototype.apply/call,
|
||||
使用 hook_jsvmp_interpreter 对RS JS 进行插桩,发现日志为空 —
|
||||
确认RS6使用内部函数表+直接调用,不经过 Function.prototype.apply/call,
|
||||
标准 JSVMP Hook 无效。
|
||||
|
||||
步骤 6: 环境对比
|
||||
使用 compare_env 采集 Camoufox 中的真实浏览器环境数据,
|
||||
发现 document.cookie 中包含 enable_XxxYyy=true 标记,
|
||||
说明瑞数 JS 执行成功后会设置一个启用标记。
|
||||
说明RS JS 执行成功后会设置一个启用标记。
|
||||
|
||||
步骤 7: 瑞数 JS 保存
|
||||
通过 save_script 将 230KB 的瑞数 JS 文件保存到本地,分析其结构:
|
||||
步骤 7: RS JS 保存
|
||||
通过 save_script 将 230KB 的RS JS 文件保存到本地,分析其结构:
|
||||
以 if($_ts.cd){ 开头,内部是 JSVMP 解释器,
|
||||
window 仅出现 3 次(环境访问全部通过 JSVMP 字节码间接进行)。
|
||||
|
||||
步骤 8: 补环境方案选型
|
||||
参考开源社区的手动补环境方案和 sdenv/纯算/JsRpc 多方案,
|
||||
确定 sdenv(魔改 jsdom + C++ V8 Addon)是瑞数6的最优纯 Node.js 方案。
|
||||
确定 sdenv(魔改 jsdom + C++ V8 Addon)是RS6的最优纯 Node.js 方案。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 还原代码模板
|
||||
|
||||
### 核心函数:瑞数6 Cookie 生成 — 基于 sdenv
|
||||
### 核心函数:RS6 Cookie 生成 — 基于 sdenv
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* 瑞数6 Cookie 生成 — 基于 sdenv (魔改 jsdom + C++ V8 Addon)
|
||||
* RS6 Cookie 生成 — 基于 sdenv (魔改 jsdom + C++ V8 Addon)
|
||||
* 依赖: npm install sdenv (需要 pnpm 安装 + node-gyp 编译原生模块)
|
||||
* 原理:
|
||||
* sdenv 的核心是 documentAll.node (51行C++),用 V8 的
|
||||
* ObjectTemplate::MarkAsUndetectable() 实现 document.all 的浏览器特有行为
|
||||
* (typeof === "undefined" 但可调用),加上完整的浏览器环境模拟,
|
||||
* 让瑞数 JSVMP 在 Node.js 中真实执行并生成有效 Cookie。
|
||||
* 让RS JSVMP 在 Node.js 中真实执行并生成有效 Cookie。
|
||||
*/
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
@@ -145,7 +145,7 @@ class RuishuClient {
|
||||
consoleConfig: { error: () => {} },
|
||||
});
|
||||
|
||||
// 等待瑞数 JS 执行完毕
|
||||
// 等待RS JS 执行完毕
|
||||
await new Promise(resolve => {
|
||||
this.dom.window.addEventListener('sdenv:exit', () => resolve());
|
||||
setTimeout(resolve, 8000);
|
||||
@@ -195,14 +195,14 @@ class RuishuClient {
|
||||
|
||||
| # | 坑 | 现象 | 解决方法 |
|
||||
|---|---|------|---------|
|
||||
| 1 | 误判反爬类型 | 通过 webFetch 直接请求返回 412,猜测是 JSL 加速乐(`__jsl_clearance_s` cookie),实际是瑞数信息(RS) | 不要猜,用 Camoufox 实际抓包看 412 响应体结构和 Set-Cookie 头 |
|
||||
| 2 | Cookie 有效但请求返回 400 | Camoufox 获取的 Cookie 有效(curl 测试返回 200),但 axios 请求返回 400 | 瑞数不仅检查 Cookie,还检查请求头指纹 — UA 必须与生成 Cookie 时一致,且必须包含 `Sec-Fetch-Site: same-origin` 等 Fetch Metadata 头 |
|
||||
| 3 | jsdom 执行瑞数 JS 卡死(超时) | 原生 jsdom + `runScripts: 'dangerously'` 加载 412 页面,瑞数 JS 执行后进入死循环 | `typeof document.all` 在 jsdom 中返回 `"object"` 而非 `"undefined"`,瑞数检测到非浏览器环境后故意卡死。必须用 sdenv(C++ 层面实现 `MarkAsUndetectable()`)或真实浏览器 |
|
||||
| 4 | V8 `%GetUndetectable()` 不够 | 用 V8 内部函数获取的 undetectable 对象通过了 `typeof === "undefined"` 检测,但它是空对象,没有 `HTMLAllCollection` 的方法 | 瑞数 JSVMP 后续尝试调用该对象的方法时报错 `_$xx[_$yy[49]] is not a function`。`document.all` 不仅要通过 typeof 检测,还要有完整的集合行为 |
|
||||
| 5 | 瑞数入口函数名每次不同 | 412 响应体底部的入口函数调用(如 `_$bV()`, `_$l1()`, `_$jH()`)每次请求都不同 | 正则匹配时需要用 `_\$[a-zA-Z0-9]+\(\)` 而非固定函数名 |
|
||||
| 1 | 误判反爬类型 | 通过 webFetch 直接请求返回 412,猜测是 JSL 加速乐(`__jsl_clearance_s` cookie),实际是RS信息(RS) | 不要猜,用 Camoufox 实际抓包看 412 响应体结构和 Set-Cookie 头 |
|
||||
| 2 | Cookie 有效但请求返回 400 | Camoufox 获取的 Cookie 有效(curl 测试返回 200),但 axios 请求返回 400 | RS不仅检查 Cookie,还检查请求头指纹 — UA 必须与生成 Cookie 时一致,且必须包含 `Sec-Fetch-Site: same-origin` 等 Fetch Metadata 头 |
|
||||
| 3 | jsdom 执行RS JS 卡死(超时) | 原生 jsdom + `runScripts: 'dangerously'` 加载 412 页面,RS JS 执行后进入死循环 | `typeof document.all` 在 jsdom 中返回 `"object"` 而非 `"undefined"`,RS检测到非浏览器环境后故意卡死。必须用 sdenv(C++ 层面实现 `MarkAsUndetectable()`)或真实浏览器 |
|
||||
| 4 | V8 `%GetUndetectable()` 不够 | 用 V8 内部函数获取的 undetectable 对象通过了 `typeof === "undefined"` 检测,但它是空对象,没有 `HTMLAllCollection` 的方法 | RS JSVMP 后续尝试调用该对象的方法时报错 `_$xx[_$yy[49]] is not a function`。`document.all` 不仅要通过 typeof 检测,还要有完整的集合行为 |
|
||||
| 5 | RS入口函数名每次不同 | 412 响应体底部的入口函数调用(如 `_$bV()`, `_$l1()`, `_$jH()`)每次请求都不同 | 正则匹配时需要用 `_\$[a-zA-Z0-9]+\(\)` 而非固定函数名 |
|
||||
| 6 | Node.js v24 的 Navigator 原型有只读属性 | `Navigator.prototype` 上的 `language` 等属性已经有了 getter,用 `Object.assign` 赋值报错 | 必须用 `Object.defineProperty` 逐个覆盖 |
|
||||
| 7 | sdenv 原生模块编译 | sdenv 的核心 `documentAll.node` 是 C++ 原生模块,需要 node-gyp 编译 | pnpm 安装时默认不执行 build scripts,需要手动 `pnpm approve-builds` 或 `npx node-gyp rebuild` |
|
||||
| 8 | JSVMP Hook 工具对瑞数6无效 | Camoufox 的 `hook_jsvmp_interpreter` 通过 Hook `Function.prototype.apply/call` 追踪,但瑞数6使用内部函数表+直接调用,日志为空 | 不是所有 JSVMP 都能用标准 Hook 工具分析,需要根据具体实现选择分析方法 |
|
||||
| 8 | JSVMP Hook 工具对RS6无效 | Camoufox 的 `hook_jsvmp_interpreter` 通过 Hook `Function.prototype.apply/call` 追踪,但RS6使用内部函数表+直接调用,日志为空 | 不是所有 JSVMP 都能用标准 Hook 工具分析,需要根据具体实现选择分析方法 |
|
||||
|
||||
---
|
||||
|
||||
@@ -210,9 +210,9 @@ class RuishuClient {
|
||||
|
||||
| 变体 | 差异点 | 影响 |
|
||||
|------|--------|------|
|
||||
| 瑞数 4/5 代 vs 6 代 | 瑞数6 JS 文件约 230KB,比 4/5 代(约 200KB)更大,环境检测项更多 | 4/5 代可通过手动补环境成功,6 代手动补环境极其困难,建议直接用 sdenv |
|
||||
| Cookie-only vs Cookie+URL后缀 | 大部分瑞数站点只需要 Cookie 即可访问。少数站点还需要 URL 后缀签名 | sdenv 只能生成 Cookie,不能生成 URL 后缀。需要后缀的站点建议用 JsRpc 方案 |
|
||||
| HTTP vs HTTPS | 部分瑞数站点使用 HTTP,其他使用 HTTPS | sdenv 的 `jsdomFromUrl` 两种协议都支持,但 HTTPS 站点需要设置 `NODE_TLS_REJECT_UNAUTHORIZED=0`(某些政府站点的证书链不完整) |
|
||||
| RS 4/5 代 vs 6 代 | RS6 JS 文件约 230KB,比 4/5 代(约 200KB)更大,环境检测项更多 | 4/5 代可通过手动补环境成功,6 代手动补环境极其困难,建议直接用 sdenv |
|
||||
| Cookie-only vs Cookie+URL后缀 | 大部分RS站点只需要 Cookie 即可访问。少数站点还需要 URL 后缀签名 | sdenv 只能生成 Cookie,不能生成 URL 后缀。需要后缀的站点建议用 JsRpc 方案 |
|
||||
| HTTP vs HTTPS | 部分RS站点使用 HTTP,其他使用 HTTPS | sdenv 的 `jsdomFromUrl` 两种协议都支持,但 HTTPS 站点需要设置 `NODE_TLS_REJECT_UNAUTHORIZED=0`(某些政府站点的证书链不完整) |
|
||||
| 静态页面 vs API 接口 | 列表页和详情页可能是纯静态 HTML(服务端渲染),用 cheerio 解析即可。其他站点可能有 JSON API 接口 | API 请求可能需要额外的签名参数(如 `sign = MD5(itemId + searchValue + timestamp)`) |
|
||||
| `$_ts` 配置差异 | 不同站点的 `$_ts` 配置结构可能不同,有的只有 `nsd` 和 `cd`,其他可能还有 `cp`、`aebi` 等字段 | sdenv 方案不需要关心这些差异,因为它让瑞数 JS 自己解析配置 |
|
||||
| 服务端动态更新 | 瑞数服务端可以随时更新 `basearr` 中的环境检测值 | 纯算方案会因此失效,sdenv 方案通常不受影响(除非瑞数新增了 sdenv 未模拟的检测点) |
|
||||
| `$_ts` 配置差异 | 不同站点的 `$_ts` 配置结构可能不同,有的只有 `nsd` 和 `cd`,其他可能还有 `cp`、`aebi` 等字段 | sdenv 方案不需要关心这些差异,因为它让RS JS 自己解析配置 |
|
||||
| 服务端动态更新 | RS服务端可以随时更新 `basearr` 中的环境检测值 | 纯算方案会因此失效,sdenv 方案通常不受影响(除非RS新增了 sdenv 未模拟的检测点) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 案例:通用 VMP 源码级插桩(骨架模板)
|
||||
|
||||
> **v2.5.0 骨架案例**。本文是一个**方法论模板**,不是某个具体站点的案例。适用于:瑞数 5/6、Akamai sensor_data v2/v3、webmssdk、obfuscator.io 等所有"算法封装在字节码 dispatch 循环里"的 VMP。
|
||||
> **v2.5.0 骨架案例**。本文是一个**方法论模板**,不是某个具体站点的案例。适用于:RS 5/6、Akamai sensor_data v2/v3、webmssdk、obfuscator.io 等所有"算法封装在字节码 dispatch 循环里"的 VMP。
|
||||
>
|
||||
> 使用方式:
|
||||
>
|
||||
@@ -20,7 +20,7 @@
|
||||
- 固定长度签名参数(128/192/256 字符) + Base64 变体(有 `-_` 替换 `+/`) 或 hex
|
||||
- 典型参数名:`a_bogus` / `_signature` / `X-Bogus` / `msToken` / `_m_h5_tk` / `acw_tc` / `FSSBBIl1UgzbN7N`
|
||||
- **请求特征**:
|
||||
- 首包返回 412(瑞数)或 403(Akamai)+ 动态 cookie 写入后通过
|
||||
- 首包返回 412(RS)或 403(Akamai)+ 动态 cookie 写入后通过
|
||||
- 存在预热接口(如 `/challenge` / `/akam/11/*` / `/v1/gen_token`)
|
||||
- Cookie 中有多个动态字段(JS 写的 + HTTP Set-Cookie 发的)
|
||||
- **反调试特征**:
|
||||
@@ -83,7 +83,7 @@ Step 5 — 装兜底 hook(若 Step 2 没走 pre_inject_hooks)
|
||||
inject_hook_preset("crypto", persistent=True)
|
||||
hook_jsvmp_interpreter(script_url="[VMP basename]")
|
||||
# 注意:本案例针对的是"通用"场景(含签名型和行为型)。如果目标是签名型反爬
|
||||
# (瑞数/Akamai),不要这样用 hook_jsvmp_interpreter,改为:
|
||||
# (RS/Akamai),不要这样用 hook_jsvmp_interpreter,改为:
|
||||
# - instrument_jsvmp_source(mode="ast") (首选)
|
||||
# - hook_jsvmp_interpreter(mode="transparent") (备选)
|
||||
# 参考 SKILL.md "反爬类型识别与工具选择" 章节
|
||||
@@ -196,8 +196,8 @@ function genSign(input) {
|
||||
|
||||
| 变体 | 差异点 | 调整策略 |
|
||||
|------|-------|---------|
|
||||
| 瑞数 5 | 固定 `sdenv-*.js` 命名 | url_pattern 用 `**/sdenv-*.js` |
|
||||
| 瑞数 6 | 脚本名每次不同 + 多层 VMP | 先 `list_network_requests` 找所有 100KB+ JS,逐个 instrument(tag 区分) |
|
||||
| RS 5 | 固定 `sdenv-*.js` 命名 | url_pattern 用 `**/sdenv-*.js` |
|
||||
| RS 6 | 脚本名每次不同 + 多层 VMP | 先 `list_network_requests` 找所有 100KB+ JS,逐个 instrument(tag 区分) |
|
||||
| Akamai sensor_data | `_abck` / `bm_sz` cookie | 重点看 `hot_keys` 中 touch/mouse 事件相关属性 |
|
||||
| webmssdk(短视频平台) | 配合 msToken 预热 | 需先让 `/v1/generate_token` 预热接口完成再触发业务 |
|
||||
| obfuscator.io(开源) | case 数可能只有 20-30 | `find_dispatch_loops(min_case_count=15)` |
|
||||
|
||||
Reference in New Issue
Block a user