File size: 7,668 Bytes
494c89b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
"""
Спуфинг 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) {{}}
}})();
'''