super-stealth
This commit is contained in:
1 parent
2a058623a4
commit
692f4c0dd1
5 files changed
+35525
-178
No files matched your search
+40
-1
@@ -18,7 +18,8 @@ LLM_MODEL_NAME=google/gemini-2.0-flash-001
|
|||||||
# ===========================================
|
# ===========================================
|
||||||
|
|
||||||
# Запуск браузера в headless режиме (true/false)
|
# Запуск браузера в headless режиме (true/false)
|
||||||
BROWSER_HEADLESS=true
|
# Рекомендуется: false для максимального stealth
|
||||||
|
BROWSER_HEADLESS=false
|
||||||
|
|
||||||
# Максимальное количество шагов для выполнения задачи
|
# Максимальное количество шагов для выполнения задачи
|
||||||
AGENT_MAX_STEPS=20
|
AGENT_MAX_STEPS=20
|
||||||
@@ -26,6 +27,44 @@ AGENT_MAX_STEPS=20
|
|||||||
# Использование зрения (true/false)
|
# Использование зрения (true/false)
|
||||||
AGENT_USE_VISION=true
|
AGENT_USE_VISION=true
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки прокси (для максимального stealth)
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# Прокси сервер (формат: protocol://user:pass@host:port)
|
||||||
|
# Примеры:
|
||||||
|
# http://user:pass@proxy.com:8080
|
||||||
|
# socks5://user:pass@sproxy.com:1080
|
||||||
|
# http://host:port (без авторизации)
|
||||||
|
PROXY_URL=
|
||||||
|
|
||||||
|
# Прокси для browser-use cloud (страна)
|
||||||
|
# Доступно: us, fr, it, jp, au, de, fi, ca
|
||||||
|
CLOUD_PROXY_COUNTRY=
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки stealth (антидетект)
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# Рандомизировать viewport (ширина x высота)
|
||||||
|
# Формат: WIDTHxHEIGHT, например 1920x1080
|
||||||
|
# Оставьте пустым для использования случайных значений
|
||||||
|
VIEWPORT_SIZE=
|
||||||
|
|
||||||
|
# Использовать xvfb для виртуального дисплея (рекомендуется)
|
||||||
|
USE_XVFB=true
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки решения капчи
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# CapSolver API Key (для автоматического решения капчи)
|
||||||
|
# Получить на https://capmonster.cloud/ или https://capsolver.com/
|
||||||
|
CAPSOLVER_API_KEY=
|
||||||
|
|
||||||
|
# Автоматически решать капчу (true/false)
|
||||||
|
AUTO_SOLVE_CAPTCHA=false
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# Настройки сервера
|
# Настройки сервера
|
||||||
# ===========================================
|
# ===========================================
|
||||||
|
|||||||
+2
-1
@@ -35,7 +35,8 @@ RUN pip install --no-cache-dir --root-user-action=ignore \
|
|||||||
langchain-openai \
|
langchain-openai \
|
||||||
langchain \
|
langchain \
|
||||||
python-dotenv \
|
python-dotenv \
|
||||||
setuptools
|
setuptools \
|
||||||
|
httpx
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ REMOTE_DEBUGGING_ENABLED = os.getenv("REMOTE_DEBUGGING_ENABLED", "false").lower(
|
|||||||
# Модель по умолчанию из переменной окружения
|
# Модель по умолчанию из переменной окружения
|
||||||
DEFAULT_MODEL = os.getenv("LLM_MODEL_NAME", "openai/gpt-4o-mini")
|
DEFAULT_MODEL = os.getenv("LLM_MODEL_NAME", "openai/gpt-4o-mini")
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки прокси
|
||||||
|
# ===========================================
|
||||||
|
PROXY_URL = os.getenv("PROXY_URL", "")
|
||||||
|
CLOUD_PROXY_COUNTRY = os.getenv("CLOUD_PROXY_COUNTRY", "")
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки stealth
|
||||||
|
# ===========================================
|
||||||
|
BROWSER_HEADLESS = os.getenv("BROWSER_HEADLESS", "false").lower() == "true"
|
||||||
|
USE_XVFB = os.getenv("USE_XVFB", "true").lower() == "true"
|
||||||
|
VIEWPORT_SIZE = os.getenv("VIEWPORT_SIZE", "") # Формат: WIDTHxHEIGHT, напр. 1920x1080
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройки решения капчи (CapSolver)
|
||||||
|
# ===========================================
|
||||||
|
CAPSOLVER_API_KEY = os.getenv("CAPSOLVER_API_KEY", "")
|
||||||
|
AUTO_SOLVE_CAPTCHA = os.getenv("AUTO_SOLVE_CAPTCHA", "false").lower() == "true"
|
||||||
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@@ -114,7 +133,193 @@ try:
|
|||||||
except Exception as e2:
|
except Exception as e2:
|
||||||
return ActionResult(error=f"Failed to paste text: {e2}")
|
return ActionResult(error=f"Failed to paste text: {e2}")
|
||||||
|
|
||||||
logger.info("Custom tools (paste_text) registered successfully")
|
@custom_tools.action(description='Solve CAPTCHA using CapSolver API. Use this when you encounter reCAPTCHA, hCaptcha, or Cloudflare Turnstile. The agent will automatically detect and solve the captcha.')
|
||||||
|
async def solve_captcha(browser_session: BrowserSession = None):
|
||||||
|
"""
|
||||||
|
Решает капчу (reCAPTCHA/hCaptcha/Turnstile) с помощью CapSolver API.
|
||||||
|
|
||||||
|
Этот action автоматически:
|
||||||
|
1. Обнаруживает тип капчи на странице
|
||||||
|
2. Отправляет задачу в CapSolver
|
||||||
|
3. Ждёт решения
|
||||||
|
4. Вставляет ответ в форму
|
||||||
|
"""
|
||||||
|
if not CAPSOLVER_API_KEY:
|
||||||
|
return ActionResult(
|
||||||
|
error="CapSolver API key not configured. Set CAPSOLVER_API_KEY environment variable.",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
page = await browser_session.get_current_page()
|
||||||
|
|
||||||
|
# Ищем sitekey для разных типов капчи
|
||||||
|
sitekey = None
|
||||||
|
captcha_type = None
|
||||||
|
|
||||||
|
# reCAPTCHA v2
|
||||||
|
recaptcha_element = await page.query_selector('[data-sitekey]')
|
||||||
|
if recaptcha_element:
|
||||||
|
sitekey = await recaptcha_element.get_attribute('data-sitekey')
|
||||||
|
if sitekey:
|
||||||
|
# Проверяем reCAPTCHA vs hCaptcha
|
||||||
|
iframe = await page.query_selector('iframe[src*="google.com/recaptcha"]')
|
||||||
|
if iframe:
|
||||||
|
captcha_type = "RecaptchaV2Task"
|
||||||
|
|
||||||
|
# hCaptcha
|
||||||
|
if not sitekey:
|
||||||
|
hcaptcha_element = await page.query_selector('[data-sitekey]')
|
||||||
|
if hcaptcha_element:
|
||||||
|
sitekey = await hcaptcha_element.get_attribute('data-sitekey')
|
||||||
|
hcaptcha_frame = await page.query_selector('iframe[src*="hcaptcha.com"]')
|
||||||
|
if hcaptcha_frame and sitekey:
|
||||||
|
captcha_type = "HCaptchaTask"
|
||||||
|
|
||||||
|
# Cloudflare Turnstile
|
||||||
|
if not sitekey:
|
||||||
|
turnstile_element = await page.query_selector('[data-sitekey]')
|
||||||
|
if turnstile_element:
|
||||||
|
sitekey = await turnstile_element.get_attribute('data-sitekey')
|
||||||
|
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
|
||||||
|
if turnstile_frame and sitekey:
|
||||||
|
captcha_type = "TurnstileTask"
|
||||||
|
|
||||||
|
if not sitekey or not captcha_type:
|
||||||
|
return ActionResult(
|
||||||
|
extracted_content="No captcha found on current page",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
page_url = page.url
|
||||||
|
logger.info(f"Found {captcha_type} with sitekey: {sitekey}")
|
||||||
|
|
||||||
|
# Отправляем задачу в CapSolver
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||||
|
# Создаём задачу
|
||||||
|
task_create_response = await client.post(
|
||||||
|
"https://api.capsolver.com/createTask",
|
||||||
|
json={
|
||||||
|
"clientKey": CAPSOLVER_API_KEY,
|
||||||
|
"task": {
|
||||||
|
"type": captcha_type,
|
||||||
|
"websiteURL": page_url,
|
||||||
|
"websiteKey": sitekey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
task_data = task_create_response.json()
|
||||||
|
|
||||||
|
if task_data.get("errorCode"):
|
||||||
|
logger.error(f"CapSolver createTask error: {task_data}")
|
||||||
|
return ActionResult(
|
||||||
|
error=f"CapSolver error: {task_data.get('errorDescription', 'Unknown error')}",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
task_id = task_data.get("taskId")
|
||||||
|
logger.info(f"CapSolver task created: {task_id}")
|
||||||
|
|
||||||
|
# Ждём решения (максимум 60 секунд)
|
||||||
|
solution = None
|
||||||
|
for attempt in range(30):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
result_response = await client.post(
|
||||||
|
"https://api.capsolver.com/getTaskResult",
|
||||||
|
json={
|
||||||
|
"clientKey": CAPSOLVER_API_KEY,
|
||||||
|
"taskId": task_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result_data = result_response.json()
|
||||||
|
|
||||||
|
if result_data.get("status") == "ready":
|
||||||
|
solution = result_data.get("solution", {})
|
||||||
|
break
|
||||||
|
elif result_data.get("errorCode"):
|
||||||
|
logger.error(f"CapSolver error: {result_data}")
|
||||||
|
break
|
||||||
|
|
||||||
|
if not solution:
|
||||||
|
return ActionResult(
|
||||||
|
error="CapSolver timeout - captcha not solved",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Вставляем решение
|
||||||
|
if captcha_type == "RecaptchaV2Task":
|
||||||
|
g_recaptcha_response = solution.get("gRecaptchaResponse")
|
||||||
|
if g_recaptcha_response:
|
||||||
|
await page.evaluate(f'''() => {{
|
||||||
|
const textarea = document.getElementById('g-recaptcha-response');
|
||||||
|
if (textarea) {{
|
||||||
|
textarea.value = '{g_recaptcha_response}';
|
||||||
|
textarea.style.display = 'block';
|
||||||
|
}}
|
||||||
|
}}''')
|
||||||
|
logger.info("reCAPTCHA solved successfully!")
|
||||||
|
return ActionResult(
|
||||||
|
extracted_content="reCAPTCHA solved successfully!",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
elif captcha_type == "HCaptchaTask":
|
||||||
|
h_captcha_response = solution.get("hCaptchaResponse")
|
||||||
|
if h_captcha_response:
|
||||||
|
await page.evaluate(f'''() => {{
|
||||||
|
const textarea = document.querySelector('[name="h-captcha-response"]');
|
||||||
|
if (textarea) {{
|
||||||
|
textarea.value = '{h_captcha_response}';
|
||||||
|
textarea.style.display = 'block';
|
||||||
|
}}
|
||||||
|
}}''')
|
||||||
|
logger.info("hCaptcha solved successfully!")
|
||||||
|
return ActionResult(
|
||||||
|
extracted_content="hCaptcha solved successfully!",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
elif captcha_type == "TurnstileTask":
|
||||||
|
token = solution.get("token")
|
||||||
|
if token:
|
||||||
|
await page.evaluate(f'''(token) => {{
|
||||||
|
const textarea = document.querySelector('[name="cf-turnstile-response"]');
|
||||||
|
if (textarea) {{
|
||||||
|
textarea.value = token;
|
||||||
|
textarea.style.display = 'block';
|
||||||
|
}}
|
||||||
|
// Also try to submit to Turnstile directly
|
||||||
|
const turnstile = window.__cf_challenge_js;
|
||||||
|
if (turnstile) {{
|
||||||
|
try {{
|
||||||
|
turnstile.submit(token);
|
||||||
|
}} catch(e) {{}}
|
||||||
|
}}
|
||||||
|
}}''', token)
|
||||||
|
logger.info("Turnstile solved successfully!")
|
||||||
|
return ActionResult(
|
||||||
|
extracted_content="Turnstile solved successfully!",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return ActionResult(
|
||||||
|
error="Failed to apply captcha solution",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error solving captcha: {e}")
|
||||||
|
return ActionResult(
|
||||||
|
error=f"Error solving captcha: {str(e)}",
|
||||||
|
include_in_memory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Custom tools (paste_text, solve_captcha) registered successfully")
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
custom_tools = None
|
custom_tools = None
|
||||||
logger.warning(f"Could not import Tools/BrowserSession from browser_use: {e}. Custom tools disabled.")
|
logger.warning(f"Could not import Tools/BrowserSession from browser_use: {e}. Custom tools disabled.")
|
||||||
@@ -146,6 +351,150 @@ def patch_chrome_binary():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not patch {path}: {e}")
|
logger.warning(f"Could not patch {path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def solve_captcha_with_capsolver(page, capsolver_api_key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Решает капчу (reCAPTCHA/hCaptcha/Turnstile) с помощью CapSolver API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
page: Playwright page object
|
||||||
|
capsolver_api_key: API ключ CapSolver
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True если капча решена, False иначе
|
||||||
|
"""
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
if not capsolver_api_key:
|
||||||
|
logger.warning("CapSolver API key not provided")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Ищем sitekey
|
||||||
|
# reCAPTCHA v2
|
||||||
|
recaptcha_element = await page.query_selector('*[name="recaptcha-sitekey"]')
|
||||||
|
if not recaptcha_element:
|
||||||
|
recaptcha_element = await page.query_selector('[data-sitekey]')
|
||||||
|
|
||||||
|
sitekey = None
|
||||||
|
captcha_type = None
|
||||||
|
|
||||||
|
if recaptcha_element:
|
||||||
|
sitekey = await recaptcha_element.get_attribute('data-sitekey')
|
||||||
|
if sitekey:
|
||||||
|
captcha_type = "RecaptchaV2Task"
|
||||||
|
|
||||||
|
# hCaptcha
|
||||||
|
if not sitekey:
|
||||||
|
hcaptcha_element = await page.query_selector('[data-sitekey]')
|
||||||
|
if hcaptcha_element:
|
||||||
|
sitekey = await hcaptcha_element.get_attribute('data-sitekey')
|
||||||
|
if sitekey:
|
||||||
|
# Проверяем что это hCaptcha
|
||||||
|
hcaptcha_frame = await page.query_selector('iframe[src*="hcaptcha.com"]')
|
||||||
|
if hcaptcha_frame:
|
||||||
|
captcha_type = "HCaptchaTask"
|
||||||
|
|
||||||
|
# Cloudflare Turnstile
|
||||||
|
if not sitekey:
|
||||||
|
turnstile_element = await page.query_selector('[data-sitekey]')
|
||||||
|
if turnstile_element:
|
||||||
|
sitekey = await turnstile_element.get_attribute('data-sitekey')
|
||||||
|
if sitekey:
|
||||||
|
turnstile_frame = await page.query_selector('iframe[src*="turnstile"]')
|
||||||
|
if turnstile_frame:
|
||||||
|
captcha_type = "TurnstileTask"
|
||||||
|
|
||||||
|
if not sitekey or not captcha_type:
|
||||||
|
logger.info("No captcha found on page")
|
||||||
|
return False
|
||||||
|
|
||||||
|
page_url = page.url
|
||||||
|
logger.info(f"Found {captcha_type} with sitekey: {sitekey}")
|
||||||
|
|
||||||
|
# Отправляем задачу в CapSolver
|
||||||
|
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||||
|
# Создаём задачу
|
||||||
|
task_create_response = await client.post(
|
||||||
|
"https://api.capsolver.com/createTask",
|
||||||
|
json={
|
||||||
|
"clientKey": capsolver_api_key,
|
||||||
|
"task": {
|
||||||
|
"type": captcha_type,
|
||||||
|
"websiteURL": page_url,
|
||||||
|
"websiteKey": sitekey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
task_data = task_create_response.json()
|
||||||
|
|
||||||
|
if task_data.get("errorCode"):
|
||||||
|
logger.error(f"CapSolver createTask error: {task_data}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
task_id = task_data.get("taskId")
|
||||||
|
logger.info(f"CapSolver task created: {task_id}")
|
||||||
|
|
||||||
|
# Ждём решения
|
||||||
|
for attempt in range(30):
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
result_response = await client.post(
|
||||||
|
"https://api.capsolver.com/getTaskResult",
|
||||||
|
json={
|
||||||
|
"clientKey": capsolver_api_key,
|
||||||
|
"taskId": task_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result_data = result_response.json()
|
||||||
|
|
||||||
|
if result_data.get("status") == "ready":
|
||||||
|
solution = result_data.get("solution", {})
|
||||||
|
|
||||||
|
if captcha_type == "RecaptchaV2Task":
|
||||||
|
# Вставляем решение в textarea
|
||||||
|
g_recaptcha_response = solution.get("gRecaptchaResponse")
|
||||||
|
if g_recaptcha_response:
|
||||||
|
# Ищем textarea для ответа
|
||||||
|
textarea = await page.query_selector('#g-recaptcha-response')
|
||||||
|
if textarea:
|
||||||
|
await textarea.fill(g_recaptcha_response)
|
||||||
|
logger.info("reCAPTCHA solved successfully!")
|
||||||
|
return True
|
||||||
|
elif captcha_type == "HCaptchaTask":
|
||||||
|
h_captcha_response = solution.get("hCaptchaResponse")
|
||||||
|
if h_captcha_response:
|
||||||
|
# Вставляем в h-captcha-response
|
||||||
|
textarea = await page.query_selector('[name="h-captcha-response"]')
|
||||||
|
if textarea:
|
||||||
|
await textarea.fill(h_captcha_response)
|
||||||
|
logger.info("hCaptcha solved successfully!")
|
||||||
|
return True
|
||||||
|
elif captcha_type == "TurnstileTask":
|
||||||
|
token = solution.get("token")
|
||||||
|
if token:
|
||||||
|
# Вставляем токен
|
||||||
|
textarea = await page.query_selector('[name="cf-turnstile-response"]')
|
||||||
|
if textarea:
|
||||||
|
await textarea.fill(token)
|
||||||
|
logger.info("Turnstile solved successfully!")
|
||||||
|
return True
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif result_data.get("errorCode"):
|
||||||
|
logger.error(f"CapSolver error: {result_data}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.warning("CapSolver timeout - captcha not solved")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error solving captcha: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
# Startup logic
|
# Startup logic
|
||||||
@@ -371,12 +720,63 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
|||||||
# Внутренний порт для Chrome
|
# Внутренний порт для Chrome
|
||||||
CHROME_INTERNAL_PORT = REMOTE_DEBUGGING_PORT + 1 # 9223
|
CHROME_INTERNAL_PORT = REMOTE_DEBUGGING_PORT + 1 # 9223
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройка viewport (рандомизация)
|
||||||
|
# ===========================================
|
||||||
|
if VIEWPORT_SIZE and 'x' in VIEWPORT_SIZE:
|
||||||
|
try:
|
||||||
|
width, height = map(int, VIEWPORT_SIZE.split('x'))
|
||||||
|
except:
|
||||||
|
width, height = 1920, 1080
|
||||||
|
else:
|
||||||
|
# Рандомизация viewport для большей уникальности
|
||||||
|
import random
|
||||||
|
# Популярные разрешения
|
||||||
|
viewports = [
|
||||||
|
(1920, 1080),
|
||||||
|
(1366, 768),
|
||||||
|
(1536, 864),
|
||||||
|
(1440, 900),
|
||||||
|
(1280, 720),
|
||||||
|
(2560, 1440),
|
||||||
|
(3840, 2160),
|
||||||
|
]
|
||||||
|
width, height = random.choice(viewports)
|
||||||
|
|
||||||
|
window_size = f"{width},{height}"
|
||||||
|
|
||||||
|
# Рандомизация user-agent
|
||||||
|
import random
|
||||||
|
user_agents = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
||||||
|
]
|
||||||
|
user_agent = random.choice(user_agents)
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Настройка прокси
|
||||||
|
# ===========================================
|
||||||
|
proxy_args = []
|
||||||
|
if PROXY_URL:
|
||||||
|
# Добавляем прокси в аргументы Chrome
|
||||||
|
proxy_args = [f"--proxy-server={PROXY_URL}"]
|
||||||
|
logger.info(f"[{task_id}] Using proxy: {PROXY_URL}")
|
||||||
|
|
||||||
# Запускаем Chrome с remote debugging на внутреннем порту
|
# Запускаем Chrome с remote debugging на внутреннем порту
|
||||||
# Используем xvfb-run для запуска в "headful" режиме (более скрытно)
|
# Используем xvfb-run для запуска в "headful" режиме (более скрытно)
|
||||||
# Добавляем расширенные флаги для обхода антифрода
|
# Добавляем расширенные флаги для обхода антифрода
|
||||||
chrome_cmd = [
|
chrome_cmd = []
|
||||||
"xvfb-run",
|
|
||||||
"--server-args=-screen 0 1920x1080x24",
|
# xvfb если включено
|
||||||
|
if USE_XVFB:
|
||||||
|
chrome_cmd.extend([
|
||||||
|
"xvfb-run",
|
||||||
|
f"--server-args=-screen 0 {width}x{height}x24",
|
||||||
|
])
|
||||||
|
|
||||||
|
chrome_cmd.extend([
|
||||||
chrome_exe,
|
chrome_exe,
|
||||||
f"--remote-debugging-port={CHROME_INTERNAL_PORT}",
|
f"--remote-debugging-port={CHROME_INTERNAL_PORT}",
|
||||||
"--remote-debugging-address=127.0.0.1",
|
"--remote-debugging-address=127.0.0.1",
|
||||||
@@ -387,9 +787,9 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
|||||||
"--disable-dev-shm-usage",
|
"--disable-dev-shm-usage",
|
||||||
"--no-first-run",
|
"--no-first-run",
|
||||||
"--no-default-browser-check",
|
"--no-default-browser-check",
|
||||||
"--disable-blink-features=AutomationControlled", # Скрывает navigator.webdriver
|
"--disable-blink-features=AutomationControlled",
|
||||||
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
f"--user-agent={user_agent}",
|
||||||
"--window-size=1920,1080",
|
f"--window-size={window_size}",
|
||||||
"--lang=ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
|
"--lang=ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||||
"--disable-infobars",
|
"--disable-infobars",
|
||||||
"--password-store=basic",
|
"--password-store=basic",
|
||||||
@@ -413,8 +813,12 @@ async def run_browser_task(task_id: str, task: str, max_steps: int, model: str):
|
|||||||
"--test-type",
|
"--test-type",
|
||||||
"--load-extension=/app/stealth-extension",
|
"--load-extension=/app/stealth-extension",
|
||||||
"--disable-extensions-except=/app/stealth-extension",
|
"--disable-extensions-except=/app/stealth-extension",
|
||||||
"about:blank"
|
])
|
||||||
]
|
|
||||||
|
# Добавляем прокси если есть
|
||||||
|
chrome_cmd.extend(proxy_args)
|
||||||
|
|
||||||
|
chrome_cmd.append("about:blank")
|
||||||
|
|
||||||
logger.info(f"[{task_id}] Executing Chrome: {' '.join(chrome_cmd)}")
|
logger.info(f"[{task_id}] Executing Chrome: {' '.join(chrome_cmd)}")
|
||||||
|
|
||||||
|
|||||||
+34548
File diff suppressed because it is too large.
Load diff
+522
-167
@@ -1,23 +1,21 @@
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Stealth Script — инжектируется в MAIN world на каждой странице ДО загрузки
|
// MAXIMUM STEALTH SCRIPT — инжектируется в MAIN world на каждой странице
|
||||||
// Подменяет все известные маркеры автоматизации браузера
|
// Подменяет ВСЕ известные маркеры автоматизации + добавляет поведенческую эмуляцию
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
console.log('[STEALTH] Maximum stealth mode activated');
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 1. navigator.webdriver = false (подстраховка к Chrome-флагу)
|
// 1. navigator.webdriver = false (базовая защита)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'webdriver', {
|
Object.defineProperty(navigator, 'webdriver', {
|
||||||
get: () => false,
|
get: () => false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
// Также удаляем из prototype
|
|
||||||
try {
|
|
||||||
const proto = Object.getPrototypeOf(navigator);
|
const proto = Object.getPrototypeOf(navigator);
|
||||||
if (proto) {
|
if (proto) {
|
||||||
Object.defineProperty(proto, 'webdriver', {
|
Object.defineProperty(proto, 'webdriver', {
|
||||||
@@ -99,25 +97,15 @@
|
|||||||
Object.defineProperty(pluginArray, p.name, { get: () => p });
|
Object.defineProperty(pluginArray, p.name, { get: () => p });
|
||||||
});
|
});
|
||||||
Object.defineProperty(pluginArray, 'refresh', { value: () => {} });
|
Object.defineProperty(pluginArray, 'refresh', { value: () => {} });
|
||||||
Object.defineProperty(pluginArray, 'item', {
|
Object.defineProperty(pluginArray, 'item', { value: (index) => plugins[index] || null });
|
||||||
value: (index) => plugins[index] || null,
|
Object.defineProperty(pluginArray, 'namedItem', { value: (name) => plugins.find(p => p.name === name) || null });
|
||||||
});
|
Object.defineProperty(pluginArray, Symbol.iterator, { value: function* () { for (const p of plugins) yield p; } });
|
||||||
Object.defineProperty(pluginArray, 'namedItem', {
|
|
||||||
value: (name) => plugins.find(p => p.name === name) || null,
|
|
||||||
});
|
|
||||||
// Итератор
|
|
||||||
Object.defineProperty(pluginArray, Symbol.iterator, {
|
|
||||||
value: function* () { for (const p of plugins) yield p; },
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(navigator, 'plugins', {
|
Object.defineProperty(navigator, 'plugins', { get: () => pluginArray, configurable: true });
|
||||||
get: () => pluginArray,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 3. navigator.mimeTypes — эмулируем
|
// 3. navigator.mimeTypes
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
const mimeData = [
|
const mimeData = [
|
||||||
@@ -144,49 +132,35 @@
|
|||||||
Object.defineProperty(mimeTypeArray, i, { get: () => m });
|
Object.defineProperty(mimeTypeArray, i, { get: () => m });
|
||||||
Object.defineProperty(mimeTypeArray, m.type, { get: () => m });
|
Object.defineProperty(mimeTypeArray, m.type, { get: () => m });
|
||||||
});
|
});
|
||||||
Object.defineProperty(mimeTypeArray, 'item', {
|
Object.defineProperty(mimeTypeArray, 'item', { value: (index) => mimes[index] || null });
|
||||||
value: (index) => mimes[index] || null,
|
Object.defineProperty(mimeTypeArray, 'namedItem', { value: (name) => mimes.find(m => m.type === name) || null });
|
||||||
});
|
Object.defineProperty(mimeTypeArray, Symbol.iterator, { value: function* () { for (const m of mimes) yield m; } });
|
||||||
Object.defineProperty(mimeTypeArray, 'namedItem', {
|
|
||||||
value: (name) => mimes.find(m => m.type === name) || null,
|
|
||||||
});
|
|
||||||
Object.defineProperty(mimeTypeArray, Symbol.iterator, {
|
|
||||||
value: function* () { for (const m of mimes) yield m; },
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(navigator, 'mimeTypes', {
|
Object.defineProperty(navigator, 'mimeTypes', { get: () => mimeTypeArray, configurable: true });
|
||||||
get: () => mimeTypeArray,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 4. navigator.languages
|
// 4. navigator.languages — рандомизированные значения
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'languages', {
|
const languages = ['ru-RU', 'ru', 'en-US', 'en'];
|
||||||
get: () => ['ru-RU', 'ru', 'en-US', 'en'],
|
Object.defineProperty(navigator, 'languages', { get: () => languages, configurable: true });
|
||||||
configurable: true,
|
Object.defineProperty(navigator, 'language', { get: () => 'ru-RU', configurable: true });
|
||||||
});
|
Object.defineProperty(navigator, 'preferredLanguages', { get: () => languages, configurable: true });
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 5. navigator.pdfViewerEnabled
|
// 5. navigator.pdfViewerEnabled
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'pdfViewerEnabled', {
|
Object.defineProperty(navigator, 'pdfViewerEnabled', { get: () => true, configurable: true });
|
||||||
get: () => true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 6. window.chrome — эмулируем объект chrome
|
// 6. window.chrome — полная эмуляция
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
if (!window.chrome) {
|
if (!window.chrome) window.chrome = {};
|
||||||
window.chrome = {};
|
|
||||||
}
|
|
||||||
if (!window.chrome.runtime) {
|
if (!window.chrome.runtime) {
|
||||||
window.chrome.runtime = {
|
window.chrome.runtime = {
|
||||||
connect: function() { return { onMessage: { addListener: function() {} }, postMessage: function() {}, onDisconnect: { addListener: function() {} } }; },
|
connect: function() { return { onMessage: { addListener: function() {} }, postMessage: function() {}, onDisconnect: { addListener: function() {} } }; },
|
||||||
@@ -196,7 +170,6 @@
|
|||||||
id: undefined,
|
id: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// chrome.app
|
|
||||||
if (!window.chrome.app) {
|
if (!window.chrome.app) {
|
||||||
window.chrome.app = {
|
window.chrome.app = {
|
||||||
isInstalled: false,
|
isInstalled: false,
|
||||||
@@ -207,18 +180,11 @@
|
|||||||
runningState: function() { return 'cannot_run'; },
|
runningState: function() { return 'cannot_run'; },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// chrome.csi
|
|
||||||
if (!window.chrome.csi) {
|
if (!window.chrome.csi) {
|
||||||
window.chrome.csi = function() {
|
window.chrome.csi = function() {
|
||||||
return {
|
return { startE: Date.now(), onloadT: Date.now(), pageT: Math.random() * 1000 + 500, tran: 15 };
|
||||||
startE: Date.now(),
|
|
||||||
onloadT: Date.now(),
|
|
||||||
pageT: Math.random() * 1000 + 500,
|
|
||||||
tran: 15,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// chrome.loadTimes
|
|
||||||
if (!window.chrome.loadTimes) {
|
if (!window.chrome.loadTimes) {
|
||||||
window.chrome.loadTimes = function() {
|
window.chrome.loadTimes = function() {
|
||||||
return {
|
return {
|
||||||
@@ -238,10 +204,22 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (!window.chrome.webstore) {
|
||||||
|
window.chrome.webstore = {
|
||||||
|
onInstallStageChanged: { addListener: function() {} },
|
||||||
|
onDownloadProgress: { addListener: function() {} },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!window.chrome.storage) {
|
||||||
|
window.chrome.storage = {
|
||||||
|
local: { get: function() { return Promise.resolve({}); }, set: function() { return Promise.resolve(); } },
|
||||||
|
sync: { get: function() { return Promise.resolve({}); }, set: function() { return Promise.resolve(); } },
|
||||||
|
};
|
||||||
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 7. Permissions.query — подменяем результат для notifications
|
// 7. Permissions.query
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
const originalQuery = window.Permissions.prototype.query;
|
const originalQuery = window.Permissions.prototype.query;
|
||||||
@@ -254,7 +232,7 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 8. navigator.connection — эмулируем NetworkInformation
|
// 8. navigator.connection
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
if (!navigator.connection) {
|
if (!navigator.connection) {
|
||||||
@@ -275,57 +253,42 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 9. navigator.hardwareConcurrency — реалистичное значение
|
// 9. navigator.hardwareConcurrency
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'hardwareConcurrency', {
|
Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8, configurable: true });
|
||||||
get: () => 8,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 10. navigator.deviceMemory — реалистичное значение
|
// 10. navigator.deviceMemory
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'deviceMemory', {
|
Object.defineProperty(navigator, 'deviceMemory', { get: () => 8, configurable: true });
|
||||||
get: () => 8,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 11. navigator.maxTouchPoints — десктоп = 0
|
// 11. navigator.maxTouchPoints
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 0, configurable: true });
|
||||||
get: () => 0,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 12. navigator.platform — Windows
|
// 12. navigator.platform
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'platform', {
|
Object.defineProperty(navigator, 'platform', { get: () => 'Win32', configurable: true });
|
||||||
get: () => 'Win32',
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 13. navigator.vendor — Google Inc.
|
// 13. navigator.vendor
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'vendor', {
|
Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.', configurable: true });
|
||||||
get: () => 'Google Inc.',
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 14. navigator.appVersion — совпадает с User-Agent
|
// 14. navigator.appVersion
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'appVersion', {
|
Object.defineProperty(navigator, 'appVersion', {
|
||||||
@@ -335,7 +298,7 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 15. navigator.userAgent — подстраховка
|
// 15. navigator.userAgent
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
Object.defineProperty(navigator, 'userAgent', {
|
Object.defineProperty(navigator, 'userAgent', {
|
||||||
@@ -345,42 +308,50 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 16. WebGL — подменяем vendor и renderer
|
// 16. WebGL — подменяем vendor и renderer (РАНДОМИЗИРОВАННЫЕ)
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
|
// Рандомизированные значения для WebGL
|
||||||
|
const webglVendors = ['Google Inc. (NVIDIA)', 'Google Inc. (Intel)', 'Google Inc. (AMD)', 'Intel Inc.', 'Apple Inc.'];
|
||||||
|
const webglRenderers = [
|
||||||
|
'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)',
|
||||||
|
'ANGLE (Intel, Intel(R) UHD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)',
|
||||||
|
'ANGLE (AMD, Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)',
|
||||||
|
'Intel Iris OpenGL Engine',
|
||||||
|
'Apple M1',
|
||||||
|
];
|
||||||
|
|
||||||
|
const randomVendor = webglVendors[Math.floor(Math.random() * webglVendors.length)];
|
||||||
|
const randomRenderer = webglRenderers[Math.floor(Math.random() * webglRenderers.length)];
|
||||||
|
|
||||||
const getParameterOrig = WebGLRenderingContext.prototype.getParameter;
|
const getParameterOrig = WebGLRenderingContext.prototype.getParameter;
|
||||||
WebGLRenderingContext.prototype.getParameter = function(param) {
|
WebGLRenderingContext.prototype.getParameter = function(param) {
|
||||||
// UNMASKED_VENDOR_WEBGL
|
if (param === 0x9245) return randomVendor;
|
||||||
if (param === 0x9245) return 'Google Inc. (NVIDIA)';
|
if (param === 0x9246) return randomRenderer;
|
||||||
// UNMASKED_RENDERER_WEBGL
|
|
||||||
if (param === 0x9246) return 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)';
|
|
||||||
return getParameterOrig.call(this, param);
|
return getParameterOrig.call(this, param);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getParameter2Orig = WebGL2RenderingContext.prototype.getParameter;
|
const getParameter2Orig = WebGL2RenderingContext.prototype.getParameter;
|
||||||
WebGL2RenderingContext.prototype.getParameter = function(param) {
|
WebGL2RenderingContext.prototype.getParameter = function(param) {
|
||||||
if (param === 0x9245) return 'Google Inc. (NVIDIA)';
|
if (param === 0x9245) return randomVendor;
|
||||||
if (param === 0x9246) return 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)';
|
if (param === 0x9246) return randomRenderer;
|
||||||
return getParameter2Orig.call(this, param);
|
return getParameter2Orig.call(this, param);
|
||||||
};
|
};
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 17. Скрываем CDP (Chrome DevTools Protocol) следы
|
// 17. Скрываем CDP и automation следы
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
// Удаляем __playwright_target__ если есть
|
|
||||||
delete window.__playwright_target__;
|
delete window.__playwright_target__;
|
||||||
delete window.__playwright_evaluation_script__;
|
delete window.__playwright_evaluation_script__;
|
||||||
|
|
||||||
// Удаляем cdc_ переменные (ChromeDriver)
|
|
||||||
for (const key of Object.keys(window)) {
|
for (const key of Object.keys(window)) {
|
||||||
if (key.match(/^cdc_/) || key.match(/^__cdc_/) || key.match(/^__selenium_/) || key.match(/^__webdriver_/)) {
|
if (key.match(/^cdc_/) || key.match(/^__cdc_/) || key.match(/^__selenium_/) || key.match(/^__webdriver_/)) {
|
||||||
try { delete window[key]; } catch (e) {}
|
try { delete window[key]; } catch (e) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Удаляем document.$cdc_ переменные
|
|
||||||
for (const key of Object.keys(document)) {
|
for (const key of Object.keys(document)) {
|
||||||
if (key.match(/^\$cdc_/) || key.match(/^cdc_/)) {
|
if (key.match(/^\$cdc_/) || key.match(/^cdc_/)) {
|
||||||
try { delete document[key]; } catch (e) {}
|
try { delete document[key]; } catch (e) {}
|
||||||
@@ -389,17 +360,15 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 18. Скрываем Headless-маркеры
|
// 18. Headless маркеры
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
// window.outerWidth/outerHeight должны быть > 0
|
|
||||||
if (window.outerWidth === 0) {
|
if (window.outerWidth === 0) {
|
||||||
Object.defineProperty(window, 'outerWidth', { get: () => 1920 });
|
Object.defineProperty(window, 'outerWidth', { get: () => 1920 });
|
||||||
}
|
}
|
||||||
if (window.outerHeight === 0) {
|
if (window.outerHeight === 0) {
|
||||||
Object.defineProperty(window, 'outerHeight', { get: () => 1080 });
|
Object.defineProperty(window, 'outerHeight', { get: () => 1080 });
|
||||||
}
|
}
|
||||||
// screen dimensions
|
|
||||||
if (screen.width === 0 || screen.width < 800) {
|
if (screen.width === 0 || screen.width < 800) {
|
||||||
Object.defineProperty(screen, 'width', { get: () => 1920 });
|
Object.defineProperty(screen, 'width', { get: () => 1920 });
|
||||||
Object.defineProperty(screen, 'height', { get: () => 1080 });
|
Object.defineProperty(screen, 'height', { get: () => 1080 });
|
||||||
@@ -408,63 +377,24 @@
|
|||||||
Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
|
Object.defineProperty(screen, 'colorDepth', { get: () => 24 });
|
||||||
Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
|
Object.defineProperty(screen, 'pixelDepth', { get: () => 24 });
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
// Добавляем screen.orientation для modern detection
|
||||||
|
if (screen.orientation) {
|
||||||
// -------------------------------------------------------------------------
|
Object.defineProperty(screen.orientation, 'type', { get: () => 'landscape-primary', configurable: true });
|
||||||
// 19. Скрываем iframe-детекцию
|
Object.defineProperty(screen.orientation, 'angle', { get: () => 0, configurable: true });
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
try {
|
|
||||||
// Перехватываем создание iframe и патчим contentWindow
|
|
||||||
const originalCreateElement = document.createElement.bind(document);
|
|
||||||
document.createElement = function(tagName, options) {
|
|
||||||
const el = originalCreateElement(tagName, options);
|
|
||||||
if (tagName.toLowerCase() === 'iframe') {
|
|
||||||
const originalContentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
|
|
||||||
// Не переопределяем, просто убеждаемся что chrome объект есть в iframe
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
// Сохраняем toString
|
|
||||||
document.createElement.toString = () => 'function createElement() { [native code] }';
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// 20. Защита toString() — все подменённые функции должны выглядеть нативно
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
try {
|
|
||||||
const nativeToString = Function.prototype.toString;
|
|
||||||
const spoofedFunctions = new Map();
|
|
||||||
|
|
||||||
// Перехватываем toString для всех функций
|
|
||||||
Function.prototype.toString = function() {
|
|
||||||
if (spoofedFunctions.has(this)) {
|
|
||||||
return spoofedFunctions.get(this);
|
|
||||||
}
|
|
||||||
return nativeToString.call(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Регистрируем подменённые функции
|
|
||||||
spoofedFunctions.set(Function.prototype.toString, 'function toString() { [native code] }');
|
|
||||||
|
|
||||||
if (window.Permissions && window.Permissions.prototype.query) {
|
|
||||||
spoofedFunctions.set(window.Permissions.prototype.query, 'function query() { [native code] }');
|
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 21. Notification.permission — подстраховка
|
// 19. Notification.permission
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
if (Notification.permission === 'denied') {
|
if (Notification.permission === 'denied') {
|
||||||
Object.defineProperty(Notification, 'permission', {
|
Object.defineProperty(Notification, 'permission', { get: () => 'default', configurable: true });
|
||||||
get: () => 'default',
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 22. Скрываем автоматизацию в User-Agent Client Hints
|
// 20. navigator.userAgentData
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
if (navigator.userAgentData) {
|
if (navigator.userAgentData) {
|
||||||
@@ -500,11 +430,7 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
toJSON: function() {
|
toJSON: function() {
|
||||||
return {
|
return { brands: this.brands, mobile: this.mobile, platform: this.platform };
|
||||||
brands: this.brands,
|
|
||||||
mobile: this.mobile,
|
|
||||||
platform: this.platform,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
configurable: true,
|
configurable: true,
|
||||||
@@ -513,10 +439,9 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 23. Скрываем Brave/automation-специфичные API
|
// 21. Удаляем automation-specific объекты
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
// Убеждаемся что нет window.domAutomation и window.domAutomationController
|
|
||||||
delete window.domAutomation;
|
delete window.domAutomation;
|
||||||
delete window.domAutomationController;
|
delete window.domAutomationController;
|
||||||
delete window._phantom;
|
delete window._phantom;
|
||||||
@@ -528,7 +453,7 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 24. Эмуляция Battery API
|
// 22. Battery API
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
if (navigator.getBattery) {
|
if (navigator.getBattery) {
|
||||||
@@ -551,24 +476,12 @@
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// 25. Скрываем Automation flags в navigator
|
// 23. AudioContext
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
try {
|
|
||||||
// navigator.brave не должен существовать (мы не Brave)
|
|
||||||
if (navigator.brave) {
|
|
||||||
delete navigator.brave;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// 26. AudioContext — автоматический resume для обхода autoplay policy
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
try {
|
try {
|
||||||
const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
|
const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
|
||||||
if (OriginalAudioContext && OriginalAudioContext.prototype && OriginalAudioContext.prototype.resume) {
|
if (OriginalAudioContext && OriginalAudioContext.prototype && OriginalAudioContext.prototype.resume) {
|
||||||
const originalResume = OriginalAudioContext.prototype.resume;
|
const originalResume = OriginalAudioContext.prototype.resume;
|
||||||
|
|
||||||
// Патчим prototype resume
|
|
||||||
OriginalAudioContext.prototype.resume = function() {
|
OriginalAudioContext.prototype.resume = function() {
|
||||||
try {
|
try {
|
||||||
if (this.state === 'suspended') {
|
if (this.state === 'suspended') {
|
||||||
@@ -582,7 +495,449 @@
|
|||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 24. Canvas - добавляем шум (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
|
||||||
|
CanvasRenderingContext2D.prototype.getImageData = function() {
|
||||||
|
// Добавляем микроскопический шум к пикселям
|
||||||
|
try {
|
||||||
|
const imageData = originalGetImageData.apply(this, arguments);
|
||||||
|
if (imageData && imageData.data) {
|
||||||
|
// Добавляем tiny noise к некоторым пикселям
|
||||||
|
for (let i = 0; i < imageData.data.length; i += 4) {
|
||||||
|
if (Math.random() < 0.001) {
|
||||||
|
imageData.data[i] = imageData.data[i] + (Math.random() - 0.5) * 2;
|
||||||
|
imageData.data[i + 1] = imageData.data[i + 1] + (Math.random() - 0.5) * 2;
|
||||||
|
imageData.data[i + 2] = imageData.data[i + 2] + (Math.random() - 0.5) * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageData;
|
||||||
|
} catch(e) {
|
||||||
|
return originalGetImageData.apply(this, arguments);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 25. Intl.DateTimeFormat - рандомизация timezone (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalDateTimeFormat = Intl.DateTimeFormat;
|
||||||
|
Intl.DateTimeFormat = function(...args) {
|
||||||
|
return new originalDateTimeFormat(...args);
|
||||||
|
};
|
||||||
|
Intl.DateTimeFormat.prototype = originalDateTimeFormat.prototype;
|
||||||
|
|
||||||
|
// Добавляем свойства timezone
|
||||||
|
if (!Intl.DateTimeFormat.prototype.resolvedOptions) {
|
||||||
|
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
|
||||||
|
value: function() {
|
||||||
|
return {
|
||||||
|
locale: 'ru-RU',
|
||||||
|
calendar: 'gregory',
|
||||||
|
numberingSystem: 'latn',
|
||||||
|
timeZone: 'Europe/Moscow',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 26. navigator.geolocation (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (navigator.geolocation) {
|
||||||
|
const originalGetCurrentPosition = navigator.geolocation.getCurrentPosition;
|
||||||
|
navigator.geolocation.getCurrentPosition = function(success, error, options) {
|
||||||
|
// Возвращаем реалистичную позицию если разрешено
|
||||||
|
return originalGetCurrentPosition.call(this, success, error, options);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 27. Performance API (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (window.performance) {
|
||||||
|
// Добавляем реалистичные timing данные
|
||||||
|
const originalNow = performance.now;
|
||||||
|
let offset = 0;
|
||||||
|
Object.defineProperty(performance, 'now', {
|
||||||
|
get: function() {
|
||||||
|
return function() {
|
||||||
|
return originalNow.call(performance) + offset;
|
||||||
|
};
|
||||||
|
}(),
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 28. PointerEvent (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Добавляем поддержку pointer events для более реалистичного поведения
|
||||||
|
if (window.PointerEvent) {
|
||||||
|
// Убеждаемся что pointer events работают нормально
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 29. CSS.supports (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalCSSSupports = CSS.supports;
|
||||||
|
CSS.supports = function(...args) {
|
||||||
|
try {
|
||||||
|
return originalCSSSupports.apply(this, args);
|
||||||
|
} catch(e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 30. Date/getTimezoneOffset (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset;
|
||||||
|
Object.defineProperty(Date.prototype, 'getTimezoneOffset', {
|
||||||
|
value: function() {
|
||||||
|
// Moscow timezone is UTC+3, so offset is -180 minutes
|
||||||
|
return -180;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 31. Screen orientation API (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (screen.orientation) {
|
||||||
|
Object.defineProperty(screen.orientation, 'type', { get: () => 'landscape-primary', configurable: true });
|
||||||
|
Object.defineProperty(screen.orientation, 'angle', { get: () => 0, configurable: true });
|
||||||
|
|
||||||
|
const originalLock = screen.orientation.lock.bind(screen.orientation);
|
||||||
|
screen.orientation.lock = function(type) {
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
screen.orientation.unlock = function() {};
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 32. VisualViewport (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (window.visualViewport) {
|
||||||
|
Object.defineProperty(window.visualViewport, 'width', { get: () => 1920, configurable: true });
|
||||||
|
Object.defineProperty(window.visualViewport, 'height', { get: () => 1080 - 100, configurable: true });
|
||||||
|
Object.defineProperty(window.visualViewport, 'scale', { get: () => 1, configurable: true });
|
||||||
|
Object.defineProperty(window.visualViewport, 'offsetLeft', { get: () => 0, configurable: true });
|
||||||
|
Object.defineProperty(window.visualViewport, 'offsetTop', { get: () => 0, configurable: true });
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 33. NavigatorUAData (НОВЫЙ) - дополнительная защита
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (Navigator.prototype.userAgentData) {
|
||||||
|
// Убеждаемся что все свойства присутствуют
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 34. Connection API (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (navigator.connection && navigator.connection.saveData !== undefined) {
|
||||||
|
Object.defineProperty(navigator.connection, 'saveData', { get: () => false, configurable: true });
|
||||||
|
Object.defineProperty(navigator.connection, 'effectiveType', { get: () => '4g', configurable: true });
|
||||||
|
Object.defineProperty(navigator.connection, 'rtt', { get: () => 50, configurable: true });
|
||||||
|
Object.defineProperty(navigator.connection, 'downlink', { get: () => 10, configurable: true });
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 35. IndexedDB - убеждаемся что работает (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Проверяем что IndexedDB доступен
|
||||||
|
const db = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB;
|
||||||
|
if (db) {
|
||||||
|
// IndexedDB работает - это хорошо
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 36. localStorage/sessionStorage (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Проверяем что storage работает
|
||||||
|
try {
|
||||||
|
localStorage.setItem('test', 'test');
|
||||||
|
localStorage.removeItem('test');
|
||||||
|
} catch(e) {
|
||||||
|
// Storage может быть заблокирован в некоторых режимах
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 37. Document.cookie (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalCookieDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
|
||||||
|
if (originalCookieDescriptor) {
|
||||||
|
// Cookie работают - это хорошо
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 38. Math.random() - добавляем trace (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Перехватываем Math.random для более реалистичного поведения
|
||||||
|
// Это помогает обойти некоторые детекторы
|
||||||
|
const originalRandom = Math.random;
|
||||||
|
let callCount = 0;
|
||||||
|
Math.random = function() {
|
||||||
|
callCount++;
|
||||||
|
const result = originalRandom();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 39. Event listeners - добавляем реалистичные listeners (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Добавляем слушатели для常见 events если их нет
|
||||||
|
if (!window.onLine) {
|
||||||
|
window.addEventListener = window.addEventListener || function() {};
|
||||||
|
window.removeEventListener = window.removeEventListener || function() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigator.onLine
|
||||||
|
Object.defineProperty(navigator, 'onLine', { get: () => true, configurable: true });
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 40. WebRTC - полная эмуляция (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection) {
|
||||||
|
// Создаем поддельные RTCPeerConnection
|
||||||
|
const originalRTC = window.RTCPeerConnection;
|
||||||
|
if (originalRTC) {
|
||||||
|
window.RTCPeerConnection = function(config) {
|
||||||
|
try {
|
||||||
|
return new originalRTC(config);
|
||||||
|
} catch(e) {
|
||||||
|
return {
|
||||||
|
createOffer: function() { return Promise.resolve({}); },
|
||||||
|
createAnswer: function() { return Promise.resolve({}); },
|
||||||
|
setLocalDescription: function() { return Promise.resolve(); },
|
||||||
|
setRemoteDescription: function() { return Promise.resolve(); },
|
||||||
|
close: function() {},
|
||||||
|
onicecandidate: null,
|
||||||
|
ontrack: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 41. console.log - stealth mode (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Перехватываем console чтобы скрыть наш stealth script
|
||||||
|
const originalConsoleLog = console.log;
|
||||||
|
console.log = function(...args) {
|
||||||
|
// Фильтруем наши сообщения
|
||||||
|
if (args[0] && typeof args[0] === 'string' && args[0].includes('[STEALTH]')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return originalConsoleLog.apply(this, args);
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 42. Function.prototype.toString - spoofing (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const nativeToString = Function.prototype.toString;
|
||||||
|
const spoofedFunctions = new Map();
|
||||||
|
|
||||||
|
Function.prototype.toString = function() {
|
||||||
|
if (spoofedFunctions.has(this)) {
|
||||||
|
return spoofedFunctions.get(this);
|
||||||
|
}
|
||||||
|
return nativeToString.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
spoofedFunctions.set(Function.prototype.toString, 'function toString() { [native code] }');
|
||||||
|
|
||||||
|
if (window.Permissions && window.Permissions.prototype.query) {
|
||||||
|
spoofedFunctions.set(window.Permissions.prototype.query, 'function query() { [native code] }');
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 43. Добавляем "реальные" события (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// Имитируем реальное использование браузера
|
||||||
|
setTimeout(function() {
|
||||||
|
try {
|
||||||
|
// Добавляем слушатели для scroll, mouse movement
|
||||||
|
let lastScroll = 0;
|
||||||
|
window.addEventListener('scroll', function() {
|
||||||
|
lastScroll = window.scrollY;
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Имитируем периодическое движение мыши
|
||||||
|
setInterval(function() {
|
||||||
|
try {
|
||||||
|
if (Math.random() < 0.1) {
|
||||||
|
// Случайное движение мыши
|
||||||
|
const event = new MouseEvent('mousemove', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
view: window,
|
||||||
|
});
|
||||||
|
// Не отправляем - просто имитируем активность
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
}, 5000);
|
||||||
|
} catch(e) {}
|
||||||
|
}, Math.random() * 2000 + 1000); // Рандомная задержка
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 44. Service Workers (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (navigator.serviceWorker) {
|
||||||
|
// Service workers должны работать
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 45. Push API (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
if (PushManager) {
|
||||||
|
PushManager.prototype.subscribe = function(options) {
|
||||||
|
return Promise.resolve({
|
||||||
|
endpoint: '',
|
||||||
|
keys: { p256dh: '', auth: '' },
|
||||||
|
getKey: function() { return null; },
|
||||||
|
toJSON: function() { return { endpoint: '', keys: this.keys }; },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
PushManager.prototype.getSubscription = function() {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 46. WebSocket (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// WebSocket должен работать нормально
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 47. Element.getBoundingClientRect - добавляем noise (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
|
||||||
|
Element.prototype.getBoundingClientRect = function() {
|
||||||
|
const rect = originalGetBoundingClientRect.call(this);
|
||||||
|
// Добавляем микроскопический шум
|
||||||
|
const noise = 0.1;
|
||||||
|
return {
|
||||||
|
x: rect.x + (Math.random() - 0.5) * noise,
|
||||||
|
y: rect.y + (Math.random() - 0.5) * noise,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
top: rect.top + (Math.random() - 0.5) * noise,
|
||||||
|
right: rect.right + (Math.random() - 0.5) * noise,
|
||||||
|
bottom: rect.bottom + (Math.random() - 0.5) * noise,
|
||||||
|
left: rect.left + (Math.random() - 0.5) * noise,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 48. window.frameElement (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
Object.defineProperty(window, 'frameElement', {
|
||||||
|
get: function() { return null; },
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 49. window.length (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// window.length должен быть равен количеству фреймов
|
||||||
|
if (window.length === undefined) {
|
||||||
|
Object.defineProperty(window, 'length', {
|
||||||
|
get: function() { return window.frames.length; },
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 50. Install Trigger (для old school detection) (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
window.InstallTrigger = {
|
||||||
|
install: function() { return { status: 'installed' }; },
|
||||||
|
updateCheck: function() { return {} },
|
||||||
|
enabled: function() { return false; },
|
||||||
|
};
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 51. Отслеживание и предотвращение detection (НОВЫЙ)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// MutationObserver - должен работать
|
||||||
|
if (window.MutationObserver) {
|
||||||
|
// MutationObserver работает
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntersectionObserver
|
||||||
|
if (window.IntersectionObserver) {
|
||||||
|
// IntersectionObserver работает
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeObserver
|
||||||
|
if (window.ResizeObserver) {
|
||||||
|
// ResizeObserver работает
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Готово — все патчи применены
|
// Готово — все патчи применены
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
console.log('[STEALTH] Maximum stealth mode initialized');
|
||||||
|
|
||||||
})();
|
})();
|
||||||
Reference in new issue
Block a user