Spaces:
Sleeping
Sleeping
| """ | |
| Спуфинг Client Hints API (navigator.userAgentData) | |
| Новый API который заменяет User-Agent строку. | |
| AWS может проверять соответствие между UA и Client Hints. | |
| """ | |
| import re | |
| from typing import Tuple | |
| from .base import BaseSpoofModule | |
| def parse_platform_from_ua(user_agent: str) -> Tuple[str, str, str, str]: | |
| """ | |
| Извлекает информацию о платформе из User-Agent строки. | |
| Returns: | |
| Tuple[platform, platform_version, architecture, bitness] | |
| Примеры User-Agent: | |
| - Windows: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36..." | |
| - macOS: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36..." | |
| - Linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36..." | |
| """ | |
| # Defaults (Windows 10 как безопасный вариант) | |
| platform = "Windows" | |
| platform_version = "10.0.0" | |
| architecture = "x86" | |
| bitness = "64" | |
| # Определяем платформу | |
| if "Macintosh" in user_agent or "Mac OS X" in user_agent: | |
| platform = "macOS" | |
| # macOS версия из UA: "Mac OS X 10_15_7" -> "10.15.7" | |
| mac_match = re.search(r'Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?', user_agent) | |
| if mac_match: | |
| major = mac_match.group(1) | |
| minor = mac_match.group(2) | |
| patch = mac_match.group(3) or "0" | |
| platform_version = f"{major}.{minor}.{patch}" | |
| else: | |
| platform_version = "10.15.7" # Безопасный default для macOS | |
| architecture = "arm" # Современные Mac на Apple Silicon | |
| bitness = "64" | |
| elif "Linux" in user_agent: | |
| platform = "Linux" | |
| platform_version = "" # Linux не передаёт версию в Client Hints | |
| if "x86_64" in user_agent: | |
| architecture = "x86" | |
| bitness = "64" | |
| elif "aarch64" in user_agent or "arm64" in user_agent: | |
| architecture = "arm" | |
| bitness = "64" | |
| else: | |
| architecture = "x86" | |
| bitness = "64" | |
| elif "Windows" in user_agent: | |
| platform = "Windows" | |
| architecture = "x86" | |
| # Определяем битность | |
| if "Win64" in user_agent or "x64" in user_agent or "WOW64" in user_agent: | |
| bitness = "64" | |
| else: | |
| bitness = "32" | |
| # Windows NT версия | |
| # Windows NT 10.0 может быть Windows 10 или Windows 11 | |
| # Различаем по build number если есть, иначе default = Windows 10 | |
| nt_match = re.search(r'Windows NT (\d+\.\d+)', user_agent) | |
| if nt_match: | |
| nt_version = nt_match.group(1) | |
| if nt_version == "10.0": | |
| # Пытаемся найти build number в UA (редко, но бывает) | |
| # Формат: "Windows NT 10.0; Win64; x64; rv:..." или с build | |
| # Некоторые UA содержат build: "Windows NT 10.0.22621" | |
| build_match = re.search(r'Windows NT 10\.0\.(\d+)', user_agent) | |
| if build_match: | |
| build = int(build_match.group(1)) | |
| if build >= 22000: | |
| # Windows 11 (build 22000+) | |
| platform_version = "15.0.0" | |
| else: | |
| # Windows 10 | |
| platform_version = "10.0.0" | |
| else: | |
| # Нет build number - безопасный default Windows 10 | |
| platform_version = "10.0.0" | |
| elif nt_version == "6.3": | |
| platform_version = "6.3.0" # Windows 8.1 | |
| elif nt_version == "6.2": | |
| platform_version = "6.2.0" # Windows 8 | |
| elif nt_version == "6.1": | |
| platform_version = "6.1.0" # Windows 7 | |
| else: | |
| platform_version = "10.0.0" # Default | |
| return platform, platform_version, architecture, bitness | |
| class ClientHintsSpoofModule(BaseSpoofModule): | |
| """Спуфинг Client Hints API""" | |
| name = "client_hints" | |
| description = "Spoof navigator.userAgentData (Client Hints)" | |
| def get_js(self) -> str: | |
| p = self.profile | |
| # Извлекаем версию Chrome из user_agent | |
| chrome_match = re.search(r'Chrome/(\d+)', p.user_agent) | |
| chrome_version = chrome_match.group(1) if chrome_match else '131' | |
| # Извлекаем информацию о платформе из User-Agent | |
| platform, platform_version, architecture, bitness = parse_platform_from_ua(p.user_agent) | |
| return f''' | |
| (function() {{ | |
| 'use strict'; | |
| const CHROME_VERSION = '{chrome_version}'; | |
| const PLATFORM = '{platform}'; | |
| const PLATFORM_VERSION = '{platform_version}'; | |
| const ARCHITECTURE = '{architecture}'; | |
| const BITNESS = '{bitness}'; | |
| // Brands array (как в реальном Chrome) | |
| const brands = [ | |
| {{ brand: 'Google Chrome', version: CHROME_VERSION }}, | |
| {{ brand: 'Chromium', version: CHROME_VERSION }}, | |
| {{ brand: 'Not_A Brand', version: '24' }} | |
| ]; | |
| const fullVersionList = [ | |
| {{ brand: 'Google Chrome', version: CHROME_VERSION + '.0.0.0' }}, | |
| {{ brand: 'Chromium', version: CHROME_VERSION + '.0.0.0' }}, | |
| {{ brand: 'Not_A Brand', version: '24.0.0.0' }} | |
| ]; | |
| // Создаём fake userAgentData с правильным prototype | |
| const NavigatorUAData = function() {{}}; | |
| NavigatorUAData.prototype.brands = brands; | |
| NavigatorUAData.prototype.mobile = false; | |
| NavigatorUAData.prototype.platform = PLATFORM; | |
| NavigatorUAData.prototype.getHighEntropyValues = function(hints) {{ | |
| return Promise.resolve({{ | |
| architecture: ARCHITECTURE, | |
| bitness: BITNESS, | |
| brands: brands, | |
| fullVersionList: fullVersionList, | |
| mobile: false, | |
| model: '', | |
| platform: PLATFORM, | |
| platformVersion: PLATFORM_VERSION, | |
| uaFullVersion: CHROME_VERSION + '.0.0.0', | |
| wow64: false, | |
| formFactors: ['Desktop'] | |
| }}); | |
| }}; | |
| NavigatorUAData.prototype.toJSON = function() {{ | |
| return {{ | |
| brands: brands, | |
| mobile: false, | |
| platform: PLATFORM | |
| }}; | |
| }}; | |
| const fakeUserAgentData = new NavigatorUAData(); | |
| fakeUserAgentData.brands = brands; | |
| fakeUserAgentData.mobile = false; | |
| fakeUserAgentData.platform = PLATFORM; | |
| // Замораживаем brands array | |
| Object.freeze(fakeUserAgentData.brands); | |
| // Регистрируем в Proxy системе (если доступна) | |
| if (typeof window.__registerSpoofedProp === 'function') {{ | |
| window.__registerSpoofedProp('userAgentData', () => fakeUserAgentData); | |
| }} | |
| // Также пробуем напрямую переопределить (для случаев без Proxy) | |
| try {{ | |
| Object.defineProperty(navigator, 'userAgentData', {{ | |
| get: () => fakeUserAgentData, | |
| configurable: true, | |
| enumerable: true | |
| }}); | |
| }} catch(e) {{}} | |
| // Добавляем Symbol.toStringTag для правильного typeof | |
| try {{ | |
| Object.defineProperty(NavigatorUAData.prototype, Symbol.toStringTag, {{ | |
| value: 'NavigatorUAData', | |
| configurable: true | |
| }}); | |
| }} catch(e) {{}} | |
| }})(); | |
| ''' | |