// ==UserScript== // @name 广建智能查寝 // @namespace http://tampermonkey.net/ // @version 9.8 // @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 hookMethod = (originalMethod, 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); 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 }); } } 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); return new Proxy(instance, { get(insTarget, insProp) { if (insProp === 'getLocation' || insProp === 'getIpLocation') { 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); 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 }); 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; } #fab { position: absolute; top: 100px; right: 20px; width: 50px; height: 50px; background: rgba(40, 167, 69, 0.9); border-radius: 50%; box-shadow: 0 4px 15px rgba(0,0,0,0.3); pointer-events: auto; display: flex; justify-content: center; align-items: center; font-size: 24px; color: white; cursor: pointer; user-select: none; backdrop-filter: blur(5px); transition: transform 0.1s; z-index: 1000; } #fab:active { transform: scale(0.9); } #overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); opacity: 0; pointer-events: none; transition: opacity 0.3s ease; z-index: 998; backdrop-filter: blur(2px); } #overlay.active { opacity: 1; pointer-events: auto; } #bottom-sheet { position: absolute; bottom: -100%; left: 0; width: 100%; background: #fff; border-top-left-radius: 20px; border-top-right-radius: 20px; padding: 20px; box-shadow: 0 -4px 20px rgba(0,0,0,0.15); pointer-events: auto; transition: bottom 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); z-index: 999; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } #bottom-sheet.active { bottom: 0; } .sheet-handle { width: 40px; height: 5px; background: #ddd; border-radius: 3px; margin: 0 auto 15px; } .sheet-title { text-align: center; font-weight: 600; font-size: 16px; margin-bottom: 20px; color: #333; } .input-group { display: flex; gap: 12px; margin-bottom: 20px; } .input-box { flex: 1; display: flex; flex-direction: column; gap: 5px; } .input-box label { font-size: 12px; color: #666; font-weight: 500; } .input-box input { width: 100%; padding: 12px; border: 1px solid #e0e0e0; border-radius: 10px; font-size: 15px; outline: none; background: #f8f9fa; transition: border 0.2s; } .input-box input:focus { border-color: #28a745; background: #fff; } .btn-group { display: flex; gap: 12px; } button { flex: 1; padding: 14px; border: none; border-radius: 12px; font-weight: 600; font-size: 15px; cursor: pointer; color: white; transition: opacity 0.2s; } button:active { opacity: 0.8; } #btn-save { background: linear-gradient(135deg, #28a745, #20c997); box-shadow: 0 4px 10px rgba(40,167,69,0.3); } #btn-reset { background: #6c757d; } #toast { position: absolute; top: 40px; left: 50%; transform: translateX(-50%); padding: 12px 24px; border-radius: 30px; color: white; font-size: 14px; font-weight: 600; opacity: 0; pointer-events: none; transition: opacity 0.3s ease, top 0.3s ease; box-shadow: 0 4px 15px rgba(0,0,0,0.2); white-space: nowrap; z-index: 2000; } .coffee-area { margin-top: 15px; text-align: center; } .coffee-link { font-size: 12px; color: #6c757d; text-decoration: none; border-bottom: 1px dashed #ccc; padding-bottom: 2px; } .coffee-qr-box { display: none; margin-top: 12px; transition: all 0.3s ease; } .coffee-qr-box img { width: 130px; height: 130px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .coffee-tips { font-size: 11px; color: #999; margin-top: 6px; } `; shadow.appendChild(style); const overlay = document.createElement('div'); overlay.id = 'overlay'; shadow.appendChild(overlay); const fab = document.createElement('div'); fab.id = 'fab'; fab.innerText = '📍'; shadow.appendChild(fab); const sheet = document.createElement('div'); sheet.id = 'bottom-sheet'; sheet.innerHTML = `