KManager / spoofers /client_hints.py
StarrySkyWorld's picture
Initial commit
494c89b
"""
Спуфинг 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) {{}}
}})();
'''