Anti-Fingerprinting Shield Plus

Full spoofing logic + settings UI panel. Browser-specific spoofing (Safari, Chrome, Edge) with persistent toggles.

目前为 2025-05-03 提交的版本,查看 最新版本

// ==UserScript==
// @name         Anti-Fingerprinting Shield Plus
// @namespace    https://365devnet.eu/userscripts
// @version      4.7
// @description  Full spoofing logic + settings UI panel. Browser-specific spoofing (Safari, Chrome, Edge) with persistent toggles.
// @author       Richard B
// @match        *://*/*
// @run-at       document-start
// @grant        none
// @license      MIT
// ==/UserScript==

(() => {
  const DEBUG = true;
  const settingsKey = '__afs_user_settings';
  const sessionExpiryKey = '__afs_last_seen';
  const spoofDefaults = {
    userAgent: true,
    platform: true,
    language: true,
    screen: true,
    hardwareConcurrency: true,
    timezone: true,
    canvas: true,
    webgl: true,
    audio: true,
    plugins: true,
    mediaDevices: true,
    storageEstimate: true,
    matchMedia: true,
    sharedArrayBuffer: true
  };

  const spoofSettings = loadSettings();
  const now = Date.now();
  const SESSION_TIMEOUT_MINUTES = 30;
  const browser = detectBrowser();

  if (isExpired()) {
    clearStoredValues();
    log(`Session expired. Values cleared.`);
  }
  localStorage.setItem(sessionExpiryKey, now.toString());
  const sessionId = getOrCreatePersistent('__afs_session_id', () => Math.random().toString(36).substring(2, 10));

  function log(...args) {
    if (DEBUG) console.log('[AFS+]', ...args);
  }

  function loadSettings() {
    const saved = localStorage.getItem(settingsKey);
    return saved ? JSON.parse(saved) : { ...spoofDefaults };
  }

  function saveSettings(settings) {
    localStorage.setItem(settingsKey, JSON.stringify(settings));
  }

  function clearStoredValues() {
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith('__afs_')) localStorage.removeItem(key);
    });
  }

  function isExpired() {
    const lastSeen = parseInt(localStorage.getItem(sessionExpiryKey), 10);
    return isNaN(lastSeen) || now - lastSeen > SESSION_TIMEOUT_MINUTES * 60 * 1000;
  }

  function getOrCreatePersistent(key, generator) {
    const fullKey = '__afs_' + key;
    let value = localStorage.getItem(fullKey);
    if (!value) {
      value = generator();
      localStorage.setItem(fullKey, value);
    }
    return value;
  }

  function detectBrowser() {
    const ua = navigator.userAgent;
    if (/Safari/.test(ua) && !/Chrome/.test(ua)) return 'Safari';
    if (/Edg\//.test(ua)) return 'Edge';
    if (/Chrome/.test(ua)) return 'Chrome';
    return 'Other';
  }

  function pick(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
  }

  function spoof(obj, prop, valueFn) {
    try {
      Object.defineProperty(obj, prop, {
        get: valueFn,
        configurable: true
      });
    } catch (e) {
      log('Spoof failed:', prop, e);
    }
  }
    // --- Spoofed values ---
    const spoofed = {
      userAgent: getOrCreatePersistent('ua', () => pick([
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
      ])),
      platform: getOrCreatePersistent('platform', () => pick(['MacIntel', 'Win32', 'Linux x86_64'])),
      language: getOrCreatePersistent('lang', () => pick(['en-US', 'nl-NL', 'de-DE'])),
      screenWidth: parseInt(getOrCreatePersistent('sw', () => pick([1920, 1366, 1440]))),
      screenHeight: parseInt(getOrCreatePersistent('sh', () => pick([1080, 900, 768]))),
      cores: parseInt(getOrCreatePersistent('cores', () => pick([2, 4, 8]))),
      memory: parseInt(getOrCreatePersistent('mem', () => pick([2, 4, 8]))),
      timezone: getOrCreatePersistent('tz', () => pick(['UTC', 'Europe/Amsterdam', 'America/New_York']))
    };
  
    // --- Apply spoofing based on settings ---
    if (spoofSettings.userAgent) spoof(navigator, 'userAgent', () => spoofed.userAgent);
    if (spoofSettings.platform) spoof(navigator, 'platform', () => spoofed.platform);
    if (spoofSettings.language) {
      spoof(navigator, 'language', () => spoofed.language);
      spoof(navigator, 'languages', () => [spoofed.language, 'en']);
    }
    if (spoofSettings.screen) {
      spoof(window.screen, 'width', () => spoofed.screenWidth);
      spoof(window.screen, 'height', () => spoofed.screenHeight);
      spoof(window, 'innerWidth', () => spoofed.screenWidth);
      spoof(window, 'innerHeight', () => spoofed.screenHeight - 40);
    }
    if (spoofSettings.hardwareConcurrency) {
      spoof(navigator, 'hardwareConcurrency', () => spoofed.cores);
      spoof(navigator, 'deviceMemory', () => spoofed.memory);
    }
    if (spoofSettings.timezone && typeof Intl !== 'undefined') {
      const orig = Intl.DateTimeFormat.prototype.resolvedOptions;
      Intl.DateTimeFormat.prototype.resolvedOptions = function () {
        const options = orig.call(this);
        options.timeZone = spoofed.timezone;
        return options;
      };
    }
    if (spoofSettings.canvas && CanvasRenderingContext2D) {
      const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
      CanvasRenderingContext2D.prototype.getImageData = function (x, y, w, h) {
        const data = originalGetImageData.call(this, x, y, w, h);
        for (let i = 0; i < data.data.length; i += 4) {
          data.data[i] += Math.floor(Math.random() * 3);
          data.data[i + 1] += Math.floor(Math.random() * 3);
          data.data[i + 2] += Math.floor(Math.random() * 3);
        }
        return data;
      };
    }
    if (spoofSettings.webgl && WebGLRenderingContext) {
      const originalGL = WebGLRenderingContext.prototype.getParameter;
      WebGLRenderingContext.prototype.getParameter = function (param) {
        const spoofMap = { 37445: 'FakeVendor Inc.', 37446: 'Virtual GPU Renderer', 3379: 4096, 35661: 8 };
        return spoofMap[param] || originalGL.call(this, param);
      };
    }
    if (spoofSettings.audio && window.AudioContext) {
      const ctx = window.AudioContext.prototype;
      spoof(ctx, 'sampleRate', () => 44100);
      if (AnalyserNode.prototype.getFloatFrequencyData) {
        const original = AnalyserNode.prototype.getFloatFrequencyData;
        AnalyserNode.prototype.getFloatFrequencyData = function (arr) {
          original.call(this, arr);
          for (let i = 0; i < arr.length; i++) {
            arr[i] += Math.random() * 0.1;
          }
        };
      }
    }
  
    if (spoofSettings.mediaDevices) {
      spoof(navigator, 'mediaDevices', () => ({
        enumerateDevices: () => Promise.resolve([])
      }));
    }
  
    if (spoofSettings.plugins) {
      spoof(navigator, 'plugins', () => []);
      spoof(navigator, 'mimeTypes', () => ({ length: 0 }));
    }
  
    if (spoofSettings.storageEstimate) {
      navigator.storage.estimate = () => Promise.resolve({
        usage: 5242880,
        quota: 1073741824
      });
    }
  
    if (spoofSettings.matchMedia) {
      const originalMatchMedia = window.matchMedia;
      window.matchMedia = function (query) {
        if (query.includes('color-scheme') || query.includes('forced-colors')) {
          return { matches: Math.random() > 0.5, media: query };
        }
        return originalMatchMedia.call(this, query);
      };
    }
  
    if (spoofSettings.sharedArrayBuffer) {
      spoof(window, 'SharedArrayBuffer', () => undefined);
    }
      // === UI Panel ===
  const panel = document.createElement('div');
  panel.style.cssText = 'position:fixed;top:10px;right:10px;background:#1b1b1b;color:#fff;padding:10px;border-radius:8px;z-index:99999;font-family:sans-serif;font-size:13px;';
  panel.innerHTML = '<strong>🛡️ AFS+ Settings</strong><br>';

  Object.keys(spoofDefaults).forEach(key => {
    const line = document.createElement('div');
    line.innerHTML = `<label><input type="checkbox" ${spoofSettings[key] ? 'checked' : ''} /> ${key}</label>`;
    line.querySelector('input').addEventListener('change', () => {
      spoofSettings[key] = !spoofSettings[key];
      saveSettings(spoofSettings);
      location.reload();
    });
    panel.appendChild(line);
  });

  const closeBtn = document.createElement('button');
  closeBtn.textContent = 'Close';
  closeBtn.style.cssText = 'margin-top:8px;padding:2px 6px;font-size:12px;';
  closeBtn.onclick = () => panel.remove();
  panel.appendChild(closeBtn);

  const toggleBtn = document.createElement('div');
  toggleBtn.textContent = '⚙️';
  toggleBtn.style.cssText = 'position:fixed;top:10px;left:10px;cursor:pointer;z-index:99999;font-size:18px;';
  toggleBtn.onclick = () => {
    if (!document.body.contains(panel)) document.body.appendChild(panel);
  };

  document.addEventListener('keydown', e => {
    if (e.ctrlKey && e.shiftKey && e.key === 'F') {
      document.body.appendChild(panel);
    }
  });

  document.addEventListener('DOMContentLoaded', () => {
    document.body.appendChild(toggleBtn);
  });

})();