(function () { 'use strict'; const ACCENT = '#00f0c8'; const ACCENT2 = '#58a6ff'; const MUTED = '#8b9aad'; const DANGER = '#ff4757'; const OK = '#3fb950'; const $ = (id) => document.getElementById(id); function isPrivateIP(ip) { if (!ip) return true; if (ip.startsWith('0.') || ip.startsWith('127.') || ip === '::1') return true; if (ip.startsWith('10.') || ip.startsWith('192.168.')) return true; if (ip.startsWith('172.')) { const s = parseInt(ip.split('.')[1], 10); if (s >= 16 && s <= 31) return true; } if (ip.startsWith('169.254.')) return true; if (ip.startsWith('fc') || ip.startsWith('fd')) return true; if (ip === '::ffff:127.0.0.1') return true; return false; } function isValidIP(ip) { if (!ip || typeof ip !== 'string') return false; const ipv4 = /^(?:(?:25[0-5]|2[0-4]\d|1?\d{1,2})\.){3}(?:25[0-5]|2[0-4]\d|1?\d{1,2})$/; if (ipv4.test(ip)) return !isPrivateIP(ip); const ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/; if (ipv6.test(ip)) return !isPrivateIP(ip); return false; } function extractIP(candidate) { if (!candidate) return null; const parts = candidate.split(' '); if (parts.length < 5) return null; const typIdx = parts.findIndex(p => p === 'typ'); if (typIdx === -1) return null; const typ = parts[typIdx + 1]; if (typ !== 'srflx' && typ !== 'prflx') return null; return isValidIP(parts[4]) ? parts[4] : null; } function esc(str) { if (str == null) return ''; return String(str).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); } function flagEmoji(cc) { if (!cc) return ''; try { return String.fromCodePoint(...[...cc.toUpperCase()].map(c => 0x1F1E6 + c.charCodeAt(0) - 65)); } catch { return ''; } } function fmtOffset(off) { if (off == null) return '—'; if (typeof off === 'string') { const m = off.match(/([+-]?)\d{1,2}:\d{2}(?::\d{2})?/); if (m) { const sign = off.includes('-') ? -1 : 1; const h = parseInt(m[0].split(':')[0].replace('+','').replace('-',''), 10); const mn = parseInt(m[0].split(':')[1], 10); const s = m[0].split(':')[2] ? parseInt(m[0].split(':')[2], 10) : 0; const totalSec = sign * (h * 3600 + mn * 60 + s); const h2 = Math.floor(Math.abs(totalSec) / 3600); const m2 = Math.floor((Math.abs(totalSec) % 3600) / 60); const sign2 = totalSec >= 0 ? '+' : '-'; return `${sign2}${h2.toString().padStart(2,'0')}:${m2.toString().padStart(2,'0')}`; } const n = Number(off.replace(/[^0-9.-]/g, '')); if (!isNaN(n)) off = n; else return '—'; } let s = Number(off); if (isNaN(s)) return '—'; if (Math.abs(s) < 20) s = s * 3600; const h = Math.floor(Math.abs(s) / 3600); const m = Math.floor((Math.abs(s) % 3600) / 60); const sign = s >= 0 ? '+' : '-'; return `${sign}${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}`; } function mergeData(results) { const merged = {}; for (const r of results) { if (!r) continue; for (const key of Object.keys(r)) { if (r[key] != null && r[key] !== '' && r[key] !== undefined) { if (merged[key] == null || merged[key] === '') { if (key === 'currency' && typeof r[key] === 'object' && r[key].code) merged[key] = r[key].code; else merged[key] = r[key]; } } } } return merged; } async function fetchGeo(ip) { const apis = [ { url: `https://get.geojs.io/v1/ip/geo/${encodeURIComponent(ip)}.json`, name: 'geojs' }, { url: `https://free.freeipapi.com/api/json/${encodeURIComponent(ip)}`, name: 'freeipapi' }, { url: `https://api.ipapi.is?q=${encodeURIComponent(ip)}`, name: 'ipapiis' } ]; let lastErr = ''; const promises = apis.map(async api => { try { const res = await fetch(api.url, { cache: 'no-store' }); if (!res.ok) { lastErr = `${api.name} HTTP ${res.status}`; return null; } const data = await res.json(); if (!data) { lastErr = `${api.name} empty`; return null; } if (data.success === false || data.error === true) { lastErr = `${api.name} error`; return null; } if (!data.ip && !data.query && !data.ipAddress) { lastErr = `${api.name} no ip`; return null; } return data; } catch (e) { lastErr = `${api.name} ${e.message}`; return null; } }); const results = (await Promise.all(promises)).filter(Boolean); if (!results.length) throw new Error(lastErr || 'All geo endpoints failed'); return mergeData(results); } function countryName(cc) { if (!cc) return ''; try { return new Intl.DisplayNames(['en'], { type: 'region' }).of(cc.toUpperCase()); } catch { return cc; } } const seenIPs = new Set(); let lastIP = null; let currentData = null; let lastRenderedIP = null; let geoBusy = false; let geoQueue = null; let peerConnections = []; function injectStyles() { if (document.getElementById('gc-styles')) return; if (!document.getElementById('gc-fonts')) { const link = document.createElement('link'); link.id = 'gc-fonts'; link.rel = 'stylesheet'; link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap'; document.head.appendChild(link); } const s = document.createElement('style'); s.id = 'gc-styles'; s.textContent = ` #gc-box{position:fixed;top:14px;right:14px;width:320px;background:rgba(11,15,20,.9);border:1px solid rgba(27,40,56,.6);color:#c8d6e0;border-radius:14px;padding:0;font-family:'Inter',system-ui,sans-serif;font-size:12px;z-index:2147483647;box-shadow:0 20px 60px rgba(0,0,0,.6),0 0 0 1px rgba(0,240,200,.06) inset;backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);line-height:1.5;overflow:hidden;} #gc-hdr{display:flex;justify-content:space-between;align-items:center;padding:10px 12px;border-bottom:1px solid rgba(27,40,56,.5);cursor:move;user-select:none;background:rgba(0,0,0,.15);} #gc-hdr .gc-logo{display:flex;align-items:center;gap:8px;} #gc-hdr .gc-icon{width:22px;height:22px;border-radius:5px;flex-shrink:0;animation:gcPulse 3s infinite;} #gc-hdr .gc-title{color:${ACCENT};font-size:12px;font-weight:700;} #gc-hdr .gc-ver{font-size:9px;color:${MUTED};background:rgba(0,240,200,.08);padding:1px 5px;border-radius:4px;margin-left:4px;} #gc-hdr .gc-close{cursor:pointer;color:${MUTED};font-size:18px;line-height:1;padding:0 4px;transition:color .2s;} #gc-hdr .gc-close:hover{color:${DANGER};} #gc-body{padding:12px;} .gc-row{display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid rgba(27,40,56,.35);} .gc-row:last-child{border-bottom:none;} .gc-lbl{font-size:10px;color:${MUTED};text-transform:uppercase;letter-spacing:.04em;} .gc-val{font-size:12px;color:#fff;text-align:right;word-break:break-word;max-width:65%;font-family:'JetBrains Mono',monospace;} .gc-top{margin-bottom:10px;text-align:center;} .gc-top .gc-big-ip{font-family:'JetBrains Mono',monospace;font-size:20px;color:#fff;font-weight:600;word-break:break-all;} .gc-top .gc-sub{display:flex;align-items:center;justify-content:center;gap:5px;margin-top:4px;flex-wrap:wrap;} .gc-top .gc-flag{font-size:18px;} .gc-top .gc-loc{color:${MUTED};font-size:11px;} .gc-badge{display:inline-flex;align-items:center;font-size:9px;font-weight:700;padding:2px 6px;border-radius:5px;border:1px solid;text-transform:uppercase;letter-spacing:.04em;} .gc-badge.cc{background:rgba(0,240,200,.08);border-color:rgba(0,240,200,.25);color:${ACCENT};} .gc-badge.clean{background:rgba(63,185,80,.08);border-color:rgba(63,185,80,.25);color:${OK};} .gc-badge.warn{background:rgba(255,159,67,.08);border-color:rgba(255,159,67,.25);color:#ff9f43;} .gc-badge.danger{background:rgba(255,71,87,.08);border-color:rgba(255,71,87,.25);color:${DANGER};} .gc-actions{display:flex;gap:6px;margin-top:10px;} .gc-actions button{flex:1;background:rgba(14,20,27,.6);border:1px solid rgba(27,40,56,.5);color:#c8d6e0;padding:7px 0;border-radius:8px;font-size:11px;font-weight:600;cursor:pointer;text-align:center;transition:all .2s ease;} .gc-actions button:hover{border-color:rgba(0,240,200,.3);color:${ACCENT};background:rgba(0,240,200,.04);} .gc-actions button.primary{background:linear-gradient(135deg,${ACCENT},${ACCENT2});color:#031411;border:none;font-weight:700;} .gc-actions button.primary:hover{filter:brightness(1.15);color:#031411;} .gc-wait{text-align:center;color:${MUTED};padding:20px 10px;font-size:11px;} @keyframes gcFadeIn{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:translateY(0);}} @keyframes gcPulse{0%{box-shadow:0 0 0 0 rgba(0,240,200,.35);}70%{box-shadow:0 0 0 8px rgba(0,240,200,0);}100%{box-shadow:0 0 0 0 rgba(0,240,200,0);}} `; document.head.appendChild(s); } function createUI() { if ($('gc-box')) return; injectStyles(); const box = document.createElement('div'); box.id = 'gc-box'; const saved = (() => { try { return JSON.parse(localStorage.getItem('gc_pos') || 'null'); } catch { return null; } })(); if (saved) { box.style.left = saved.l + 'px'; box.style.top = saved.t + 'px'; box.style.right = 'auto'; } box.innerHTML = `
0 peers ×
Waiting for peer connection...
`; document.body.appendChild(box); const hdr = $('gc-hdr'); let dragging = false, dx, dy; hdr.addEventListener('mousedown', e => { dragging = true; const r = box.getBoundingClientRect(); dx = e.clientX - r.left; dy = e.clientY - r.top; }); document.addEventListener('mousemove', e => { if (!dragging) return; box.style.left = (e.clientX - dx) + 'px'; box.style.top = (e.clientY - dy) + 'px'; box.style.right = 'auto'; }); document.addEventListener('mouseup', () => { if (dragging) { dragging = false; try { localStorage.setItem('gc_pos', JSON.stringify({l: parseInt(box.style.left), t: parseInt(box.style.top)})); } catch {} } }); $('gc-x').addEventListener('click', () => box.remove()); } function render(data) { if (!data || !data.ip) return; if (data.ip === lastRenderedIP && currentData) return; currentData = data; lastRenderedIP = data.ip; if (data.location && typeof data.location === 'object') { const loc = data.location; if (!data.city && loc.city) data.city = loc.city; if (!data.region && loc.state) data.region = loc.state; if (!data.state && loc.state) data.state = loc.state; if (!data.country && loc.country) data.country = loc.country; if (!data.country_code && loc.country_code) data.country_code = loc.country_code; if (!data.latitude && loc.latitude != null) data.latitude = loc.latitude; if (!data.longitude && loc.longitude != null) data.longitude = loc.longitude; if (!data.continent && loc.continent) data.continent = loc.continent; } const cc = esc((data.country_code || data.countryCode || data.country || '').toUpperCase()); const f = esc(flagEmoji(cc)); const cn = esc(countryName(cc) || data.country_name || data.country || 'Unknown'); const proxy = data.proxy ?? data.is_proxy ?? data.is_vpn ?? null; const hosting = data.hosting ?? data.is_datacenter ?? null; const tor = data.is_tor ?? null; let threat = ''; if (proxy === true) threat += 'Proxy/VPN'; if (hosting === true) threat += 'Hosting'; if (tor === true) threat += 'Tor'; if (!threat && (proxy === false || hosting === false)) threat = 'Clean'; const tzRaw = data.timezone ?? data.location?.timezone ?? data.time_zone?.name ?? data.time_zone?.id ?? data.timeZone ?? (Array.isArray(data.timeZones) ? data.timeZones[0] : null); const tz = esc((typeof tzRaw === 'string' ? tzRaw : tzRaw?.id ?? tzRaw?.name) || 'N/A'); const off = data.offset ?? data.timezone?.offset ?? data.time_zone?.offset ?? data.timezone_offset ?? data.timeZoneOffset ?? data.utc_offset ?? data.location?.utcoffset ?? null; const curRaw = data.currency ?? data.location?.currency_code ?? (Array.isArray(data.currencies) ? data.currencies[0] : null); const cur = esc((curRaw && typeof curRaw === 'object' ? curRaw.code : curRaw) || 'N/A'); const row = (l, v) => `
${esc(l)}${esc(v)}
`; $('gc-body').innerHTML = `
${esc(data.ip)}
${cc || '—'} ${threat} ${f} ${[data.city, data.region || data.regionName || data.state, cn].filter(Boolean).map(esc).join(', ')}
${row('ISP', data.organization_name || data.org || data.isp || 'N/A')} ${row('Timezone', tz)} ${row('UTC Offset', fmtOffset(off))} ${row('Currency', cur)} ${row('ASN', data.asn ? (String(data.asn).startsWith('AS') ? data.asn : 'AS' + data.asn) : data.as || 'N/A')}
`; $('gc-copy').addEventListener('click', async () => { try { await navigator.clipboard.writeText(data.ip); } catch (e) { const ta = document.createElement('textarea'); ta.value = data.ip; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } const btn = $('gc-copy'); btn.textContent = 'Copied'; setTimeout(() => btn.textContent = 'Copy IP', 1200); }); const lat = parseFloat(data.latitude ?? data.lat); const lon = parseFloat(data.longitude ?? data.lon); const hasCoords = !isNaN(lat) && !isNaN(lon); const mapBtn = $('gc-map'); if (hasCoords) { mapBtn.addEventListener('click', () => window.open(`https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}#map=14/${lat}/${lon}`, '_blank')); } else { mapBtn.disabled = true; mapBtn.style.opacity = '.4'; mapBtn.style.cursor = 'not-allowed'; } } async function geo(ip) { if (ip === lastRenderedIP && currentData) return; createUI(); if (geoBusy) { geoQueue = ip; return; } geoBusy = true; const body = $('gc-body'); if (body && ip !== lastRenderedIP) body.innerHTML = `
Found IP: ${esc(ip)}
Fetching geolocation...
`; try { const data = await fetchGeo(ip); render(data); } catch (e) { console.warn('[GeoChecker] Geo fetch failed:', e.message); if (body) body.innerHTML = '
Geo lookup failed. Retrying on next peer...
'; } geoBusy = false; if (geoQueue) { const next = geoQueue; geoQueue = null; geo(next); } } function checkCandidate(candidate, pc) { const ip = extractIP(candidate); if (!ip || seenIPs.has(ip)) return; seenIPs.add(ip); lastIP = ip; const cnt = $('gc-count'); if (cnt) cnt.textContent = seenIPs.size + ' peer' + (seenIPs.size > 1 ? 's' : ''); geo(ip); } function cleanupPeerConnections() { peerConnections = peerConnections.filter(pc => { const state = pc.connectionState || pc.iceConnectionState; return state !== 'closed' && state !== 'failed'; }); } function checkSDP(sdp, pc) { if (!sdp) return; for (const line of sdp.split(/\r?\n/)) { if (line.startsWith('a=candidate:')) checkCandidate(line, pc); } } function hookInstance(pc) { if (!pc || pc.__gc_hooked) return; pc.__gc_hooked = true; cleanupPeerConnections(); if (!peerConnections.includes(pc)) peerConnections.push(pc); const orig = pc.addIceCandidate; pc.addIceCandidate = function (candidate, ...rest) { try { if (candidate && candidate.candidate) checkCandidate(candidate.candidate, pc); } catch {} return orig.apply(this, arguments); }; const origLocal = pc.setLocalDescription; pc.setLocalDescription = function (desc, ...rest) { try { if (desc && desc.sdp) { checkSDP(desc.sdp, this); } } catch {} return origLocal.apply(this, arguments); }; const origRemote = pc.setRemoteDescription; pc.setRemoteDescription = function (desc, ...rest) { try { if (desc && desc.sdp) { checkSDP(desc.sdp, this); } } catch {} return origRemote.apply(this, arguments); }; } const GC_HOOK_VERSION = 3; function hook() { const NativeRTC = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection; if (!NativeRTC) return; if (!NativeRTC.prototype.__gc_hooked || NativeRTC.prototype.__gc_hooked < GC_HOOK_VERSION) { NativeRTC.prototype.__gc_hooked = GC_HOOK_VERSION; if (!NativeRTC.prototype.__gc_orig_addIceCandidate) NativeRTC.prototype.__gc_orig_addIceCandidate = NativeRTC.prototype.addIceCandidate; if (!NativeRTC.prototype.__gc_orig_setLocalDescription) NativeRTC.prototype.__gc_orig_setLocalDescription = NativeRTC.prototype.setLocalDescription; if (!NativeRTC.prototype.__gc_orig_setRemoteDescription) NativeRTC.prototype.__gc_orig_setRemoteDescription = NativeRTC.prototype.setRemoteDescription; if (!NativeRTC.prototype.__gc_orig_createOffer) NativeRTC.prototype.__gc_orig_createOffer = NativeRTC.prototype.createOffer; if (!NativeRTC.prototype.__gc_orig_createAnswer) NativeRTC.prototype.__gc_orig_createAnswer = NativeRTC.prototype.createAnswer; NativeRTC.prototype.addIceCandidate = function (candidate, ...rest) { try { if (candidate && candidate.candidate) checkCandidate(candidate.candidate, this); } catch {} return NativeRTC.prototype.__gc_orig_addIceCandidate.apply(this, arguments); }; NativeRTC.prototype.setLocalDescription = function (desc, ...rest) { try { if (desc && desc.sdp) checkSDP(desc.sdp, this); } catch {} return NativeRTC.prototype.__gc_orig_setLocalDescription.apply(this, arguments); }; NativeRTC.prototype.setRemoteDescription = function (desc, ...rest) { try { if (desc && desc.sdp) checkSDP(desc.sdp, this); } catch {} return NativeRTC.prototype.__gc_orig_setRemoteDescription.apply(this, arguments); }; NativeRTC.prototype.createOffer = function (...args) { try { if (!peerConnections.includes(this)) { cleanupPeerConnections(); peerConnections.push(this); } } catch {} return NativeRTC.prototype.__gc_orig_createOffer.apply(this, args); }; NativeRTC.prototype.createAnswer = function (...args) { try { if (!peerConnections.includes(this)) { cleanupPeerConnections(); peerConnections.push(this); } } catch {} return NativeRTC.prototype.__gc_orig_createAnswer.apply(this, args); }; } if (!window.RTCPeerConnection || !window.RTCPeerConnection.__gc_ctor_hooked || window.RTCPeerConnection.__gc_ctor_hooked < GC_HOOK_VERSION) { try { const origCtor = NativeRTC; const Wrapped = function (...args) { const pc = (typeof Reflect !== 'undefined' && Reflect.construct) ? Reflect.construct(origCtor, args, new.target || Wrapped) : new origCtor(...args); hookInstance(pc); return pc; }; Wrapped.prototype = origCtor.prototype; Object.setPrototypeOf(Wrapped, origCtor); for (const k of Object.getOwnPropertyNames(origCtor)) { if (!Wrapped.hasOwnProperty(k)) try { Wrapped[k] = origCtor[k]; } catch {} } Wrapped.__gc_ctor_hooked = GC_HOOK_VERSION; window.RTCPeerConnection = Wrapped; if (window.webkitRTCPeerConnection && window.webkitRTCPeerConnection !== Wrapped) window.webkitRTCPeerConnection = Wrapped; if (window.mozRTCPeerConnection && window.mozRTCPeerConnection !== Wrapped) window.mozRTCPeerConnection = Wrapped; } catch {} } function scan(obj, depth, seen) { if (depth > 6 || !obj || typeof obj !== 'object') return; if (seen.has(obj)) return; seen.add(obj); try { if (obj.getStats && obj.addIceCandidate && (obj.setLocalDescription || obj.setRemoteDescription) && !obj.__gc_hooked) { hookInstance(obj); return; } } catch {} try { for (const key of Object.keys(obj)) try { scan(obj[key], depth + 1, seen); } catch {} } catch {} try { const proto = Object.getPrototypeOf(obj); if (proto && proto !== Object.prototype && proto !== Array.prototype) { for (const key of Object.getOwnPropertyNames(proto)) { try { scan(obj[key], depth + 1, seen); } catch {} } } } catch {} } try { scan(window, 0, new WeakSet()); } catch {} try { for (const iframe of document.querySelectorAll('iframe')) { if (iframe.contentWindow && iframe.contentWindow.RTCPeerConnection) { const iRTC = iframe.contentWindow.RTCPeerConnection; if (!iRTC.prototype.__gc_hooked || iRTC.prototype.__gc_hooked < GC_HOOK_VERSION) { iRTC.prototype.__gc_hooked = GC_HOOK_VERSION; if (!iRTC.prototype.__gc_orig_addIceCandidate) iRTC.prototype.__gc_orig_addIceCandidate = iRTC.prototype.addIceCandidate; if (!iRTC.prototype.__gc_orig_setLocalDescription) iRTC.prototype.__gc_orig_setLocalDescription = iRTC.prototype.setLocalDescription; if (!iRTC.prototype.__gc_orig_setRemoteDescription) iRTC.prototype.__gc_orig_setRemoteDescription = iRTC.prototype.setRemoteDescription; iRTC.prototype.addIceCandidate = function (candidate, ...rest) { try { if (candidate && candidate.candidate) checkCandidate(candidate.candidate, this); } catch {} return iRTC.prototype.__gc_orig_addIceCandidate.apply(this, arguments); }; iRTC.prototype.setLocalDescription = function (desc, ...rest) { try { if (desc && desc.sdp) checkSDP(desc.sdp, this); } catch {} return iRTC.prototype.__gc_orig_setLocalDescription.apply(this, arguments); }; iRTC.prototype.setRemoteDescription = function (desc, ...rest) { try { if (desc && desc.sdp) checkSDP(desc.sdp, this); } catch {} return iRTC.prototype.__gc_orig_setRemoteDescription.apply(this, arguments); }; } try { for (const key of Object.keys(iframe.contentWindow)) { const val = iframe.contentWindow[key]; if (val && typeof val === 'object' && val.addIceCandidate && !val.__gc_hooked) hookInstance(val); } } catch {} } } } catch {} } setInterval(hook, 3000); hook(); let lastUrl = location.href; setInterval(() => { if (location.href !== lastUrl) { lastUrl = location.href; seenIPs.clear(); lastIP = null; currentData = null; lastRenderedIP = null; geoBusy = false; geoQueue = null; peerConnections = []; window.__gc_deep_scan_done = false; const body = $('gc-body'); if (body) body.innerHTML = '
Waiting for peer connection...
'; hook(); } }, 2000); createUI(); console.warn('[GeoChecker] Console inject v5.0 loaded. Waiting for WebRTC peer...'); })();