cat-catch 猫抓资源嗅探实战:改写请求头的 3 个场景与避坑指南
打开一个视频站,用猫抓 cat-catch 资源嗅探扩展列出页面资源,点下载,结果一堆 .ts 切片全回 403。原因不复杂:站点在校验 Referer,请求不是"从那个页面发出的"就拒发文件。缺的只是"请求头修改"这一步。
cat-catch(猫抓)解决的就是这类问题。它是个开源的浏览器资源嗅探扩展,把页面里的视频、音频、图片列成清单,同时借助 declarativeNetRequest API(声明式网络请求,Manifest V3 的能力)在请求发出去之前改写请求头:换成手机 UA、塞进会员 Authorization、补上防盗链 Referer,403 就常常变回正常的 200。
先跑起来:猫抓怎么安装,嗅探弹窗长什么样
扩展商店里就有官方版;想读代码的话,clone 源码后以开发者模式加载更方便:
git clone https://gitcode.com/GitHub_Trending/ca/cat-catch
- 打开浏览器扩展管理页,启用"开发者模式";
- 点"加载已解压的扩展程序",选中 cat-catch 目录;
- 打开任意带视频的页面,点右上角的猫抓图标。
弹窗里是"当前页面 / 其他页面"两个页签的资源列表:文件名、大小、MIME,每行挂着播放、复制、下载按钮。底部一排功能按钮里有个"模拟手机"——点一下,当前标签页就会以手机 UA 重新嗅探,按钮随即翻转为"关闭"状态。先不用懂原理,点两下就有感觉。
机制拆解:一个请求被改写请求头的完整时间线
📌 Manifest V3 里,老的 webRequest API 只能看不能改:能读到请求头,改不了。修改请求要换新通道——declarativeNetRequest。
用个类比:webRequest 像站在门口登记进出人员的保安;declarativeNetRequest 是提前放在分拣中心的"转寄单"。包裹(请求)出库前,分拣中心拿单据比对一下,命中就重新贴标签。扩展全程不拦截任何请求,只管维护这些单据。
一次改写的完整时间线(代码分别在 js/background.js 和 js/function.js):
1. 你点"模拟手机" / 填了 referer 点解析
│
▼
2. 扩展调用 updateSessionRules 放入一条会话规则
│
▼
3. 浏览器发出请求 → 网络层逐条比对规则的 condition
(tabIds? resourceTypes? urlFilter? initiatorDomains?)
│
├── 不命中 → 原样发出
▼ 命中
4. modifyHeaders 执行:按清单 set / remove 各个头
▼
5. 请求带着新头出门,服务端放行,资源能被嗅探和下载
两个值得留意的分工:
- 监听和修改是两套 API。
chrome.webRequest.onSendHeaders/onResponseStarted负责"听"——猫抓靠它们配合一个requestId → headers的 Map 完成嗅探;declarativeNetRequest 只管"改"。manifest.json 里两个权限都声明了。 - 规则 ID 是有意设计的。猫抓直接用
tabId或requestId当规则的id,因为 API 支持按 ID 删除,开和关的成本就是一次removeRuleIds。
实战用例:UA 伪装、referer 注入、单请求改写怎么配
UA 伪装:把请求头改成手机 UA
现象:有的站点按 UA 分发不同档位的资源,网页版只能拿到低清源。
配置:弹窗里点"模拟手机"。js/popup.js 的按钮只负责转发消息,干活的是 js/background.js 里的 mobileUserAgent():
chrome.declarativeNetRequest.updateSessionRules({
removeRuleIds: [tabId], // 先删旧规则,防止重复叠加
addRules: [{
"id": tabId, // 规则 ID = 标签页 ID,好删
"action": {
"type": "modifyHeaders",
"requestHeaders": [{ "header": "User-Agent", "operation": "set",
"value": G.MobileUserAgent }], // init.js 定义的 iPhone UA
},
// condition 只限当前标签页,覆盖全部资源类型
"condition": { "tabIds": [tabId],
"resourceTypes": Object.values(chrome.declarativeNetRequest.ResourceType) }
}]
});
验证:刷新页面,DevTools 的 Network 面板看请求头,UA 已变成 iPhone;重新嗅探到的资源换成移动端源。
自定义请求头:m3u8 解析器的 referer 输入框
现象:m3u8 主播放列表能打开,里面的 .ts 切片却全部 403——典型的防盗链,只认"从那个页面来"的请求。
配置:m3u8 解析器页面有一个 referer 输入框。填纯 URL 时按 referer 值处理;填 JSON 则一次配置多个头:
// js/m3u8.js:点"解析"时触发
let referer = $("#referer").val().trim();
if (referer) {
if (referer.startsWith("http")) {
setRequestHeaders({ referer: referer }); // 纯链接
} else {
setRequestHeaders(JSONparse(referer)); // JSON:{referer:..., origin:...}
}
}
js/function.js 里的 setRequestHeaders 走同样的会话规则通道,差别在 condition:只作用于 xmlhttprequest / media / image 三种嗅探相关的资源类型,而不是全站资源。
验证:重新点解析,切片列表正常加载、可合并下载;顺手在 Network 里打开任意切片请求,能看到 referer 已被注入。
防盗链精确改写:setHeaders 只动一个请求
现象:只想下载某一个资源,服务端校验 Referer 或 Cookie,但又不想"全局伪装"影响整个标签页的其他请求。
配置:setHeaders() 是精准版——用 requestId 当规则 ID,再用 urlFilter 把范围锁死到单个请求:
const rule = {
"id": parseInt(item.requestId), // 每个请求一条规则
"action": {
"type": "modifyHeaders",
"requestHeaders": Object.keys(item.requestHeaders).map(key => ({
header: key, operation: "set", value: item.requestHeaders[key]
}))
},
"condition": {
"resourceTypes": ["xmlhttprequest", "media", "image"],
"tabIds": [tabId],
"urlFilter": item.url // 只改写这一个 URL
}
};
下载页 js/downloader.js 的 referer 输入框、图片另存、预览播放,都走这条通道;带 Cookie 的条目还会被额外追加一条 Cookie 头。
验证:打开目标资源的预览或下载,Network 里只有目标请求带着新头,同标签页的其他请求原样通过。
容易踩的坑 & 怎么绕
⚠️ 排障清单,按"现象 → 原因 → 解法"列出:
| 现象 | 原因 | 解法 |
|---|---|---|
| 规则静默不生效 | initiatorDomains 条件要 Chrome 101+ / Firefox 113+ 才支持,旧版内核直接被跳过 | 升级浏览器内核;或在标签页上下文里触发功能,改用 tabIds 圈定范围 |
| 换了 UA 后资源反而 403/404 | 部分资源 URL 带时效签名(Expires、KID 之类参数),刷新页面后旧签名失效 | 重新嗅探拿新 URL,不要复用旧链接 |
| 关掉标签页后规则残留 | 会话规则存活到浏览器退出,不随标签页自动消失 | 猫抓的定时任务 clearRedundant 会遍历 getSessionRules,把 tabIds 已不存在的规则删掉 |
| 规则条数吃紧 | 规则总数有硬性上限(默认 5000,企业策略可放宽) | 规则 ID 用 tabId 或 requestId,一 tab 一规则,用完即删,别囤积 |
| Service Worker 重启后状态丢失 | MV3 后台空闲 5 分钟就被强杀,内存变量清零 | 会话规则由浏览器持有不丢;猫抓另把 featMobileTabId 等状态写入 storage 供重启后恢复 |
第一行对应的防御代码在 js/function.js:
// initiatorDomains 只支持 chrome 101+ firefox 113+
if (G.version < 101 || (G.isFirefox && G.version < 113)) {
callback && callback();
return; // 宁可跳过规则,也不报错
}
进阶 & 周边:规则上限、浏览器差异与性能
- 规则生命周期:上文全部用的是会话规则(session rules),浏览器退出即清空,天然适合"伪装"这种临时需求。别图省事做成动态规则,否则忘了清理会一直生效。
- 浏览器差异:Chrome / Edge 是主场;Firefox 走 Manifest V2 模式(见 manifest.firefox.json),同一功能要多一层版本判断,比如 js/preview.js 里就有
G.version < 102 || (G.isFirefox && G.version < 128)这类分支。 - 性能:规则匹配发生在浏览器网络层,条数越多越慢。猫抓每个标签页最多一两条规则,且 condition 都带
tabIds收窄范围;自己照着写也保持条件精确,少用*。
🧪 下次再碰到 403,先别急着下结论,看看是不是请求头的问题:点一下"模拟手机",或填个 referer 重新解析。如果还没用过 m3u8 解析器,找个公开的测试流地址丢进输入框,切片列表立刻铺开的过程,就是猫抓资源嗅探最直观的体感。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





