// ==UserScript== // @name 查看全局属性 // @description 一键查看挂载到window上的非原生属性,并注入一个。 // @namespace cxxjackie // @author cxxjackie // @version 1.2.0 // @include * // @grant unsafeWindow // @grant GM_registerMenuCommand // @run-at document-idle // @homepage https://bbs.tampermonkey.net.cn/thread-916-1-1.html // @supportURL https://bbs.tampermonkey.net.cn/thread-916-1-1.html // ==/UserScript== 'use strict'; const MAX_LENGTH = 5e5; //最大收集数,超过此数字停止收集并提醒改小搜索深度,0不设限 let 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; const _prompt = iWindow.prompt; 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); function seekAllKeys() { const allKeys = new _Map(); let keyLength = 0; function addKey(key, path) { const arr = allKeys.get(key) || new _Array(); arr.push(path); allKeys.set(key, arr); keyLength++; } 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 (MAX_LENGTH > 0 && keyLength > MAX_LENGTH) 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; try { value = item[key]; } catch (e) { continue; } if (value instanceof _Promise) { value.catch(e => {}); continue; } 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); } } } if (MAX_LENGTH > 0 && keyLength > MAX_LENGTH) { const maxDepth = +_prompt(`超过最大收集数,是否将最大搜索深度改小重试(当前${MAX_DEPTH}):`); if (maxDepth > 0) { MAX_DEPTH = maxDepth; return seekAllKeys(); } } return allKeys; } const allKeys = seekAllKeys(); unsafeWindow[SEARCH_FUNCTION_NAME] = (key, fuzzy) => { if (fuzzy) { const result = new _Array(); for (const item of allKeys.keys()) { if (item.toLowerCase().includes(key.toLowerCase())) { result.push(...allKeys.get(item)); } } _console.log(result); } else { _console.log(allKeys.get(key) || []); } }; _console.log(`${SEARCH_FUNCTION_NAME}函数已注入!`); });