> (() => { const d = document, W = window, report = []; const now = new Date(); const safe = s => (s+"").replace(/\s+/g, " ").slice(0, 400); const isFn = v => typeof v === "function"; const fnSrc = fn => { try { return Function.prototype.toString.call(fn); } catch(e){ return String(fn); } }; const isNativeFn = fn => { try { return /\{\s*\[native code\]\s*\}/.test(fnSrc(fn)); } catch(e){ return false; } }; // 环境指纹 const env = { ua: navigator.userAgent, url: location.href, time: now.toISOString(), electron: !!(W.process && W.process.versions && W.process.versions.electron), nodeLike: !!(W.process && W.process.versions && W.process.versions.node), reactNativeWebView: !!(W.ReactNativeWebView && isFn(W.ReactNativeWebView.postMessage)), iosWebKit: !!(W.webkit && W.webkit.messageHandlers && Object.keys(W.webkit.messageHandlers||{}).length>0), webview2: !!(W.chrome && W.chrome.webview && isFn(W.chrome.webview.postMessage)), cordova: !!W.cordova, }; // 已知桥名字/路径 const suspects = [ "Android","android","JSBridge","JsBridge","NativeBridge","WebViewJavascriptBridge", "WeixinJSBridge","AlipayJSBridge","QQJSBridge","WVJBBridge","_dsbridge","dsBridge", "__bridge","tbsJS","ReactNativeWebView","webkit","external","cefQuery","cordova", "_cordovaNative","__crWeb","__gCrWeb","TencentWebView","TTJSBridge","AliJSBridge", "chrome" ]; const knownWindowFns = new Set([ "alert","confirm","prompt","open","close","print", "fetch","setTimeout","clearTimeout","setInterval","clearInterval", "requestAnimationFrame","cancelAnimationFrame","postMessage", "queueMicrotask","btoa","atob","eval","escape","unescape", "blur","focus","getComputedStyle","matchMedia","reportError", "scroll","scrollBy","scrollTo" ]); const skipObjects = new Set([ "window","self","globalThis","document","frames","parent","top", "location","history","navigator","performance","crypto","console", "Math","JSON","Intl","Reflect","Atomics","WebAssembly","CSS" ]); function add(line="") { report.push(line); } function listFns(obj, max=50){ const out = []; let keys = []; try { keys = Object.getOwnPropertyNames(obj); } catch(e){ return out; } for (const k of keys) { if (out.length >= max) break; let v; try { v = obj[k]; } catch(e){ continue; } if (isFn(v)) { out.push({name:k, native:isNativeFn(v), sig:safe(fnSrc(v))}); } } return out; } function summarizeObject(path, obj){ const type = Object.prototype.toString.call(obj); const keys = (()=>{ try { return Object.getOwnPropertyNames(obj).slice(0,100); } catch(e){ return []; } })(); const fns = listFns(obj, 20); add(`- ${path} :: ${type} keys=${keys.length}, sampleKeys=[${keys.slice(0,10).join(", ")}]`); if (fns.length) { add(` functions (${fns.length}):`); for (const f of fns) add(` • ${path}.${f.name} [native=${f.native}] ${f.sig}`); } } add("=== JSBridge / Native 注入自检报告 ==="); add(`Time: ${env.time}`); add(`URL : ${env.url}`); add(`UA : ${env.ua}`); add(""); add("== 环境特征 =="); add(JSON.stringify(env, null, 2)); add(""); // 针对性探测 add("== 已知桥探测 =="); for (const name of suspects) { let v; try { v = W[name]; } catch(e){ v = undefined; } if (typeof v === "undefined") continue; // 特判路径 if (name === "webkit") { const mh = v && v.messageHandlers; if (mh && typeof mh === "object") { const names = Object.keys(mh); add(`- webkit.messageHandlers 存在,通道数=${names.length}, 列表=[${names.slice(0,20).join(", ")}]`); for (const n of names.slice(0,20)) { const handler = mh[n]; if (handler) summarizeObject(`webkit.messageHandlers.${n}`, handler); } } else { summarizeObject("webkit", v); } continue; } if (name === "chrome") { const cw = v && v.webview; if (cw) summarizeObject("chrome.webview", cw); continue; } if (name === "external") { summarizeObject("external", v); continue; } if (name === "ReactNativeWebView") { summarizeObject("ReactNativeWebView", v); continue; } if (name === "cefQuery" && isFn(v)) { add(`- cefQuery 函数存在 [native=${isNativeFn(v)}] ${safe(fnSrc(v))}`); continue; } summarizeObject(name, v); } add(""); // 常见桥别名路径额外探测 try { if (W.webkit && W.webkit.messageHandlers) { const names = Object.keys(W.webkit.messageHandlers); for (const n of names) { const h = W.webkit.messageHandlers[n]; if (h && isFn(h.postMessage)) { add(`- webkit.messageHandlers.${n}.postMessage [native=${isNativeFn(h.postMessage)}] ${safe(fnSrc(h.postMessage))}`); } } } } catch(e){} try { if (W.chrome && W.chrome.webview) { add(`- chrome.webview.postMessage [native=${isNativeFn(W.chrome.webview.postMessage)}] ${safe(fnSrc(W.chrome.webview.postMessage))}`); if (isFn(W.chrome.webview.addEventListener)) add(`- chrome.webview.addEventListener [native=${isNativeFn(W.chrome.webview.addEventListener)}] ${safe(fnSrc(W.chrome.webview.addEventListener))}`); } } catch(e){} // 函数劫持/替换线索 add(""); add("== 核心窗口 API 状态(是否仍为原生) =="); for (const k of ["alert","confirm","prompt","open","close","fetch","postMessage"]) { try { const v = W[k]; if (isFn(v)) add(`- ${k} native=${isNativeFn(v)} ${safe(fnSrc(v))}`); else add(`- ${k} type=${typeof v}`); } catch(e){ add(`- ${k} `); } } // 广泛枚举:window 上看起来“不标准”的原生函数 add(""); add("== 可疑原生函数枚举(window 直接属性) =="); let names = []; try { names = Object.getOwnPropertyNames(W); } catch(e){ names = []; } const suspicious = []; for (const k of names) { if (skipObjects.has(k)) continue; let v; try { v = W[k]; } catch(e){ continue; } if (isFn(v)) { if (knownWindowFns.has(k)) continue; // 忽略常见 if (isNativeFn(v)) suspicious.push({name:k, sig:safe(fnSrc(v))}); continue; } // 对象里挑“疑似桥”函数 if (v && typeof v === "object") { // 名称命中关键词更可疑 if (/(bridge|jsbridge|native|android|ios|webkit|wvjb|weixin|qq|alipay|cef|tbs|webview)/i.test(k)) { const fns = listFns(v, 10).filter(x=>x.native); if (fns.length) { add(`- ${k} 可能为桥对象,含原生函数 ${fns.length} 个`); for (const f of fns) add(` • ${k}.${f.name} ${f.sig}`); } } } } if (suspicious.length) { for (const it of suspicious.slice(0, 100)) add(`- ${it.name} ${it.sig}`); if (suspicious.length > 100) add(`… 其余 ${suspicious.length-100} 个已省略`); } else { add("- 未发现明显“非常见”的原生函数(不代表没有桥,仅表示未命中上述启发式)。"); } // Electron / Node 细节 add(""); add("== Electron / Node 线索 =="); try { if (env.electron) add(`- process.versions.electron = ${W.process.versions.electron}`); if (env.nodeLike) add(`- process.versions.node = ${W.process.versions.node}`); if (typeof W.require === "function") add(`- window.require [native=${isNativeFn(W.require)}] ${safe(fnSrc(W.require))}`); if (W.process && W.process.type) add(`- process.type = ${W.process.type}`); } catch(e){} // 输出到 textarea(不逃逸) const ta = d.createElement("textarea"); ta.readOnly = true; ta.wrap = "off"; ta.spellcheck = false; ta.style.cssText = [ "position:fixed","inset:12px","z-index:2147483647", "width:calc(100% - 24px)","height:calc(100% - 24px)", "white-space:pre","font:12px/1.5 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace", "background:#0b1020","color:#cde3ff","border:1px solid #334","border-radius:8px","padding:10px","box-sizing:border-box" ].join(";"); ta.value = report.join("\n"); (d.body || d.documentElement).appendChild(ta); })();