JS 逆向入门:用 DevTools 追踪请求参数生成链,附可运行本地实验

做网页采集时,复制 Network 中的一条请求通常只能解释“这一次发出了什么”。要复现请求,还要知道参数来自输入框、页面状态、配置,还是运行时计算。JS 逆向分析可以先从这条数据链入手:原始输入 → 归一化 → 序列化 → 发起请求。

本文使用自建本地页面,不依赖真实站点。示例故意包含空格、加号和时间戳,用来观察字符串在哪一步变化。已通过本地浏览器验证正常请求与空输入分支;断点操作方法依据 Chrome 官方文档,未把它表述成第三方站点的测试结果。

一、准备两个文件

在同一个目录建立 index.html 和 result.json。index.html 内容如下:

<!doctype html>
<meta charset="utf-8">
<title>请求参数追踪实验</title>
<label>SKU <input id="sku" value=" A B+01 "></label>
<button id="query">查询商品</button>
<pre id="output">等待点击</pre>
<script>
const input = document.querySelector('#sku');
const output = document.querySelector('#output');
function normalize(raw) {
  return raw.trim();
}
function buildQuery(sku, now) {
  return new URLSearchParams({sku, region: 'CN', ts: String(now)}).toString();
}
async function requestProduct() {
  const raw = input.value;
  const sku = normalize(raw);
  if (!sku) { output.textContent = 'SKU 不能为空'; return; }
  const query = buildQuery(sku, Date.now());
  const url = '/result.json?' + query;
  try {
    const response = await fetch(url, {cache: 'no-store'});
    if (!response.ok) throw new Error('HTTP ' + response.status);
    const data = await response.json();
    output.textContent = JSON.stringify({raw, sku, url, data}, null, 2);
  } catch (error) {
    output.textContent = String(error);
  }
}
document.querySelector('#query').addEventListener('click', requestProduct);
</script>

result.json 内容:

{"ok": true, "note": "静态演示响应,不是商品接口"}

在该目录运行:

python3 -m http.server 8768 --bind 127.0.0.1

浏览器打开 http://127.0.0.1:8768/ ,点击“查询商品”。请通过这个本地 HTTP 地址访问;直接双击文件会改变请求上下文。静态服务器忽略查询参数来读取 result.json,因此响应只用于验证链路,没有实现商品查询,也没有验证时间戳。

二、先记录一次正常样本

默认输入在两端带空格,中间包含一个空格与一个字面加号。页面输出的 raw 保留原输入,sku 为 A B+01,URL 中对应片段为:

sku=A+B%2B01&region=CN&ts=运行时毫秒时间戳

这里发生了两件不同的事:trim 去除了首尾空白;URLSearchParams 把中间空格序列化为 +,把字面加号序列化为 %2B。它是 URL 查询参数编码,不是加密。MDN 的 URLSearchParams 文档说明了此类编码行为。

这也是为什么不应先把 SKU 手动编码,再交给 URLSearchParams:百分号可能被再次编码,最终发送值与业务值不同。先确定你比较的是原始字符串、解码后的参数,还是 URL 字节表示。

三、从请求出口向上追

打开 DevTools 的 Network,找到 result.json 请求,核对查询参数和响应。请求详情中的 Initiator 可以帮助定位发起代码。随后在 Sources 的 XHR/fetch Breakpoints 中添加 result.json,再次点击按钮。相关能力见 Chrome 的 断点文档

暂停后先看当前请求相关的 url、query、sku;在调用栈中切换到业务函数,分清哪层负责准备参数,哪层仅仅包装 fetch。异步边界可能使调用栈不完整,此时结合 Initiator 和明确的行断点定位。

有个很容易踩的坑:停在 fetch 时,normalize 和 buildQuery 已经执行完了。调试器不会因为你继续单步就回到过去。要看参数生成过程,应在 requestProduct 中读取输入前或 buildQuery 调用前设置行断点,然后重新触发一次操作。

第二轮依次观察 raw、sku、query、url,每经过一个函数就记录输入与返回值。这样得到的是可验证的数据流,而不是根据压缩函数名猜用途。

四、用少量对照输入确认推断

本地正常样本已经观察到 A B+01 被发送为 A+B%2B01;仅输入空格时页面显示“SKU 不能为空”,不进入 fetch 分支。读者还可以继续做三组对照:

  • 把字面加号换成空格,比较编码结果是否变化。
  • 连续点击两次,检查只有时间戳变化还是其他字段也变了。
  • 保持输入不变,修改本地 region 配置,确认字段究竟来自哪里。

这些对照建议不等于真实站点的业务规则。本例没有服务端签名校验、会话校验和缓存层,不能根据它推导生产请求只需要这三个参数。

如果值每次都变,也先不要断言“用了随机加密”。时间戳、计数器、随机数、会话状态和序列化顺序都可能导致变化。区分它们需要固定其他输入,并追到具体赋值点。

五、压缩代码与 source map 怎么处理

格式化可以改善缩进,但不会恢复原始变量名和业务含义。可用的 source map 能帮助把部署代码映射到源文件;是否存在、是否加载成功,要看实际页面。参考 Chrome 的 source map 文档

没有 source map 时,优先用已观察到的 URL 片段、参数键和调用位置收窄范围,再从运行时输入输出判断函数作用。不要一开始就试图读懂整个 bundle。

AI 可以帮助解释已经定位的小段代码,或列出待验证的变量来源假设。给它的材料最好包括函数片段、一次输入、一次输出和你已经确认的调用关系。它给出的“像哈希”“可能是签名”只能作为线索,需要回到运行时验证。

六、最终应留下什么记录

一份有用的逆向笔记应写明触发动作、输入上下文、参数来源、转换函数、请求出口,以及尚未确认的依赖。比如本例可归纳为:按钮事件读取输入,trim 得到 sku,固定 CN 和 Date.now() 组成对象,URLSearchParams 序列化后交给 fetch。

采集实现是否与页面一致,要逐字段对比同一业务输入下的结果。先把“这个参数怎样生成”讲清楚,再讨论换客户端、移植逻辑或增加 AI 自动化,排错会更有依据。

本文由 AI 辅助编写;本地功能示例已运行验证,生产站点的行为需另行核实。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值