doc(case): 沉淀抖音a_bogus实战踩坑经验——踩坑记录+6条(#7-#12),关键经验+1条(显式markNative清单),致命级差异+4项(JSON.stringify/parse/permissions/clipboard)

This commit is contained in:
白夜
2026-04-19 18:22:43 +08:00
parent a568c7de86
commit 4f84657647
2 changed files with 23 additions and 2 deletions
+3 -2
View File
@@ -45,8 +45,9 @@ AI 输出格式(必须以此结构复述并填空):
速查表(内嵌,免去路径问题):
tiktok.com / X-Bogus / X-Gnarly / webmssdk / cacheOpts
→ case: jsvmp-dual-sign-xhr-intercept-cacheOpts-jsdom-firefox.md | 方案: jsdom 环境伪装
douyin.com / a_bogus / _sdkGlueInit
→ case: jsvmp-xhr-interceptor-env-emulation.md | 方案: vm 沙箱 + 自定义字符表
├─ douyin.com / a_bogus / _sdkGlueInit / byted_acrawler
│ → case: jsvmp-xhr-interceptor-env-emulation.md | 方案: jsdom 环境伪装(喂入-截出)
│ ⚠️ 关键踩坑: ttwid 需从浏览器导出; JSON.stringify/parse 必须显式 markNative; resources:'usable' 是拦截器激活必要条件
nmpa.gov.cn / NfBCSins2OywS / 412 挑战 / sdenv
→ case: jsvmp-ruishu6-cookie-412-sdenv.md | 方案: sdenv 纯 Node.js
FSSBBIl1UgzbN7N / _RSG / 200KB 混淆 JS + 412
@@ -291,6 +291,12 @@ function generateABogus(fullUrl, cookieStr) {
| 4 | Object.prototype.toString 标签错误 | jsdom 的 document 原型链是 `Document` 而非 `HTMLDocument`,screen 是普通对象 | `Symbol.toStringTag` 修复 + 原型链 `constructor.name` 修改 |
| 5 | XHR Hook 顺序导致截获失败 | webmssdk 保存的是 Hook 之前的原始 `open`,我方 Hook 截获不到最终 URL | 必须在 webmssdk 加载前完成 XHR Hook 安装 |
| 6 | 服务端静默拒绝 | 环境不完整时返回 HTTP 200 + 空 body,不返回错误码 | 通过对比 Camoufox 真实环境逐项排查 58 项差异 |
| 7 | ttwid Cookie 无法通过协议获取 | `POST /ttwid/check/` 返回 `status_code: 1002, "check not pass"`;首页 `GET /` 的 `set-cookie` 只返回 `__ac_nonce`,不返回 ttwid | ttwid 是浏览器端 JS 生成后写入的,纯协议无法直接获取。**解决方案**:从 Camoufox 调试浏览器的 Cookie 中导出完整 cookie_str 硬编码到脚本中(含 ttwid、odin_tt 等 httpOnly Cookie)。ttwid 有效期约 1 年,不需要频繁更新 |
| 8 | `JSON.stringify` / `JSON.parse` 的 toString 格式暴露 Node.js 环境 | JSVMP 生成 192 字符 a_bogus 但服务端返回 HTTP 200 + 空 body(2 字节)。签名格式正确但环境指纹哈希不匹配 | V8 原生函数的 `toString()` 返回单行格式 `function stringify() { [native code] }`,而 Firefox 格式是多行 `function stringify() {\n [native code]\n}`。**必须显式 `markNative(JSON.stringify)` 和 `markNative(JSON.parse)`**,因为它们不在任何 DOM 原型链上,`scanPrototypeChain` 扫描不到 |
| 9 | `doc.createElement` 等 DOM 实例方法未被 markNative | 同上:签名格式正确但服务端静默拒绝 | jsdom 的部分 DOM 方法直接挂在实例上而非原型链上,`scanPrototypeChain(doc, 5)` 扫描不到。**必须在 env-patch 末尾显式标记**:`markNative(doc.createElement)`、`markNative(doc.getElementById)`、`markNative(doc.querySelector)`、`markNative(doc.querySelectorAll)` 等 |
| 10 | jsdomPatterns 正则不完整导致部分 jsdom 函数 toString 泄露 | 同上 | jsdom 内部函数有两个额外特征模式需要加入检测正则:`/tryImplForWrapper/` 和 `/ceReactionsPreSteps/`。漏掉这两个会导致部分 DOM 操作函数的 `toString()` 暴露完整 jsdom 源码 |
| 11 | `resources: 'usable'` 是 JSVMP 拦截器激活的必要条件 | 不加 `resources: 'usable'` 时 SDK 加载成功、`byted_acrawler.frontierSign()` 能返回 X-Bogus,但 JSVMP 的 XHR 拦截器不触发(a_bogus 不生成) | jsdom 的 `resources: 'usable'` 选项会启用完整的 XHR 实现(含网络层),JSVMP 拦截器依赖这个完整实现才能正确 Hook `XMLHttpRequest.prototype.open`。不加此选项时 XHR 是简化版,拦截器无法正常工作。副作用是会尝试加载外部脚本导致 `SyntaxError: Unexpected token '<'`,但不影响 SDK 功能 |
| 12 | `navigator.permissions` 和 `navigator.clipboard` 缺失 | 环境指纹哈希不匹配 | Firefox 浏览器有这两个 API,jsdom 没有。需要补充存根:`nav.permissions = { query: markNative(function query() { return Promise.resolve({ state: 'prompt' }); }) }` 和 `nav.clipboard = { readText/writeText 存根 }` |
---
@@ -459,6 +465,10 @@ JSON.stringify({
| `navigator.plugins.length` | `5` | `0` | 模拟 Chrome 5 个 PDF 插件(含完整 PluginArray/Plugin/MimeType 结构) |
| `document.hasFocus()` | `true` | `false` | 方法覆写,始终返回 `true` |
| DOM `offsetHeight/Width` | 非零值(如 100) | `0` | `HTMLElement.prototype` getter 覆写,解析 style 或返回默认值 |
| `JSON.stringify.toString()` | `function stringify() {\n [native code]\n}` | `function stringify() { [native code] }`(V8 单行格式) | 显式 `markNative(JSON.stringify)` 覆写 toString 为 Firefox 多行格式 |
| `JSON.parse.toString()` | `function parse() {\n [native code]\n}` | `function parse() { [native code] }`(V8 单行格式) | 显式 `markNative(JSON.parse)` |
| `navigator.permissions` | `Permissions` 对象 | `undefined` | 补充 `{ query: () => Promise.resolve({state:'prompt'}) }` 存根 |
| `navigator.clipboard` | `Clipboard` 对象 | `undefined` | 补充 `{ readText/writeText }` 存根 |
### 高危级(可能参与指纹哈希)
@@ -536,3 +546,13 @@ JSVMP 用自定义指令集执行,反编译成本极高且版本迭代频繁
### 4. 对比方法论
用 Camoufox 反检测浏览器(C++ 引擎级指纹伪装)作为「干净基准」,在目标页面上下文中执行环境采集代码,与 jsdom 输出逐项 diff。分批采集(navigator / screen+window / document+performance+toString / DOM+Canvas+WebGL+Audio)避免单次执行过长。
### 5. env-patch 的「显式 markNative 清单」是必须的最后一步
`scanPrototypeChain` 只能扫描 DOM 类原型链上的方法,以下三类函数必须在 env-patch 末尾**显式逐个 markNative**:
- **V8 全局原生函数**:`JSON.stringify`、`JSON.parse`、`parseInt`、`parseFloat`、`encodeURIComponent`、`decodeURIComponent`、`encodeURI`、`decodeURI`、`btoa`、`atob`
- **jsdom window 上的定时器**:`setTimeout`、`setInterval`、`clearTimeout`、`clearInterval`、`eval`
- **document 实例方法**:`createElement`、`createTextNode`、`createDocumentFragment`、`getElementById`、`getElementsByClassName`、`getElementsByTagName`、`querySelector`、`querySelectorAll`
遗漏任何一个都可能导致 JSVMP 检测到非原生环境,生成的 a_bogus 被服务端静默拒绝(HTTP 200 + 空 body)。