Files

12 KiB
Raw Permalink Blame History

证据收集规则

本文说明 JS 逆向自动化中的证据收集方法:网络捕获、软 Hook、源码定位和运行时 Hook 跟踪,并规定证据强度、记录方式与风险控制。


1. 网络捕获

适用场景

  • 确认目标参数位于 URL、请求头、Cookie、JSON 请求体还是表单请求体。
  • 页面动作触发多个相似请求,需要缩小到真正参与签名或加密的请求。
  • 用户提供了 Optional Fetch Example,需要映射到真实浏览器请求。

证据优先级

  1. 真实页面动作触发的网络记录。
  2. 请求详情:方法、URL、请求头、请求体和响应。
  3. 页面内观察代码输出:调用栈、序列化对象和函数参数。
  4. 控制台日志与页面状态快照。

推荐流程

  1. 打开目标页面并完成必要交互,确认请求确实发出。
  2. 使用 chrome-devtools-mcp 的网络请求列表筛选候选请求。
  3. 读取每个候选请求的详情,确认目标参数位置和请求体结构。
  4. 请求较多时,按以下条件缩小范围:
    • 是否出现目标参数。
    • 请求触发时间是否接近用户动作。
    • 响应状态是否符合业务预期。
    • 请求体是否同时包含未加密明文和加密结果等相邻字段。
  5. 锁定请求后立即记录:
    • 请求 URL。
    • HTTP 方法。
    • Content-Type。
    • 参数位置。
    • 关键请求头。
    • 触发动作。
  6. 请求详情不足以定位源码时,再增加最小观察代码或最小 Hook。

成功标准

  • 至少一个请求被明确标记为目标请求。
  • 每个待分析参数的位置已知。
  • 已记录足以支持后续源码定位的请求上下文。

失败信号

  • 只能看到静态资源请求,看不到业务 API。
  • 目标动作触发大量相似请求,无法区分签名参与者。
  • 能看到请求,但无法确认参数经过哪一层序列化后出现。

处理策略

  • 多请求竞争时只改变一个输入值,观察哪个请求字段同步变化。
  • 序列化不透明时,对以下边界增加最小 Hook:
    • window.fetch
    • XMLHttpRequest.prototype.open
    • XMLHttpRequest.prototype.send
    • JSON.stringify
  • 页面会刷新时,通过 evaluate_script 注入;需要在刷新前生效时,使用 navigate_page(initScript=...) 预注入。
  • Hook 的目的只是记录“发送前最后一个可观察形态”,不能替代网络证据。

2. 软 Hook 规则

适用范围

  • 网络证据已锁定目标请求,但仍缺少入口函数证据。
  • 需要确认参数在某个函数调用前后是否发生变化。
  • 需要确认返回值、this 绑定、异步行为或全局依赖。

最小 Hook 原则

  • 先 Hook 通用边界,再 Hook 业务函数。
  • 先记录后改写,默认不替换原函数。
  • 每个 Hook 只增加一个观察点,避免多个 Hook 同时污染证据。
  • 页面已加载时,优先使用 evaluate_script 注入。
  • 页面将刷新或导航时,使用 navigate_page(initScript=...) 预注入。

推荐 Hook 顺序

  1. window.fetch
  2. XMLHttpRequest.prototype.open
  3. XMLHttpRequest.prototype.send
  4. JSON.stringify
  5. 已明确匹配的业务函数
  6. 必要时再补充 eval、Function 或 Promise 相关节点

每个 Hook 至少记录

  • Hook 点路径。
  • 匹配条件。
  • 输入参数摘要。
  • 返回值摘要。
  • 调用栈摘要。
  • 是否改变页面行为。
  • 注入方式:evaluate_script 或 navigate_page(initScript=...)。

入口确认规则

业务函数 Hook 至少要证明以下一项:

  • 明文输入与请求中的目标参数存在可验证映射。
  • 函数返回值直接进入请求体、请求头或 Cookie。

只看到密码库内部调用、没有业务调用者上下文,不能认定为最终入口。

依赖提取规则

明确记录:

  • this 绑定来源。
  • 依赖的全局对象或闭包对象。
  • 是否需要等待异步结果。
  • 页面是否必须先完成启动流程。

这些信息最终写入 artifacts/phase3_dependencies.json 和 analysis_result.json。

风险控制

  • 默认不要冻结大量原型。
  • 不要为了方便观察而全局替换所有密码学 API。
  • Hook 导致业务分支改变时,先回退到更小范围,再继续分析。

3. 源码定位

核心原则

  • 先从请求详情、页面内调用栈和序列化节点回溯,再进行关键词搜索。
  • 先找“参数最后一次发生变化的位置”,再找“最深层算法位置”。
  • 只接受有证据支持的源码定位结果,不接受纯文本匹配结论。

定位优先级

  1. 请求详情、页面内 Error().stack 或观察代码捕获的调用栈。
  2. 发送前对象构造或序列化位置。
  3. 与参数同名或相邻的源码片段。
  4. 密码库调用位置。

推荐流程

  1. 从目标请求开始,确认请求方法、请求体和相关脚本线索。
  2. 在请求构造处加入最小观察代码:
    • 观察 fetch / XHR send。
    • 观察 JSON.stringify。
    • 在页面内输出 Error().stack。
  3. 页面刷新或导航时:
    • 使用 navigate_page(initScript=...) 预注入。
    • 重新触发目标动作并记录序列化前对象。
  4. 标记参数的三个关键位置:
    • 明文入口。
    • 处理或加密位置。
    • 发送前最终写入位置。
  5. 只有其中至少两个位置被证据串联后,才能输出首选入口。

候选入口接受标准

  • 能解释参数从明文到密文的变化。
  • 能定位可调用函数或稳定解析器。
  • 能解释 this 绑定、参数签名和必要依赖。

常见误判

  • 只匹配到参数名,但变量只是中间副本。
  • 只匹配到 md5、aes 或 sha,但它并不服务于目标请求。
  • 只看到包装函数,没有继续确认真实执行点。

输出要求

  • source_hint 尽量落到具体 bundle 位置或对象路径。
  • evidence 至少包含以下一项:
    • 页面内调用栈。
    • Hook 捕获的输入参数和返回值。
    • 序列化前对象快照。
    • 参数值对照实验结果。

4. 运行时 Hook 跟踪

概述

运行时 Hook 使用预注入探针,在网络、密码学和序列化层捕获证据,是入口发现的重要证据源。

探针安装

生成:

python3 scripts/emit_runtime_hook_probe.py --output generated/runtime_hook_probe.js --params "target_param1,target_param2"

注入方式:

  • 页面已加载时使用 evaluate_script。
  • 页面会刷新或导航时使用 navigate_page(initScript=...)。

幂等性:

  • 探针安装前检查 window.JSRA_TRACE,重复注入应安全地跳过。

探针捕获内容

探针在以下边界安装软 Hook,并将证据写入 window.JSRA_TRACE。

4.1 网络请求(requests[])

  • Hook 点:window.fetch、XMLHttpRequest.prototype.open、XMLHttpRequest.prototype.send。
  • 每条记录包括:
    • type:fetch 或 xhr。
    • url:请求 URL。
    • method:HTTP 方法。
    • headers:摘要后的请求头。
    • bodySnippet:截断后的请求体。
    • timestamp:Date.now()。
    • stack:通过 Error().stack 捕获的调用栈,最多 10 帧。

4.2 密码学事件(crypto[])

  • Hook 点:crypto.subtle.digest、sign、encrypt、decrypt。
  • 每条记录包括:
    • type:digest、sign、encrypt 或 decrypt。
    • algorithm:算法名称。
    • inputLen:输入字节数。
    • outputLen:Promise 完成后的输出字节数。
    • outputHex:摘要结果前 32 个十六进制字符,仅用于 digest。
    • timestamp:Date.now()。
    • stack:调用栈,最多 10 帧。

4.3 序列化事件(serializers[])

  • Hook 点:FormData.append、FormData.set、URLSearchParams.append、URLSearchParams.set、URLSearchParams.toString、JSON.stringify。
  • 每条记录包括:
    • type:序列化方法名。
    • name:字段名。
    • valueSnippet 或 resultSnippet:截断后的值。
    • keys:JSON.stringify 对象的键,最多 20 个。
    • timestamp:Date.now()。
    • stack:JSON.stringify 和 URLSearchParams.toString 的调用栈。

4.4 选择性 JSON.stringify Hook

  • 只有待序列化对象包含目标参数或敏感字段时才记录。
  • 敏感字段包括 sign、token、enc、password、signature、hash、key、nonce、timestamp、ts、data、encrypt、decrypt。
  • 这样可以避免无关序列化调用造成日志洪泛。

读取证据

导出全部证据:

evaluate_script("window.__JSRA_TRACE__.dump()")

返回包含 requests、crypto、serializers、errors 的 JSON 字符串。

清空证据缓存:

evaluate_script("window.__JSRA_TRACE__.clear()")

使用证据发现入口

  1. 从 requests[].stack 中寻找加密或签名函数调用帧,这些帧指向业务调用者,而不仅是密码库。
  2. 与序列化事件交叉比对:如果 serializers[] 显示 JSON.stringify 或 URLSearchParams.toString 处理了关注字段,调用栈可以定位请求体组装位置。
  3. 将密码学算法和请求体对应:crypto[] 中的 digest 或 encrypt 事件,应与请求体中的密文长度和算法相匹配。
  4. 从调用栈提取最外层业务函数,排除 fetch、XMLHttpRequest 和密码库内部函数,作为候选入口。

运行时跟踪的证据标准

  • 高置信度:至少一条经过业务函数的 requests[] 调用栈,以及一条与目标参数相关的 crypto[] 或 serializers[] 事件。
  • 中置信度:至少一条 bodySnippet 中出现目标参数的 requests[] 记录,即使调用栈未明确指向业务函数。
  • 仅有密码库内部调用、没有业务调用者上下文,不能达到高置信度。

失败处理

现象 处理方式
探针注入失败(被反调试阻断) 记录反调试现象,参阅反调试规则,降级到静态分析
触发目标动作后没有 requests[] 确认动作确实执行,并检查探针是否注入到正确执行上下文
crypto[] 为空但请求体包含密文 可能使用非 Subtle 密码库,使用 CryptoJS/JSEncrypt Hook 规则
stack 全部匿名或已混淆 使用源码定位规则,将调用栈与 bundle 行号关联

风险提示

  • 探针不能替代网络证据,只能补充网络证据。
  • 探针使用软包装,不应替换原函数或破坏页面功能。
  • Hook 造成计时变化时,先记录未启用 Hook 的基线,再记录启用后的观察结果。
  • 探针不会自动跨页面导航持久化,页面刷新后需要通过 initScript 重新注入。

5. 跨场景证据标准

证据层级

  1. 网络证据:请求 URL、方法、请求体、请求头和响应,强度最高。
  2. 运行时 Hook 证据:调用栈、密码学事件和序列化事件,强度较高。
  3. 源码证据:bundle 位置、对象路径和关键词匹配,仅作为辅助。

置信度要求

置信度 最低证据
high 网络证据 + 运行时/调用栈/模块证据,至少两类
medium 单一证据源(网络或运行时)
low 仅有关键词搜索或源码模式

可计入证据的内容

  • 运行时探针产生的 requests[],且调用栈经过业务函数。
  • 能将算法和输出长度与实际密文对应的 crypto[] 事件。
  • 显示参数在请求发送前被序列化的 serializers[] 事件。
  • evaluate_script 捕获并指向业务调用者的 Error().stack。
  • 能将明文映射到密文的 Hook 输入/输出对。
  • 只改变一个输入值后,能证明哪个请求字段随之变化的实验结果。

不可单独计入证据的内容

  • 没有运行时关联的源码关键词匹配。
  • 没有业务调用者上下文的密码库函数匹配。
  • 存在于全局对象上、但目标动作期间从未被观察到调用的路径。
  • 没有调用栈、Hook 或网络确认的疑似入口。

记录要求

每条证据必须包含:

  • 来源类型:网络、Hook、调用栈、序列化、密码学或实验。
  • 时间戳或序列号。
  • 足以复现观察的上下文:URL、方法、Hook 点路径和调用栈。
  • 观察发生时是否启用了补丁或 Hook。