论坛首页 安全编程开发区 阅读主题

[原创]JSVMP 类型 a_bogus 本地化复现实践:基于 Claude Code 与 DeepSeek V4 的提效方法

285 浏览 17 回复
#1 楼主 2026-06-01 21:09:15
爬虫逆向工程中,最棘手的场景莫过于JSVMP(JavaScript Virtual Machine Protection)保护的签名字段。这类保护方案将核心签名逻辑编译为自定义字节码,由内嵌在 JS 文件中的栈式虚拟机解释执行。传统的"读混淆 JS → 翻译为 Python"路线在此基本失效——面对 100+ KB 的 opcode 表和上千条虚拟机指令,算法提取的时间成本以周甚至月计。**补环境(Environment Patching)**提供了一条替代路径:不提取算法,而是在 Node.js 的 vm 沙箱中搭建一个足以"欺骗"签名 SDK 的假浏览器环境,将目标站点原样的 JS 文件丢进去直接执行,从而离线产出签名。本文以某音系签名字段 a_bogus 为案例,记录从零到完整出参的 7 阶段工作流,重点讨论如何借助 Claude Code 与 DeepSeek V4 的能力组合将传统需要 3-5 天的手动迭代压缩到数小时内完成。a_bogus 的签名由 3 个 JS 文件协作完成,加载顺序严格不可变:SDK 通过 window.bdms.init({ aid: 6383, paths: ['^/aweme/v1/web/'] }) 激活。paths 是一个正则前缀数组——只有匹配这些前缀的 API 请求才会被 XHR hook 拦截并自动附加 a_bogus 参数。这是整个流程中最高频的静默故障点:init() 不报错、签名长度正确、字母表正确,但服务端拒绝——只因 paths 未配置或配置错误。在实验过程中发现,同一个 SDK 在不同浏览器会话中会产出不同长度的 a_bogus:最常见的是 172 字符,但也会出现 192 字符。初步假设是 Math.random 种子或设备指纹(hardwareConcurrency、deviceMemory)影响,但对 200 个随机种子、多种硬件指纹组合的遍历测试结果显示——所有组合都输出 172 字符。这与 platform: 'Linux aarch64_64' 的特殊设置有关,但本质上 192 更可能是会话级随机波动,而非可稳定复现的分支。这个探索过程本身很有价值:它展示了补环境方法论在"不理解算法细节"的前提下,如何通过受控实验(遍历种子、替换环境变量、注入不同轨迹数据)来缩小问题空间。使用 Playwright 驱动的 capture_sdk.js 打开目标页面,自动按文件大小(>20KB 阈值)和 URL 正则(匹配 vmp|protect|crawler|risk|sec|sdk|runtime|bundler|glue|loader|sign|guard|shield)过滤 JS 响应,保存到 bundles/ 并生成含 SHA-256 校验的 manifest.json。关键原则:永远从目标站点线上实时抓取,不要从 GitHub 镜像或博客文章复制。SDK 版本以周为单位变化,过期文件会导致签名被服务端静默拒绝。这是整套工具链中最具创新性的环节。传统补环境流程中,这一步需要人工反复:加载 SDK → 观察报错 → 补齐缺失属性 → 重新加载 → 再观察报错,通常需要 3-5 轮手动迭代。trace_env.js 将此过程全自动化。核心机制:启发式猜值函数 guessDefault():内置约 80 个属性名模式匹配规则。例如以 hardwareConcurrency 结尾的属性返回 8,以 onLine 结尾返回 false,以 get/set/create 等动词开头的属性返回空函数,首字母大写的标识符返回空构造函数。递归自愈 Proxy makeHealer():对每个对象包裹深度为 2 的 Proxy,当 SDK 访问不存在的属性时自动调用 guessDefault() 创建桩值,并记录访问路径到 traceLog。自动重跑机制:如果 Proxy 的自动补全仍然导致 SDK 崩溃(例如函数返回值被链式访问了更深层属性),脚本解析错误信息中的属性路径、注入缺失桩、重新加载 SDK。默认最多 8 轮自愈。日志输出示例:最终输出的 fake_env.js 是一个可直接 require 的完整环境桩模块,覆盖了 SDK 实际触碰的所有属性。基于 Phase 2 的追踪输出或手动编写的 fake_env.js,构建完整的假浏览器表面。a_bogus_192/core/fake_env.js 覆盖了以下核心表面:navigator 层(~40 属性):UA、platform、hardwareConcurrency=8、deviceMemory=8、webdriver=false、connection(effectiveType/rtt/downlink)、userAgentData(brands 含 Chromium 146)、clipboard、permissions、plugins/mimeTypes。DOM 层:document.createElement 返回带完整方法集的伪元素,canvas 的 2D/webgl 上下文返回稳定的假渲染结果。addEventListener 必须捕获 handler 引用(存入 __eventHandlers 数组)——这是 Phase 5.5 轨迹注入的前提。存储层:localStorage/sessionStorage 通过 Storage 原型类实现,带正确的 Symbol.toStringTag 和原型链。这是 SDK 检测 instanceof Storage 的关键。平台类:Headers、Request、Response、FormData、Blob、AbortController、URL、URLSearchParams——全部 typeof === 'function'。Observer 系列(IntersectionObserver、MutationObserver、ResizeObserver)为合法的空类。反重放检测:process、Deno、require、global、module 全部显式设为 undefined,防止 SDK 检测到 Node 运行时环境。签名 SDK 不暴露 sign(url) 这样的公开 API——它透明地 hook 了 XMLHttpRequest.prototype.send,在请求发出前改写 URL。因此离线捕获签名的核心技巧是构建一个假的 XHR 对象:几个关键要点:触发签名只需一行:window.bdms.init() 的配置参数直接决定签名分支。错误的 aid(应用 ID)或 paths(路由匹配)会导致签名产出长度正确、字母表正确、Base64 解码干净的字符串——但服务端拒绝。paths 发现技巧:在浏览器 DevTools Sources 面板全文搜索 .init(,复制线上完整配置对象。paths 字段是正则前缀,不是字面量路径字符串。例如 ['^/aweme/v1/web/'] 匹配所有 /aweme/v1/web/* 路径,而字面量 '/aweme/v1/web/aweme/post/' 只匹配精确路径。签名 S

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-291194.htm
#2 2026-06-01 21:09:15
tql
#3 2026-06-01 21:09:15
认真学习
#4 2026-06-01 21:09:15
666学习一下
#5 2026-06-01 21:09:15
强!
#6 2026-06-01 21:09:15
学习下
#7 2026-06-01 21:09:15
tql
#8 2026-06-01 21:09:15
谢谢
#9 2026-06-01 21:09:15
tql
#10 2026-06-01 21:09:15
xuexixuexi
#11 2026-06-01 21:09:15
tql
#12 2026-06-01 21:09:15
tql
#13 2026-06-01 21:09:15
dddd
#14 2026-06-01 21:09:15
tql
#15 2026-06-01 21:09:15
牛皮学习下
#16 2026-06-01 21:09:15
大佬太强了
‹ 上一页 1 2 下一页 ›

请登录后参与讨论

立即登录 注册账号