// ==UserScript== // @name 查看全局属性 // @description 一键查看挂载到window上的非原生属性,并注入一个$searchKey函数搜索属性名。 // @namespace Sency // @author Sency // @version 1.1.2 // @include * // @grant unsafeWindow // @grant GM_registerMenuCommand // @run-at document-idle // ==/UserScript== 'use strict'; const MAX_DEPTH = 20; //最大搜索深度,0不设限 const SEARCH_FUNCTION_NAME = '$searchKey'; const tag = unsafeWindow == unsafeWindow.top ? 'top' : location.origin + location.pathname; GM_registerMenuCommand(tag, () => { let iframe = document.getElementById('iframe_for_test'); if (!iframe) { iframe = document.createElement('iframe'); iframe.id = 'iframe_for_test'; iframe.style.display = 'none'; document.body.appendChild(iframe); //iframe不能移除 } const iWindow = iframe.contentWindow; //反劫持 const _Object = iWindow.Object; const _String = iWindow.String; const _Array = iWindow.Array; const _Map = iWindow.Map; const _RegExp = iWindow.RegExp; const _Promise = iWindow.Promise; const _console = iWindow.console; function getType(item) { const str = new _String(_Object.prototype.toString.call(item)); return str.slice(8, -1).toLowerCase(); } const globalProps = new _Object(); const wKeys = _Object.getOwnPropertyNames(unsafeWindow); const iKeys = _Object.getOwnPropertyNames(iWindow); const numRE = new _RegExp(/^\d+$/); for (let key of wKeys) { if (key != SEARCH_FUNCTION_NAME && !numRE.test(key) && !iKeys.includes(key)) { const type = getType(unsafeWindow[key]); globalProps[type] = globalProps[type] || new _Array(); globalProps[type].push(key); } } _console.log(`${tag} 全局属性:\n%o`, globalProps); const allKeys = new _Map(); function addKey(key, path) { const arr = allKeys.get(key) || new _Array(); arr.push(path); allKeys.set(key, arr); } const ignoreProps = new _Array('length', 'arguments', 'caller', 'prototype'); const references = new _Array(unsafeWindow); //存储已收集过的引用以提高效率 function getAllKeys(item, path, depth = 1) { if (MAX_DEPTH !== 0 && depth >= MAX_DEPTH) return; if (item === undefined || item === null || references.includes(item)) return; references.push(item); const props = _Object.getOwnPropertyNames(item); const keys = props.filter(prop => !ignoreProps.includes(prop)); for (let key of keys) { const curPath = `${path}['${key}']`; addKey(key, curPath); let value; const descriptor = _Object.getOwnPropertyDescriptor(item, key); if (descriptor && 'get' in descriptor) { //减少try catch次数以提高性能 try { _Promise.resolve(value = item[key]).catch(e => {}); } catch(e) {} if (value instanceof Promise) continue; } else { value = item[key]; } const type = typeof value; if (type == 'object' || type == 'function') { getAllKeys(value, curPath, depth + 1); } } } for (let type in globalProps) { for (let key of globalProps[type]) { const path = `window['${key}']`; addKey(key, path); if (typeof unsafeWindow[key] == 'object' || typeof unsafeWindow[key] == 'function') { getAllKeys(unsafeWindow[key], path); } } } unsafeWindow[SEARCH_FUNCTION_NAME] = key => _console.dir(allKeys.get(key) || []); _console.log(`${SEARCH_FUNCTION_NAME}函数已注入!`); });