/** * Content Script - 使用 DevTools Network API 获取 XHR 请求 * * 注意:这个脚本只在 DevTools 面板中使用 * 实际的请求捕获通过 chrome.devtools.network.onRequestFinished 实现 */ // 这个文件现在已经不需要了,因为请求捕获在 DevTools 面板中完成 // 但保留文件以避免错误 // 将完整的拦截脚本注入到页面上下文 const script = document.createElement('script'); script.textContent = ` (function() { 'use strict'; console.log('🔧 [页面上下文] 拦截脚本已执行'); console.log('🔧 [页面上下文] XMLHttpRequest 类型:', typeof XMLHttpRequest); console.log('🔧 [页面上下文] fetch 类型:', typeof fetch); // 在页面上下文中存储解密记录 if (!window.__XHR_DECRYPT_DATA__) { window.__XHR_DECRYPT_DATA__ = { requests: [], addRequest: function(record) { this.requests.push(record); if (this.requests.length > 100) { this.requests.shift(); } // 通知 content script window.dispatchEvent(new CustomEvent('__XHR_DECRYPTED__', { detail: record })); }, getRequests: function() { return this.requests; }, clear: function() { this.requests = []; } }; } const decryptedData = window.__XHR_DECRYPT_DATA__; const requestHeadersMap = new Map(); const skipDecryptPaths = ['v1/picture/upload']; // ========== 密钥获取策略管理器 ========== const KeyGetStrategies = { strategies: [], // 注册密钥获取策略 register: function(strategy) { if (strategy && typeof strategy.getName === 'function' && typeof strategy.getKey === 'function') { this.strategies.push({ name: strategy.getName(), priority: strategy.priority || 100, getKey: strategy.getKey, canHandle: strategy.canHandle || (() => true) }); // 按优先级排序(数字越小优先级越高) this.strategies.sort((a, b) => a.priority - b.priority); } }, // 获取密钥(按优先级尝试所有策略) getKey: function(url, uuid, requestHeaders) { for (const strategy of this.strategies) { try { if (strategy.canHandle(url, uuid, requestHeaders)) { const key = strategy.getKey(url, uuid, requestHeaders); if (key) { console.log('🔑 [密钥策略] 使用策略:', strategy.name, '获取密钥成功'); return { key: key, strategy: strategy.name }; } } } catch (error) { console.warn('🔑 [密钥策略] 策略', strategy.name, '执行失败:', error); continue; } } console.warn('🔑 [密钥策略] 所有策略都无法获取密钥'); return null; }, // 初始化默认策略 init: function() { // 策略1: 从页面工具获取密钥(优先级最高) this.register({ name: '页面工具获取', priority: 10, canHandle: function(url, uuid, requestHeaders) { return window.__DECRYPT_TOOLS__ && window.__DECRYPT_TOOLS__.getKey; }, getKey: function(url, uuid, requestHeaders) { const tools = window.__DECRYPT_TOOLS__; if (tools && tools.getKey) { return tools.getKey(url, uuid); } return null; } }); // 策略2: 从请求头计算密钥(timestamp + TraceId) this.register({ name: '请求头计算', priority: 20, canHandle: function(url, uuid, requestHeaders) { const timestamp = requestHeaders?.time || requestHeaders?.Time || ''; const traceId = requestHeaders?.TraceId || requestHeaders?.['trace-id'] || ''; return !!(timestamp && traceId); }, getKey: function(url, uuid, requestHeaders) { const timestamp = requestHeaders?.time || requestHeaders?.Time || ''; const traceId = requestHeaders?.TraceId || requestHeaders?.['trace-id'] || ''; if (timestamp && traceId) { return String(timestamp + traceId).slice(0, 16); } return null; } }); // 可以在这里添加更多密钥获取策略 // 例如:从 localStorage 获取、从 URL 参数获取、从 Cookie 获取等 } }; // ========== 解密策略管理器 ========== const DecryptStrategies = { strategies: [], // 注册解密策略 register: function(strategy) { if (strategy && typeof strategy.getName === 'function' && typeof strategy.decrypt === 'function') { this.strategies.push({ name: strategy.getName(), priority: strategy.priority || 100, decrypt: strategy.decrypt, canHandle: strategy.canHandle || (() => true) }); // 按优先级排序 this.strategies.sort((a, b) => a.priority - b.priority); } }, // 解密数据(按优先级尝试所有策略) decrypt: function(encryptedData, key, isRequest = false) { for (const strategy of this.strategies) { try { if (strategy.canHandle(encryptedData, key, isRequest)) { const decrypted = strategy.decrypt(encryptedData, key, isRequest); if (decrypted !== null && decrypted !== undefined && decrypted !== encryptedData) { console.log('🔓 [解密策略] 使用策略:', strategy.name, '解密成功'); return { data: decrypted, strategy: strategy.name }; } } } catch (error) { console.warn('🔓 [解密策略] 策略', strategy.name, '执行失败:', error); continue; } } console.warn('🔓 [解密策略] 所有策略都无法解密数据'); return { data: encryptedData, strategy: null }; }, // 初始化默认策略 init: function() { // 策略1: 使用页面工具解密(优先级最高) this.register({ name: '页面工具解密', priority: 10, canHandle: function(encryptedData, key, isRequest) { return window.__DECRYPT_TOOLS__ && window.__DECRYPT_TOOLS__.Decrypt; }, decrypt: function(encryptedData, key, isRequest) { const tools = window.__DECRYPT_TOOLS__; if (!tools || !tools.Decrypt) { return null; } try { if (isRequest) { // 请求数据格式:{ data: "base64String" } if (encryptedData && encryptedData.data && typeof encryptedData.data === 'string') { const decrypted = tools.Decrypt(encryptedData.data, key); if (decrypted) { return JSON.parse(decrypted); } } } else { // 响应数据格式:字符串或对象 if (typeof encryptedData === 'string') { const decrypted = tools.Decrypt(encryptedData, key); if (decrypted) { try { return JSON.parse(decrypted); } catch (e) { if (window.JSON5) { return window.JSON5.parse(decrypted); } return decrypted; } } } } } catch (error) { console.error('解密失败:', error); } return null; } }); // 策略2: 使用 CryptoJS 直接解密(如果可用) this.register({ name: 'CryptoJS 解密', priority: 20, canHandle: function(encryptedData, key, isRequest) { return typeof CryptoJS !== 'undefined' && key; }, decrypt: function(encryptedData, key, isRequest) { try { if (typeof CryptoJS === 'undefined') { return null; } let dataToDecrypt = null; if (isRequest) { if (encryptedData && encryptedData.data && typeof encryptedData.data === 'string') { dataToDecrypt = encryptedData.data; } else { return null; } } else { if (typeof encryptedData === 'string') { dataToDecrypt = encryptedData; } else { return null; } } if (!dataToDecrypt) { return null; } // 清理数据 const cleanData = dataToDecrypt.replace(/\s/g, '').replace(/"/g, ''); const cleanKey = key.replace(/\s/g, '').replace(/"/g, ''); // AES 解密 const decrypt = CryptoJS.AES.decrypt( cleanData, CryptoJS.enc.Utf8.parse(cleanKey), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ); const decrypted = decrypt.toString(CryptoJS.enc.Utf8); if (!decrypted) { return null; } // 尝试解析 JSON try { return JSON.parse(decrypted); } catch (e) { if (window.JSON5) { return window.JSON5.parse(decrypted); } return decrypted; } } catch (error) { return null; } } }); // 可以在这里添加更多解密策略 // 例如:其他加密算法、不同的填充方式等 } }; // 初始化策略管理器 KeyGetStrategies.init(); DecryptStrategies.init(); // ========== 便捷函数 ========== // 获取密钥 function getTimestampKey(url, uuid, requestHeaders) { const result = KeyGetStrategies.getKey(url, uuid, requestHeaders); return result ? result.key : null; } // 解密请求数据 function decryptRequestData(encryptedData, timestampKey) { if (!timestampKey) { return encryptedData; } const result = DecryptStrategies.decrypt(encryptedData, timestampKey, true); return result.data; } // 解密响应数据 function decryptResponseData(encryptedData, timestampKey) { if (!timestampKey) { return encryptedData; } const result = DecryptStrategies.decrypt(encryptedData, timestampKey, false); return result.data; } // 暴露策略管理器,允许外部注册新策略 window.__XHR_DECRYPT_STRATEGIES__ = { keyGetStrategies: KeyGetStrategies, decryptStrategies: DecryptStrategies }; // ========== 拦截 XMLHttpRequest ========== // 必须在页面脚本运行前拦截,否则会被覆盖 (function() { if (typeof XMLHttpRequest === 'undefined') { console.warn('⚠️ [页面上下文] XMLHttpRequest 未定义'); return; } // 保存原始函数(必须在任何其他脚本运行前保存) const originalXHR = window.XMLHttpRequest; const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; // 检查是否已经被拦截过 const openStr = originalOpen.toString(); if (openStr.includes('_method') || openStr.includes('__XHR_DECRYPT')) { console.log('⚠️ [页面上下文] XHR 可能已被其他脚本拦截'); } XMLHttpRequest.prototype.open = function(method, url, ...args) { this._method = method; this._url = url; this._requestHeaders = {}; requestHeadersMap.set(this, {}); console.log('🔍 [页面上下文] 拦截 XHR open:', method, url); // 确保返回正确的结果 const result = originalOpen.apply(this, [method, url, ...args]); return result; }; XMLHttpRequest.prototype.setRequestHeader = function(name, value) { if (!this._requestHeaders) { this._requestHeaders = {}; } this._requestHeaders[name] = value; const headers = requestHeadersMap.get(this) || {}; headers[name] = value; requestHeadersMap.set(this, headers); return originalSetRequestHeader.apply(this, [name, value]); }; XMLHttpRequest.prototype.send = function(body) { const url = this._url; const method = this._method; const requestId = Date.now() + Math.random(); const shouldSkip = skipDecryptPaths.some(path => url.includes(path)); // 监听响应 this.addEventListener('loadend', function() { if (shouldSkip) return; try { const headers = requestHeadersMap.get(this) || {}; const uuid = this.getResponseHeader('uuid'); if (!uuid) { return; } // 获取密钥(包含策略信息) const keyResult = KeyGetStrategies.getKey(url, uuid, headers); if (!keyResult || !keyResult.key) { console.warn('无法获取密钥', { url, uuid, headers }); return; } const timestampKey = keyResult.key; const keyStrategy = keyResult.strategy; // 解密请求数据(包含策略信息) let requestBody = null; let requestDecryptStrategy = null; if (body) { try { const parsed = typeof body === 'string' ? JSON.parse(body) : body; const decryptResult = DecryptStrategies.decrypt(parsed, timestampKey, true); requestBody = decryptResult.data; requestDecryptStrategy = decryptResult.strategy; } catch (e) { requestBody = body; } } // 解密响应数据(包含策略信息) let responseData = null; try { if (this.responseText) { responseData = JSON.parse(this.responseText); } } catch (e) { responseData = this.responseText || this.response; } const decryptResult = DecryptStrategies.decrypt(responseData, timestampKey, false); const decryptedResponse = decryptResult.data; const responseDecryptStrategy = decryptResult.strategy; // 记录解密结果 const decryptedRecord = { id: requestId, url: url, method: method, uuid: uuid, timestampKey: timestampKey, keyStrategy: keyStrategy, request: requestBody, requestDecryptStrategy: requestDecryptStrategy, response: decryptedResponse, responseDecryptStrategy: responseDecryptStrategy, timestamp: new Date().toISOString() }; // 存储到页面上下文 decryptedData.addRequest(decryptedRecord); // 输出到控制台 console.group('🔓 XHR 解密 [' + method + '] ' + url); console.log('🔑 密钥:', timestampKey, '(策略: ' + keyStrategy + ')'); if (requestDecryptStrategy) { console.log('📤 请求:', requestBody, '(解密策略: ' + requestDecryptStrategy + ')'); } else { console.log('📤 请求:', requestBody); } if (responseDecryptStrategy) { console.log('📥 响应:', decryptedResponse, '(解密策略: ' + responseDecryptStrategy + ')'); } else { console.log('📥 响应:', decryptedResponse); } console.log('📍 UUID:', uuid); console.groupEnd(); } catch (error) { console.error('❌ 解密过程出错:', error); } }); return originalSend.apply(this, [body]); }; console.log('✅ [页面上下文] XHR 拦截已设置'); console.log('✅ [页面上下文] originalOpen 类型:', typeof originalOpen); console.log('✅ [页面上下文] XMLHttpRequest.prototype.open:', typeof XMLHttpRequest.prototype.open); // 验证拦截是否真的生效 setTimeout(function() { const testXhr = new XMLHttpRequest(); const testOpenStr = testXhr.open.toString(); if (testOpenStr.includes('_method')) { console.log('✅ [页面上下文] XHR 拦截验证成功'); } else { console.error('❌ [页面上下文] XHR 拦截验证失败,可能被覆盖'); console.error(' 当前 open 函数:', testOpenStr.substring(0, 100)); } }, 500); })(); // ========== 拦截 Fetch ========== // 必须在页面脚本运行前拦截 (function() { if (typeof fetch === 'undefined' || !window.fetch) { console.warn('⚠️ [页面上下文] fetch 未定义'); return; } // 保存原始函数 const originalFetch = window.fetch; // 检查是否已经被拦截过 const fetchStr = originalFetch.toString(); if (fetchStr.includes('clonedResponse') || fetchStr.includes('__XHR_DECRYPT')) { console.log('⚠️ [页面上下文] Fetch 可能已被其他脚本拦截'); } window.fetch = function(...args) { const [url, options = {}] = args; const method = options.method || 'GET'; const requestId = Date.now() + Math.random(); const shouldSkip = skipDecryptPaths.some(path => url.includes(path)); const originalBody = options.body; console.log('🔍 [页面上下文] 拦截 Fetch:', method, url); return originalFetch.apply(this, args).then(async (response) => { if (shouldSkip) return response; try { const clonedResponse = response.clone(); const uuid = clonedResponse.headers.get('uuid'); if (!uuid) { return response; } const headers = options.headers || {}; const timestamp = headers.time || headers.Time || ''; const traceId = headers.TraceId || headers['trace-id'] || ''; // 获取密钥(包含策略信息) const keyResult = KeyGetStrategies.getKey(url, uuid, { time: timestamp, TraceId: traceId }); if (!keyResult || !keyResult.key) { console.warn('无法获取密钥 (Fetch)', { url, uuid }); return response; } const timestampKey = keyResult.key; const keyStrategy = keyResult.strategy; // 解密请求数据(包含策略信息) let requestBody = null; let requestDecryptStrategy = null; if (originalBody) { try { if (typeof originalBody === 'string') { const parsed = JSON.parse(originalBody); const decryptResult = DecryptStrategies.decrypt(parsed, timestampKey, true); requestBody = decryptResult.data; requestDecryptStrategy = decryptResult.strategy; } else { requestBody = originalBody; } } catch (e) { requestBody = originalBody; } } // 解密响应数据(包含策略信息) let responseData = null; try { const text = await clonedResponse.text(); if (text) { try { responseData = JSON.parse(text); } catch (e) { responseData = text; } } } catch (e) { console.error('读取响应数据失败:', e); } const decryptResult = DecryptStrategies.decrypt(responseData, timestampKey, false); const decryptedResponse = decryptResult.data; const responseDecryptStrategy = decryptResult.strategy; // 记录解密结果 const decryptedRecord = { id: requestId, url: url, method: method, uuid: uuid, timestampKey: timestampKey, keyStrategy: keyStrategy, request: requestBody, requestDecryptStrategy: requestDecryptStrategy, response: decryptedResponse, responseDecryptStrategy: responseDecryptStrategy, timestamp: new Date().toISOString() }; // 存储到页面上下文 decryptedData.addRequest(decryptedRecord); // 输出到控制台 console.group('🔓 Fetch 解密 [' + method + '] ' + url); console.log('🔑 密钥:', timestampKey, '(策略: ' + keyStrategy + ')'); if (requestDecryptStrategy) { console.log('📤 请求:', requestBody, '(解密策略: ' + requestDecryptStrategy + ')'); } else { console.log('📤 请求:', requestBody); } if (responseDecryptStrategy) { console.log('📥 响应:', decryptedResponse, '(解密策略: ' + responseDecryptStrategy + ')'); } else { console.log('📥 响应:', decryptedResponse); } console.log('📍 UUID:', uuid); console.groupEnd(); } catch (error) { console.error('❌ Fetch 解密过程出错:', error); } return response; }).catch((error) => { console.error('❌ Fetch 请求失败:', error); return Promise.reject(error); }); }; console.log('✅ [页面上下文] Fetch 拦截已设置'); console.log('✅ [页面上下文] originalFetch 类型:', typeof originalFetch); console.log('✅ [页面上下文] window.fetch:', typeof window.fetch); // 验证拦截是否真的生效 setTimeout(function() { const fetchStr = window.fetch.toString(); if (fetchStr.includes('clonedResponse') || fetchStr.includes('requestId')) { console.log('✅ [页面上下文] Fetch 拦截验证成功'); } else { console.error('❌ [页面上下文] Fetch 拦截验证失败,可能被覆盖'); console.error(' 当前 fetch 函数:', fetchStr.substring(0, 100)); } }, 500); })(); // 暴露 API 供 DevTools 面板使用 window.__XHR_DECRYPT_EXTENSION__ = { getDecryptedRequests: function() { return decryptedData.getRequests(); }, clearDecryptedRequests: function() { decryptedData.clear(); console.log('✅ 已清空解密记录'); } }; console.log('✅ [页面上下文] 拦截脚本初始化完成'); console.log('✅ [页面上下文] 已拦截 XHR:', typeof XMLHttpRequest !== 'undefined'); console.log('✅ [页面上下文] 已拦截 Fetch:', typeof fetch !== 'undefined'); // 测试一下拦截是否真的工作 setTimeout(function() { console.log('🧪 [页面上下文] 测试:检查拦截状态'); console.log('🧪 [页面上下文] XMLHttpRequest.prototype.open:', typeof XMLHttpRequest.prototype.open); console.log('🧪 [页面上下文] window.fetch:', typeof window.fetch); // 尝试创建一个测试请求(仅用于验证) try { const testXhr = new XMLHttpRequest(); console.log('🧪 [页面上下文] 测试:可以创建 XMLHttpRequest 实例'); } catch (e) { console.error('🧪 [页面上下文] 测试:无法创建 XMLHttpRequest:', e); } }, 1000); })(); `; // 注入到页面上下文 // 必须在页面脚本运行之前注入,否则无法拦截 function injectToPage() { try { // 方式1:尝试注入到 head if (document.head) { document.head.appendChild(script.cloneNode(true)); console.log('✅ 拦截脚本已注入到 head'); return; } // 方式2:注入到 documentElement if (document.documentElement) { document.documentElement.appendChild(script.cloneNode(true)); console.log('✅ 拦截脚本已注入到 documentElement'); return; } // 方式3:如果都不行,等待 DOM 加载 console.warn('⚠️ DOM 未准备好,等待加载...'); const checkInterval = setInterval(function () { if (document.head || document.documentElement) { clearInterval(checkInterval); injectToPage(); } }, 50); // 超时保护 setTimeout(function () { clearInterval(checkInterval); console.error('❌ 注入超时,DOM 可能无法加载'); }, 5000); } catch (e) { console.error('❌ 注入脚本失败:', e); } } // 立即尝试注入(不等待) try { // 如果 DOM 已准备好,立即注入 if (document.head || document.documentElement) { injectToPage(); } else { // 如果还没准备好,使用多种方式等待 console.log('⏳ DOM 未准备好,准备注入...'); // 方式1:等待 DOMContentLoaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { console.log('📄 DOMContentLoaded 触发,开始注入'); injectToPage(); }, { once: true }); } // 方式2:也尝试立即注入(某些浏览器允许) setTimeout(function () { if (document.head || document.documentElement) { console.log('⏱️ 延迟注入尝试'); injectToPage(); } }, 0); // 方式3:监听 readystatechange document.addEventListener('readystatechange', function () { if (document.readyState !== 'loading') { console.log('📄 readystatechange 触发,readyState:', document.readyState); injectToPage(); } }, { once: true }); } } catch (e) { console.error('❌ 注入过程出错:', e); } // 在 Content Script 中监听来自页面上下文的事件 window.addEventListener('__XHR_DECRYPTED__', function (event) { if (event.detail) { decryptedRequests.push(event.detail); if (decryptedRequests.length > 100) { decryptedRequests.shift(); } // 发送到 background if (typeof chrome !== 'undefined' && chrome.runtime) { chrome.runtime.sendMessage({ type: 'DECRYPTED_REQUEST', data: event.detail }); } } }); // 监听来自 popup 的消息 if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.onMessage) { chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { if (message.action === 'getDecryptedRequests') { // 直接返回 content script 中存储的数据 // content script 会从页面事件中接收更新 sendResponse({ data: decryptedRequests }); return true; } else if (message.action === 'clearDecryptedRequests') { decryptedRequests = []; // 清除页面上下文的记录 const clearScript = document.createElement('script'); clearScript.textContent = ` (function() { if (window.__XHR_DECRYPT_EXTENSION__) { window.__XHR_DECRYPT_EXTENSION__.clearDecryptedRequests(); } })(); `; document.documentElement.appendChild(clearScript); clearScript.remove(); sendResponse({ success: true }); } }); } // 暴露 API 供 popup 使用(Content Script 上下文) window.__XHR_DECRYPT_EXTENSION__ = { getDecryptedRequests: function () { // 返回 content script 中存储的数据 return decryptedRequests; }, clearDecryptedRequests: function () { decryptedRequests = []; // 清除页面上下文的记录 const clearScript = document.createElement('script'); clearScript.textContent = ` (function() { if (window.__XHR_DECRYPT_EXTENSION__) { window.__XHR_DECRYPT_EXTENSION__.clearDecryptedRequests(); } })(); `; document.documentElement.appendChild(clearScript); clearScript.remove(); } }; console.log('✅ Content Script 已启动'); console.log('📊 Content Script 当前记录数:', decryptedRequests.length); console.log('📊 Content Script document.readyState:', document.readyState); console.log('📊 Content Script 准备注入拦截脚本...'); console.log('📊 Content Script window.location:', window.location.href); // 立即检查是否已经注入成功(某些情况下可能已经注入) try { const testScript = document.createElement('script'); testScript.textContent = ` (function() { if (window.__XHR_DECRYPT_EXTENSION__) { console.log('✅ [页面上下文] 拦截对象已存在'); } else { console.warn('⚠️ [页面上下文] 拦截对象不存在'); } if (window.__XHR_DECRYPT_DATA__) { console.log('✅ [页面上下文] 数据对象已存在'); } else { console.warn('⚠️ [页面上下文] 数据对象不存在'); } })(); `; if (document.head || document.documentElement) { (document.head || document.documentElement).appendChild(testScript); testScript.remove(); } } catch (e) { console.error('❌ 检查注入状态失败:', e); } // 延迟检查注入是否成功 setTimeout(function () { // 通过 DevTools API 检查 if (typeof chrome !== 'undefined' && chrome.devtools && chrome.devtools.inspectedWindow) { chrome.devtools.inspectedWindow.eval('typeof window.__XHR_DECRYPT_EXTENSION__', function (result, isException) { if (isException) { console.warn('⚠️ 无法访问页面上下文,可能注入失败。错误:', result); } else { if (result === 'object') { console.log('✅ 拦截脚本注入成功,页面上下文对象存在'); } else { console.warn('⚠️ 拦截脚本可能未注入成功,页面上下文对象类型:', result); } } }); } else { // 如果没有 DevTools,使用其他方式检查 const checkScript = document.createElement('script'); checkScript.textContent = ` (function() { console.log('🔍 [页面上下文] 检查拦截状态...'); console.log('🔍 [页面上下文] __XHR_DECRYPT_EXTENSION__:', typeof window.__XHR_DECRYPT_EXTENSION__); console.log('🔍 [页面上下文] __XHR_DECRYPT_DATA__:', typeof window.__XHR_DECRYPT_DATA__); console.log('🔍 [页面上下文] XMLHttpRequest:', typeof XMLHttpRequest); console.log('🔍 [页面上下文] fetch:', typeof fetch); // 检查拦截是否真的工作 if (XMLHttpRequest.prototype.open.toString().includes('_method')) { console.log('✅ [页面上下文] XHR 拦截已生效'); } else { console.warn('⚠️ [页面上下文] XHR 拦截可能未生效'); } if (window.fetch.toString().includes('clonedResponse') || window.fetch.toString().includes('requestId')) { console.log('✅ [页面上下文] Fetch 拦截已生效'); } else { console.warn('⚠️ [页面上下文] Fetch 拦截可能未生效'); } })(); `; if (document.head || document.documentElement) { (document.head || document.documentElement).appendChild(checkScript); checkScript.remove(); } } }, 3000); }) ();