// ==UserScript== // @name 广建智能查寝 // @namespace http://tampermonkey.net/ // @version 10.7 // @description 全链路 Proxy 隐蔽劫持 + 腾讯地图 API 拦截 + 可自由拖拽悬浮窗 + 动态抖动防检测 // @author Security Researcher // @license MIT // @match *://xsgz.gdcvi.edu.cn/* // @match *://*.gdcvi.edu.cn/* // @match *://*.map.qq.com/* // @match *://*.mapapi.qq.com/* // @match *://*.lbs.qq.com/* // @match *://*.3gimg.qq.com/* // @grant GM_getValue // @grant GM_setValue // @run-at document-start // ==/UserScript== (function() { 'use strict'; const DEFAULT_LAT = 23.736924; const DEFAULT_LNG = 113.087081; let FAKE_LAT = GM_getValue('fake_lat', DEFAULT_LAT); let FAKE_LNG = GM_getValue('fake_lng', DEFAULT_LNG); const getBaseCoords = () => { const jitter = () => (Math.random() - 0.5) * 0.00008; return { lat: FAKE_LAT + jitter(), lng: FAKE_LNG + jitter(), acc: Math.floor(15 + Math.random() * 12) }; }; const getW3CPosition = () => { const c = getBaseCoords(); return { coords: { latitude: c.lat, longitude: c.lng, accuracy: c.acc, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; }; const getTencentPosition = () => { const c = getBaseCoords(); return { module: 'geolocation', type: 'h5', lat: c.lat, lng: c.lng, accuracy: c.acc, adcode: '', nation: '中国' }; }; try { localStorage.clear(); sessionStorage.clear(); } catch (e) {} const nativeFunctions = new WeakSet(); const originalToString = Function.prototype.toString; Function.prototype.toString = new Proxy(originalToString, { apply(target, thisArg, args) { if (nativeFunctions.has(thisArg)) { return `function ${thisArg.name || ''}() { [native code] }`; } return Reflect.apply(target, thisArg, args); } }); nativeFunctions.add(Function.prototype.toString); if (navigator.geolocation) { const cachedMethods = {}; const hookMethod = (originalMethod, name) => { if (cachedMethods[name]) return cachedMethods[name]; const proxy = new Proxy(originalMethod, { apply(target, thisArg, args) { const successCallback = args[0]; const pos = getW3CPosition(); if (typeof successCallback === 'function') { setTimeout(() => successCallback(pos), 400 + Math.random() * 200); } if (name === 'watchPosition') return Math.floor(Math.random() * 100000); } }); nativeFunctions.add(proxy); cachedMethods[name] = proxy; return proxy; }; const geoProxy = new Proxy(navigator.geolocation, { get(target, prop, receiver) { if (prop === 'getCurrentPosition') return hookMethod(target[prop], 'getCurrentPosition'); if (prop === 'watchPosition') return hookMethod(target[prop], 'watchPosition'); if (prop === 'clearWatch') return function() {}; return Reflect.get(target, prop, receiver); } }); const originalDescriptor = Object.getOwnPropertyDescriptor(Navigator.prototype, 'geolocation'); if (originalDescriptor && originalDescriptor.get) { const hookedGetter = new Proxy(originalDescriptor.get, { apply() { return geoProxy; } }); nativeFunctions.add(hookedGetter); Object.defineProperty(Navigator.prototype, 'geolocation', { get: hookedGetter, set: originalDescriptor.set, enumerable: originalDescriptor.enumerable, configurable: false }); } else { Object.defineProperty(navigator, 'geolocation', { get: function() { return geoProxy; }, enumerable: true, configurable: false }); } if (window.Geolocation && window.Geolocation.prototype) { const proto = window.Geolocation.prototype; const origGetCurrent = proto.getCurrentPosition; if (origGetCurrent) { Object.defineProperty(proto, 'getCurrentPosition', { value: hookMethod(origGetCurrent, 'getCurrentPosition'), writable: false, configurable: false }); } const origWatch = proto.watchPosition; if (origWatch) { Object.defineProperty(proto, 'watchPosition', { value: hookMethod(origWatch, 'watchPosition'), writable: false, configurable: false }); } } } const hookedTencent = new WeakSet(); const createGeoProxy = (obj) => { if (!obj || typeof obj !== 'function' || hookedTencent.has(obj)) return obj; hookedTencent.add(obj); const proxy = new Proxy(obj, { construct(target, args) { const instance = Reflect.construct(target, args); const cachedInsMethods = {}; return new Proxy(instance, { get(insTarget, insProp) { if (insProp === 'getLocation' || insProp === 'getIpLocation') { if (cachedInsMethods[insProp]) return cachedInsMethods[insProp]; const methodProxy = new Proxy(insTarget[insProp], { apply(fnTarget, thisArg, fnArgs) { const successCallback = fnArgs[0]; const pos = getTencentPosition(); if (typeof successCallback === 'function') { setTimeout(() => successCallback(pos), 350 + Math.random() * 150); } } }); nativeFunctions.add(methodProxy); cachedInsMethods[insProp] = methodProxy; return methodProxy; } return Reflect.get(insTarget, insProp); } }); } }); nativeFunctions.add(proxy); return proxy; }; const createMapsProxy = (obj) => { if (!obj || typeof obj !== 'object' || hookedTencent.has(obj)) return obj; hookedTencent.add(obj); return new Proxy(obj, { get(target, prop) { const val = Reflect.get(target, prop); if (prop === 'Geolocation') return createGeoProxy(val); return val; }, set(target, prop, value) { if (prop === 'Geolocation') value = createGeoProxy(value); return Reflect.set(target, prop, value); } }); }; const createQQProxy = (obj) => { if (!obj || typeof obj !== 'object' || hookedTencent.has(obj)) return obj; hookedTencent.add(obj); return new Proxy(obj, { get(target, prop) { const val = Reflect.get(target, prop); if (prop === 'maps') return createMapsProxy(val); return val; }, set(target, prop, value) { if (prop === 'maps') value = createMapsProxy(value); return Reflect.set(target, prop, value); } }); }; let _qq = window.qq; if (_qq) _qq = createQQProxy(_qq); Object.defineProperty(window, 'qq', { get() { return _qq; }, set(val) { _qq = createQQProxy(val); }, configurable: true, enumerable: true }); // ========================================== // 🎨 UI 注入区域 (v10.7 质感重塑版) // ========================================== function createInjectUI() { if (window !== window.top) return; const hostId = 'gdcvi-host-' + Math.random().toString(36).substring(2); const host = document.createElement('div'); host.id = hostId; host.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483647;'; document.documentElement.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; } /* 底部抽屉:iOS 毛玻璃面板 */ #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 提示:药丸形状与平滑滑出 */ #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; } `; shadow.appendChild(style); const overlay = document.createElement('div'); overlay.id = 'overlay'; shadow.appendChild(overlay); const fab = document.createElement('div'); fab.id = 'fab'; // 使用更高清的定位 icon,如果显示异常可改回文字 '📍' fab.innerHTML = ``; shadow.appendChild(fab); const sheet = document.createElement('div'); sheet.id = 'bottom-sheet'; sheet.innerHTML = `