// ==UserScript== // @name 智能查寝 (全平台通用版) // @namespace http://tampermonkey.net/ // @version 11.4 // @description 解决 Stay 多重 iframe 影分身 Bug + 原生级 UI // @author Security Researcher // @license MIT // @match *://xsgz.gdcvi.edu.cn/* // @match *://cas.gdcvi.edu.cn/lyuapServer/login* // @match *://*.map.qq.com/* // @match *://*.mapapi.qq.com/* // @match *://*.lbs.qq.com/* // @match *://*.3gimg.qq.com/* // @run-at document-start // @grant none // ==/UserScript== (function() { const corePayload = function() { if(window.__CYBER_HOOK_ACTIVE__) return; window.__CYBER_HOOK_ACTIVE__ = true; const D_LAT = 23.738999; const D_LNG = 113.0890105; let F_LAT = D_LAT; let F_LNG = D_LNG; try { const savedLat = localStorage.getItem('gdcvi_fake_lat'); const savedLng = localStorage.getItem('gdcvi_fake_lng'); if (savedLat) F_LAT = parseFloat(savedLat); if (savedLng) F_LNG = parseFloat(savedLng); } catch(e) {} const PI = 3.1415926535897932384626; const a = 6378245.0; const ee = 0.00669342162296594323; const transformLat = (x, y) => { let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0; return ret; }; const transformLng = (x, y) => { let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0; return ret; }; const gcj02towgs84 = (lng, lat) => { let dlat = transformLat(lng - 105.0, lat - 35.0); let dlng = transformLng(lng - 105.0, lat - 35.0); let radlat = lat / 180.0 * PI; let magic = Math.sin(radlat); magic = 1 - ee * magic * magic; let sqrtmagic = Math.sqrt(magic); dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); return [lng * 2 - (lng + dlng), lat * 2 - (lat + dlat)]; }; const getJitter = () => (Math.random() - 0.5) * 0.00004; const getFakeData = () => ({ lat: F_LAT + getJitter(), lng: F_LNG + getJitter(), accuracy: 10 + Math.random() * 5 }); const origToString = Function.prototype.toString; Object.defineProperty(Function.prototype, 'toString', { value: new Proxy(origToString, { apply(target, thisArg, args) { if (thisArg && thisArg.__cyber_name) return 'function ' + thisArg.__cyber_name + '() { [native code] }'; return Reflect.apply(target, thisArg, args); } }), configurable: true, writable: true }); try { const origIframeSrcDesc = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'src'); if (origIframeSrcDesc) { const newSet = new Proxy(origIframeSrcDesc.set, { apply(target, thisArg, args) { if (args[0] && typeof args[0] === 'string' && args[0].includes('geolocation')) args[0] = 'about:blank'; return Reflect.apply(target, thisArg, args); } }); newSet.__cyber_name = 'set src'; Object.defineProperty(HTMLIFrameElement.prototype, 'src', { set: newSet, get: origIframeSrcDesc.get, configurable: origIframeSrcDesc.configurable, enumerable: origIframeSrcDesc.enumerable }); } } catch(e) {} const observer = new MutationObserver(mutations => { mutations.forEach(m => m.addedNodes.forEach(node => { if (node.tagName === 'IFRAME' && node.src && node.src.includes('geolocation')) node.src = 'about:blank'; })); }); if(document.documentElement) observer.observe(document.documentElement, { childList: true, subtree: true }); const hookW3C = (origMethod, name) => { if (!origMethod) return origMethod; const proxy = new Proxy(origMethod, { apply(target, thisArg, args) { const suc = args[0]; if (typeof suc === 'function') { const fd = getFakeData(); const [wgsLng, wgsLat] = gcj02towgs84(fd.lng, fd.lat); setTimeout(() => suc({coords:{latitude: wgsLat, longitude: wgsLng, accuracy: fd.accuracy}, timestamp:Date.now()}), 50); } return name === 'watchPosition' ? Math.floor(Math.random() * 10000) : undefined; } }); proxy.__cyber_name = name; return proxy; }; if (window.Geolocation && Geolocation.prototype) { Geolocation.prototype.getCurrentPosition = hookW3C(Geolocation.prototype.getCurrentPosition, 'getCurrentPosition'); Geolocation.prototype.watchPosition = hookW3C(Geolocation.prototype.watchPosition, 'watchPosition'); } else if (navigator.geolocation) { navigator.geolocation.getCurrentPosition = hookW3C(navigator.geolocation.getCurrentPosition, 'getCurrentPosition'); } const proxyCache = new WeakSet(); const hookTencentGeo = (OrigGeo) => { if (proxyCache.has(OrigGeo)) return OrigGeo; const HookedGeo = new Proxy(OrigGeo, { construct(target, args) { const instance = Reflect.construct(target, args); if (proxyCache.has(instance)) return instance; const InstanceProxy = new Proxy(instance, { get(target2, prop) { if (prop === 'getLocation' || prop === 'getIpLocation') { const fakeMethod = function(suc) { if (suc) { const fd = getFakeData(); setTimeout(() => suc({ module: 'geolocation', type: 'h5', lat: fd.lat, lng: fd.lng, accuracy: fd.accuracy, nation: '中国', province: '广东省', city: '清远市', adcode: '441802', __cyber_fake__: true }), 50); } }; fakeMethod.__cyber_name = prop; return fakeMethod; } const val = Reflect.get(target2, prop); return typeof val === 'function' ? val.bind(target2) : val; } }); proxyCache.add(InstanceProxy); return InstanceProxy; } }); proxyCache.add(HookedGeo); return HookedGeo; }; let _qq = window.qq; Object.defineProperty(window, 'qq', { get() { if (_qq && !proxyCache.has(_qq)) { _qq = new Proxy(_qq, { get(target, prop) { if (prop === 'maps') { const maps = Reflect.get(target, prop); if (maps && !proxyCache.has(maps)) { const mapsProxy = new Proxy(maps, { get(target2, prop2) { return prop2 === 'Geolocation' ? hookTencentGeo(Reflect.get(target2, prop2)) : Reflect.get(target2, prop2); } }); proxyCache.add(mapsProxy); return mapsProxy; } } return Reflect.get(target, prop); } }); proxyCache.add(_qq); } return _qq; }, set(v) { _qq = v; }, configurable: true }); const origAddEvent = window.addEventListener; window.addEventListener = new Proxy(origAddEvent, { apply(target, thisArg, args) { if (args[0] === 'message' && typeof args[1] === 'function') { const handler = args[1]; args[1] = function(e) { if (e.data && e.data.module === 'geolocation' && !e.data.__cyber_fake__) return; return handler.apply(this, arguments); }; } return Reflect.apply(target, thisArg, args); } }); window.addEventListener.__cyber_name = 'addEventListener'; setInterval(() => { const fd = getFakeData(); window.postMessage({ module: 'geolocation', type: 'geolocation', lat: fd.lat, lng: fd.lng, accuracy: fd.accuracy, nation: '中国', province: '广东省', city: '清远市', adcode: '441802', __cyber_fake__: true }, '*'); }, 50); // ========================================== // 🛡️ 智能 UI 渲染探针 (终结 iframe 影分身) // ========================================== const shouldRenderUI = () => { try { // 1. 过滤第三方地图 iframe const h = window.location.hostname || ''; if (h.includes('qq.com') || h.includes('tencent') || h.includes('map')) return false; // 2. 过滤体积过小的暗桩 iframe(如专门发请求的不可见层) if (window.innerWidth < 50 || window.innerHeight < 50) return false; // 3. 同源霸体锁:防止嵌套的主页面和子页面同时渲染 UI if (window.parent !== window && window.parent.document) { // 如果存在同源父页面,将渲染权让渡给父页面 return false; } } catch(e) {} // 跨域时自动捕获异常,允许渲染 return true; }; const createInjectUI = () => { if (!shouldRenderUI()) return; if (document.getElementById('gdcvi-stay-host')) return; if (!document.body && !document.documentElement) return; const root = document.documentElement || document.body; const host = document.createElement('div'); host.id = 'gdcvi-stay-host'; host.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:2147483647;'; root.appendChild(host); const shadow = host.attachShadow({ mode: 'closed' }); const style = document.createElement('style'); style.textContent = ` * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Helvetica, Arial, sans-serif; } #fab { position: absolute; top: 100px; right: 20px; width: 52px; height: 52px; background: rgba(255, 255, 255, 0.85); border-radius: 50%; box-shadow: 0 4px 16px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,1); pointer-events: auto; display: flex; justify-content: center; align-items: center; font-size: 24px; cursor: pointer; user-select: none; backdrop-filter: blur(12px) saturate(180%); transition: transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 1000; border: 0.5px solid rgba(0,0,0,0.05); } #fab:active { transform: scale(0.88); } #overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.4); opacity: 0; pointer-events: none; transition: opacity 0.35s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 998; backdrop-filter: blur(4px); } #overlay.active { opacity: 1; pointer-events: auto; } #bottom-sheet { position: absolute; bottom: -100%; left: 0; width: 100%; background: rgba(245, 245, 247, 0.95); border-top-left-radius: 28px; border-top-right-radius: 28px; padding: 24px 20px 32px; box-shadow: 0 -10px 40px rgba(0,0,0,0.15); pointer-events: auto; transition: bottom 0.4s cubic-bezier(0.32, 0.72, 0, 1); z-index: 999; backdrop-filter: blur(25px) saturate(200%); touch-action: pan-y; border-top: 1px solid rgba(255,255,255,0.7); } #bottom-sheet.active { bottom: 0; } .sheet-handle { width: 42px; height: 5px; background: rgba(0, 0, 0, 0.15); border-radius: 3px; margin: 0 auto 20px; } .header-bar { position: relative; margin-bottom: 24px; display: flex; align-items: center; justify-content: center; } .sheet-title { font-weight: 700; font-size: 17px; color: #1d1d1f; letter-spacing: -0.4px; } #btn-close { position: absolute; right: 0; width: 28px; height: 28px; background: rgba(0,0,0,0.06); border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 16px; color: #1d1d1f; cursor: pointer; transition: background 0.2s; } #btn-close:active { background: rgba(0,0,0,0.12); } .input-group { display: flex; gap: 14px; margin-bottom: 24px; } .input-box { flex: 1; display: flex; flex-direction: column; gap: 8px; } .input-box label { font-size: 13px; color: #86868b; font-weight: 600; letter-spacing: -0.2px; } .input-box input { width: 100%; padding: 14px 12px; border: 1.5px solid rgba(0,0,0,0.04); border-radius: 14px; font-size: 16px; outline: none; background: #ffffff; color: #1d1d1f; font-weight: 600; font-family: ui-monospace, SFMono-Regular, monospace; box-shadow: inset 0 2px 4px rgba(0,0,0,0.02); transition: all 0.25s ease; } .input-box input:focus { border-color: #34c759; background: #ffffff; box-shadow: 0 0 0 4px rgba(52, 199, 89, 0.15), inset 0 2px 4px rgba(0,0,0,0.01); } .btn-group { display: flex; gap: 14px; } button { flex: 1; padding: 16px; border: none; border-radius: 16px; font-weight: 600; font-size: 16px; cursor: pointer; transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); letter-spacing: -0.2px; } button:active { transform: scale(0.96); } #btn-save { background: linear-gradient(135deg, #32d74b, #28a745); color: #ffffff; box-shadow: 0 6px 16px rgba(40, 167, 69, 0.25); text-shadow: 0 1px 1px rgba(0,0,0,0.1); } #btn-reset { background: #e5e5ea; color: #8e8e93; } #btn-reset:active { background: #d1d1d6; } #toast { position: absolute; top: -50px; left: 50%; transform: translateX(-50%); padding: 14px 28px; border-radius: 40px; color: white; font-size: 15px; font-weight: 600; opacity: 0; pointer-events: none; transition: all 0.4s cubic-bezier(0.32, 0.72, 0, 1); box-shadow: 0 8px 24px rgba(0,0,0,0.15); white-space: nowrap; z-index: 2000; backdrop-filter: blur(10px); } .coffee-area { margin-top: 24px; text-align: center; } .coffee-link { font-size: 13px; color: #86868b; text-decoration: none; border-bottom: 1px solid rgba(134, 134, 139, 0.3); padding-bottom: 2px; transition: color 0.2s; } .coffee-link:active { color: #1d1d1f; } .coffee-qr-box { display: none; margin-top: 16px; transition: all 0.3s ease; } .coffee-qr-box img { width: 140px; height: 140px; border-radius: 12px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); } .coffee-tips { font-size: 12px; color: #999; margin-top: 8px; font-weight: 500; } @keyframes popIn { 0% { transform: translate(-50%, -40%) scale(0.9); opacity: 0; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 1; } } `; shadow.appendChild(style); const overlay = document.createElement('div'); overlay.id = 'overlay'; shadow.appendChild(overlay); const fab = document.createElement('div'); fab.id = 'fab'; fab.innerHTML = ``; shadow.appendChild(fab); const sheet = document.createElement('div'); sheet.id = 'bottom-sheet'; sheet.innerHTML = `