您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
DeepQuery 客户端(仅沙箱内可见)。密钥存放到 GM 存储,不写在代码里;负责签名并与 Core 通信。
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/548365/1654742/DeepQuery%20Secure%20Client%20%28GM%20storage%20key%29.js
// ==UserScript== // @name DeepQuery Secure Client (GM storage key) // @namespace dq.secure.v2.client // @version 2.1.0 // @description DeepQuery 客户端(仅沙箱内可见)。密钥存放到 GM 存储,不写在代码里;负责签名并与 Core 通信。 // @author you // @match http://*/* // @match https://*/* // @include about:blank // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_setClipboard // ==/UserScript== (function () { 'use strict'; /******** 基本配置(无需放密钥) ********/ const CHANNEL = '__DQ_SECURE_V2__'; // 必须与 Core 保持一致 const KEY_STORE_NAME = 'DQ_SECURE_V2_KEY_B64'; // GM 存储的键名 const EXPOSE_TOP_PROXY = false; // 设为 true 可零改动复用 top.DeepQuery(仅对本沙箱可见) /******** 小工具 ********/ const te = new TextEncoder(); function u8FromB64(b64) { if (!b64 || typeof b64 !== 'string') return null; const s = b64.replace(/-/g, '+').replace(/_/g, '/'); const pad = s.length % 4 ? 4 - (s.length % 4) : 0; const bin = atob(s + '='.repeat(pad)); const u8 = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i); return u8; } function b64FromU8(u8) { let s = ''; for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]); return btoa(s).replace(/=+$/,''); } const hasGM = (typeof GM !== 'undefined' && GM) || {}; const gmGet = (k, d=null) => (hasGM.getValue ? hasGM.getValue(k, d) : (typeof GM_getValue === 'function' ? Promise.resolve(GM_getValue(k, d)) : Promise.resolve(d))); const gmSet = (k, v) => (hasGM.setValue ? hasGM.setValue(k, v) : (typeof GM_setValue === 'function' ? Promise.resolve(GM_setValue(k, v)) : Promise.resolve())); const gmReg = (name, fn) => (hasGM.registerMenuCommand ? GM.registerMenuCommand(name, fn) : (typeof GM_registerMenuCommand === 'function' ? GM_registerMenuCommand(name, fn) : null)); const gmClip = (text) => (typeof GM_setClipboard === 'function' ? GM_setClipboard(text) : navigator.clipboard?.writeText?.(text)); /******** HMAC ********/ async function sha256U8(u8) { const buf = await crypto.subtle.digest('SHA-256', u8); return new Uint8Array(buf); } async function hmacSignRaw(keyU8, bytes) { const k = await crypto.subtle.importKey('raw', keyU8, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']); const sig = await crypto.subtle.sign('HMAC', k, bytes); return new Uint8Array(sig); } /******** 读取/管理密钥(来自 GM 存储) ********/ let KEY_U8 = null; let keyLoadPromise = null; async function ensureKeyLoaded() { if (KEY_U8) return KEY_U8; if (!keyLoadPromise) { keyLoadPromise = (async () => { const b64 = (await gmGet(KEY_STORE_NAME, '')).trim(); if (!b64) throw new Error('[DeepQuery Client] KEY_MISSING: 请先在脚本菜单里设置密钥'); const u8 = u8FromB64(b64); if (!u8 || u8.length < 16) throw new Error('[DeepQuery Client] KEY_INVALID: GM 存储中的密钥格式不正确'); KEY_U8 = u8; return KEY_U8; })(); } return keyLoadPromise; } // 菜单:设置/生成/查看密钥 gmReg('Set DeepQuery Key…', async () => { const b64 = prompt('粘贴与你的 Core 相同的密钥(base64):\n(提示:不要泄露给网页脚本)'); if (!b64) return; const u8 = u8FromB64(b64.trim()); if (!u8 || u8.length < 16) { alert('密钥格式不正确(必须是 base64,建议 >= 32 字节)'); return; } await gmSet(KEY_STORE_NAME, b64.trim()); KEY_U8 = u8; keyLoadPromise = Promise.resolve(u8); alert('已保存密钥到 GM 存储。'); }); gmReg('Generate Random Key', async () => { const u8 = new Uint8Array(32); crypto.getRandomValues(u8); const b64 = b64FromU8(u8); await gmSet(KEY_STORE_NAME, b64); KEY_U8 = u8; keyLoadPromise = Promise.resolve(u8); gmClip && gmClip(b64); alert('已生成随机密钥并保存到 GM 存储;密钥已复制到剪贴板。\n⚠️ 请同步更新 Core 脚本中的密钥为相同值!'); }); gmReg('Show Current Key (base64)', async () => { const b64 = await gmGet(KEY_STORE_NAME, ''); if (!b64) { alert('尚未设置密钥'); return; } gmClip && gmClip(b64); alert('已复制当前密钥到剪贴板。'); }); /******** 与 Core 通信 ********/ const pending = new Map(); function rid() { return (Date.now().toString(36) + Math.random().toString(36).slice(2, 10)).toUpperCase(); } function send(payload) { // 只与顶层 Core 打交道(Core 会做逐级转发) window.top.postMessage({ [CHANNEL]: payload }, '*'); } window.addEventListener('message', (e) => { const msg = e.data && e.data[CHANNEL]; if (!msg || msg.cmd !== 'RESP' || !msg.id) return; const hit = pending.get(msg.id); if (!hit) return; pending.delete(msg.id); hit.resolve(msg.res); }, false); async function request(spec) { const key = await ensureKeyLoaded(); // 确保已加载 GM 密钥 const id = rid(); const ts = Date.now(); const nonce = rid() + Math.random().toString(36).slice(2); const header = te.encode(id + '\n' + ts + '\n' + nonce + '\n'); const bodyHash = await sha256U8(te.encode(JSON.stringify(spec || {}))); const toSign = new Uint8Array(header.length + bodyHash.length); toSign.set(header, 0); toSign.set(bodyHash, header.length); const sigU8 = await hmacSignRaw(key, toSign); const sigB64 = b64FromU8(sigU8); const timeout = typeof spec?.timeout === 'number' ? Math.max(200, spec.timeout + 500) : 6000; return new Promise((resolve) => { pending.set(id, { resolve }); send({ cmd: 'REQ', id, ts, nonce, sigB64, spec }); setTimeout(() => { if (pending.has(id)) { pending.delete(id); resolve({ ok: false, error: 'TIMEOUT' }); } }, timeout); }); } /******** 暴露 API(与旧版一致) ********/ const DeepQuery = { async get(spec = {}) { return request(spec); }, async attr({ framePath, chain, name, timeout }) { return request({ framePath, chain, timeout, pick: { attr: name } }); }, async prop({ framePath, chain, name, timeout }) { return request({ framePath, chain, timeout, pick: { prop: name } }); }, async text({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { text: true } }); }, async html({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { html: true } }); }, async rect({ framePath, chain, timeout }) { return request({ framePath, chain, timeout, pick: { rect: true } }); }, version: '2.1.0-client' }; // 方式 A(推荐):直接用 DeepQuery try { window.DeepQuery = DeepQuery; } catch {} // 方式 B(零改动):在当前沙箱里暴露 top.DeepQuery 代理(页面不可见) if (EXPOSE_TOP_PROXY) { try { Object.defineProperty(window.top, 'DeepQuery', { configurable: true, get() { return DeepQuery; } }); } catch {} } // 尝试提前加载密钥(不影响后续调用) ensureKeyLoaded().catch(() => { // 首次未设置密钥时静默,调用时会抛清晰错误 }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址