ZaStoGram — форк Telegram для Android с MTProxy FakeTLS, нативным WSS-транспортом, защитой приватности и обновлениями из Forgejo. https://t.me/zastogram
  • Java 59.6%
  • C++ 25.3%
  • C 10.6%
  • Kotlin 2.5%
  • Python 1.2%
  • Other 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
loop-uh f754cd6a32 Include MtProxyEndpointRecorder in native scan
Add ENDPOINT_RECORDER path and include MtProxyEndpointRecorder.cpp in native diagnostics. Expanded native_diagnostics signature to accept and scan the endpoint recorder, include its contents when collecting phase names and native constants, and update main() to read and pass the new file. Ensures MTProxy endpoint-recorder diagnostics are detected and matched against the contract phases.
2026-07-02 21:54:35 +03:00
.github/workflows Add proxy activation generation tracking and TCP failure disambiguation 2026-07-01 18:08:38 +03:00
buildSrc update to 12.8.0 (6913) 2026-06-16 19:04:08 +02:00
docs/superpowers/specs Delay prefetch during MTProxy startup and improve DNS outage handling 2026-06-27 20:33:01 +03:00
gradle/wrapper update to 12.0.0 (6163) 2025-09-01 19:12:49 +04:00
TMessagesProj Refactor MTProxy recorder and emoji animation scheduler 2026-07-02 21:01:55 +03:00
TMessagesProj_App Add ZaStoGram plugin engine with Chaquopy/Pine 2026-06-28 00:06:34 +03:00
TMessagesProj_AppHockeyApp Add ZaStoGram plugin engine with Chaquopy/Pine 2026-06-28 00:06:34 +03:00
TMessagesProj_AppHuawei Add ZaStoGram plugin engine with Chaquopy/Pine 2026-06-28 00:06:34 +03:00
TMessagesProj_AppStandalone Add DNS negative cache & socket cleanup 2026-06-29 22:49:16 +03:00
TMessagesProj_AppTests update to 12.8.0 (6913) 2026-06-16 19:04:08 +02:00
Tools Include MtProxyEndpointRecorder in native scan 2026-07-02 21:54:35 +03:00
.gitignore Move R8 minification to final APK stage 2026-07-01 18:57:58 +03:00
apkdiff.py update apkdiff.py apkfrombundle.py 2023-01-26 17:06:12 +04:00
apkfrombundle.py update apkdiff.py apkfrombundle.py 2023-01-26 17:06:12 +04:00
build.gradle Add ZaStoGram plugin engine with Chaquopy/Pine 2026-06-28 00:06:34 +03:00
Dockerfile update to 12.0.0 (6163) 2025-09-01 19:12:49 +04:00
gradle.properties Improve APK build cache diagnostics 2026-06-18 20:27:11 +03:00
gradlew first commit 2013-10-25 19:19:00 +04:00
LICENSE first commit 2013-10-25 19:19:00 +04:00
README.md Add proxy activation generation tracking and TCP failure disambiguation 2026-07-01 18:08:38 +03:00
settings.gradle update to 11.14.0 (6102) 2025-08-11 13:36:23 +04:00

ZaStoGram — Telegram для Android с усиленной маскировкой MTProxy

image

Экспериментальный форк официального Telegram для Android. Цель форка — сделать подключение к MTProxy FakeTLS (ee-секрет) менее похожим на стандартный Telegram MTProxy-трафик и ближе к обычному браузерному HTTPS.

Это не новый мессенджер и не отдельный протокол. База остаётся официальным Telegram Android, а основные изменения сосредоточены вокруг MTProxy/FakeTLS, отдельного WSS-транспорта и клиентских privacy/UX-настроек форка.

Простыми словами

  • MTProxy — прокси для Telegram.
  • FakeTLS — режим MTProxy, где начало соединения выглядит как TLS/HTTPS.
  • WSS — WebSocket-транспорт поверх настоящего TLS. В этом форке он живёт отдельно от MTProxy/FakeTLS и обычного SOCKS5.
  • DPI / ТСПУ — оборудование провайдера, которое классифицирует и режет трафик по признакам на проводе.
  • JA4 — отпечаток TLS ClientHello. По нему можно отличить настоящий браузер от синтетического FakeTLS, если ClientHello сделан плохо.

Проблема MTProxy не только в одном хеше. DPI может смотреть на TLS-рукопожатие, временные интервалы, размеры записей, количество одновременных соединений и поведение после рукопожатия. Поэтому здесь важна аккуратная маскировка без поломки MTProto.

Что сейчас реализовано

Где мы сейчас

Текущая архитектура разделяет сбои MTProxy по фазам, чтобы не лечить DNS или TCP через JA4:

  • host_resolve_failed — слой DNS/endpoint stability: sslip.io разбирается локально без DNS-запроса, регистр суффикса не важен, а доменные прокси могут использовать последний успешный IPv4 из кэша. Повторный холодный DNS lookup для того же host:port коротко склеивается, чтобы не стрелять пачкой одинаковых DNS-запросов;
  • tcp_not_connected — слой endpoint circuit-breaker: один плохой endpoint не должен получать пачку быстрых connect() от разных аккаунтов, DC и download/upload-потоков. Активный TCP-start gate держит только фазу connect() и освобождается сразу после socket_connected или закрытия сокета;
  • client_hello_sent_no_server_hello — слой phase-adaptive FakeTLS: следующий запуск осторожно меняет только рецепт рукопожатия, а не весь транспорт сразу;
  • mtproxy_packet_sent_no_response — слой обычного dd/legacy MTProxy: проблема уже после TCP и первого MTProxy-пакета, поэтому JA4/ClientHello тут не участвуют. Если первый MTProxy-пакет отправлен, но ответа нет, соединение закрывается отдельным коротким таймаутом этой фазы, а не висит до общего socket timeout.

GUI и logcat используют одни и те же строковые фазы. Если прокси зависает, первым делом смотрим именно фазу, а не общий текст "подключаемся".

DRS пока не первым: для него уже есть базовый runtime-режим и guard на завершённые FakeTLS-записи, но агрессивные профили размеров записей будем включать только после отдельной проверки record-boundary на всём пути build -> partial send -> complete TLS frame -> discard.

Быстрая проверка перед сборкой

Перед сборкой APK можно прогнать все статические guards по MTProxy-архитектуре одной командой:

python3 Tools/check_mtproxy_all.py

Этот suite проверяет FakeTLS-путь, профили JA4, фрагментацию ClientHello, паттерн открытия соединений, startup cover, data-aware IPT, endpoint resilience, границы завершённых FakeTLS-записей, dd/legacy lifecycle, live-статусы GUI, scheduler проверок прокси и анализатор логов. Он не заменяет установку APK и реальные логи, но быстро ловит разъезд слоёв в коде.

Для соседних поверхностей есть отдельные быстрые guards, которые не требуют полной Android-сборки:

python3 Tools/check_login_proxy_button.py
python3 Tools/check_single_active_presence.py
python3 Tools/check_background_network_policy.py
python3 Tools/check_hide_main_screen_stories.py
python3 Tools/check_zasto_edit_history_contract.py
python3 Tools/check_app_log_cleanup.py
python3 Tools/check_contacts_permission_prompt.py
python3 Tools/check_splash_icon.py
python3 Tools/check_settings_durov_links.py
python3 Tools/check_zapret_proxy_sponsor.py
python3 Tools/check_plugin_client_utils_contract.py
python3 Tools/check_plugin_python_deps.py
python3 Tools/check_plugin_utils_javadoc.py
python3 Tools/check_build_apk_workflow.py

Пользовательские функции форка

Помимо MTProxy-слоя в клиенте есть отдельные пользовательские изменения:

  • экран ZaSto Приватность с переключателями, которые по умолчанию включают клиентские override'ы: сохранять удалённые сообщения, сохранять самоуничтожающиеся / view-once материалы, хранить локальную историю редактирования сообщений, разрешать сохранение и пересылку защищённого контента и историй, разрешать скриншоты, не отправлять уведомление о скриншоте в секретных чатах и отключать рекламные/промо-поверхности Telegram;
  • настройка чатов Скрыть истории на главном экране: она прячет stories row, кнопку создания истории и hint только на главном списке диалогов, не выключая сторис глобально;
  • настройка уведомлений Всегда держать сеть в фоне: клиент не вызывает свой native_pauseNetwork() при уходе приложения в фон. Это помогает не ронять живые подключения самим приложением, но не отменяет системные ограничения Android по батарее и процессам;
  • отдельная строка Zapret VPNs / Спонсор прокси в верхней части списка чатов с переходом на zapretvpns_bot. Она управляется тем же общим анти-рекламным переключателем: если Отключить рекламу включено, локальная sponsor-строка тоже скрывается;
  • дополнительный блок в настройках со ссылками Наш канал, Наш VPN и Донаты & поддержать;
  • раздел Плагины в настройках: установка Python .plugin-файлов, совместимых с базовой моделью exteraGram, включение/выключение, удаление и отдельные экраны настроек плагинов;
  • экран входа показывает кнопку прокси сразу в обычном login-flow и при отмене удаления аккаунта, даже если прокси ещё не настроен;
  • открытие контактов больше не вызывает автоматический lifecycle-показ объяснения про доступ к контактам;
  • только выбранный аккаунт отправляет online presence. Фоновый keep-awake не должен превращать остальные аккаунты в постоянно online;
  • Android 12+ splash использует логотип форка со стаканом, а не стандартный самолётик Telegram;
  • файловые логи приложения чистятся на старте процесса до первой новой записи, чтобы свежий запуск не смешивался со старыми локальными логами.

Плагины exteraGram

В ZaStoGram встроен Python-движок плагинов для .plugin-файлов, близких к формату exteraGram. Он живёт внутри основного Android-клиента, а не как отдельный бот или внешний скрипт:

  • CPython запускается через Chaquopy в модуле TMessagesProj; текущий runtime — Python 3.11;
  • пользовательские .plugin-файлы ставятся из интерфейса приложения и хранятся в data-директории приложения, а не зашиваются в APK;
  • каждый плагин — один Python-файл с метаданными вроде __id__, __name__, __version__, __min_version__, __icon__ и классом-наследником BasePlugin;
  • жизненный цикл плагина: on_plugin_load(), on_plugin_unload() и create_settings() для собственного экрана настроек;
  • настройки плагинов рендерятся нативными Telegram-ячейками через ui.settings (Header, Switch, Input, Selector, Text, Divider) и сохраняются отдельно для каждого id плагина;
  • доступны helper-модули client_utils, android_utils, hook_utils, file_utils, ui.alert и ui.bulletin;
  • client_utils экспортирует send_request() и RequestCallback для exteraGram-плагинов, которые импортируют request callback вручную;
  • в APK заранее кладутся Python-зависимости requests, Pillow/PIL и pyfiglet, потому что runtime-установки через pip на устройстве нет;
  • хуки Java-методов работают через Pine с Xposed-совместимым API: MethodHook, XposedHook, hook_method() и hook_all_constructors();
  • для сетевых запросов сейчас подключён post-response hook: post_request_hook() может пропустить, заменить или отменить ответ через HookStrategy.

Путь в приложении: Настройки -> Плагины -> + -> выбрать .plugin файл. После установки плагин можно открыть, включить/выключить, удалить или настроить, если сам плагин отдаёт модель настроек.

Это совместимость с реализованной здесь базовой поверхностью exteraGram, а не обещание полной поддержки каждого публичного плагина. Если внешний плагин использует API, которого нет в TMessagesProj/src/main/python или Java-мостах org.telegram.plugins, его нужно адаптировать.

Отдельный WSS-транспорт

WSS — это не MTProxy FakeTLS и не Python/локальный relay. В форке он вынесен в нативный путь WssTransport.cpp и управляется отдельным состоянием. На первом запуске режим по умолчанию становится официальным WSS Telegram:

  • режим WSS выбирается в списке прокси отдельной секцией: Выкл, официальный WSS Telegram или свой WSS gateway;
  • официальный WSS использует kws2.web.telegram.org и kws4.web.telegram.org для поддержанных DC2/DC4 и не требует собственного сервера;
  • WSS-настройки сохраняются отдельно от обычного currentProxy, включая wssHost, wssPath, wssUseForMiniApps и выбранный SOCKS5 upstream;
  • если WSS включён, legacy proxy path отключается, а MTProxy-настройки не показываются как активные для этого режима;
  • официальный WSS использует реальный TLS/WebSocket upgrade и каталог Telegram WSS route'ов, а для DC без стабильного WSS route может уйти в обычный fallback;
  • выбранный SOCKS5 для WSS подключается до TLS/WebSocket как upstream. Это не то же самое, что "SOCKS внутри WSS", и не должно перетирать обычный список прокси Telegram;
  • для mini apps есть отдельный wssUseForMiniApps hook, который использует тот же выбранный WSS SOCKS upstream, а не случайный legacy-прокси.

Путь MTProxy FakeTLS от tsrman-коммита

В активном пути транспорта сохранено поведение, близкое к рабочему tsrman/tg-варианту:

  • ClientHello держится в отдельном буфере до полной отправки: если send() отправил только часть данных, остаток досылается на следующем EPOLLOUT;
  • по умолчанию фаза данных использует стабильное фиксированное ограничение 2878 байт;
  • дополнительные слои фазы данных включаются только через явные настройки GUI и не меняют MTProto-формат.

Это важно для надёжности: сначала соединение должно стабильно работать, и только потом можно включать дополнительные слои и сравнивать результат на конкретном провайдере или прокси.

Стабильный выбор TLS-профиля

Для MTProxy FakeTLS выбранный TLS-профиль теперь не меняется хаотично на каждое соединение. Профиль выбирается стабильно по адресу прокси, секрету и локальной соли.

В автоматическом пуле сейчас два профиля, которые показали себя наиболее безопасными для текущей диагностики:

  • Firefox Android;
  • Yandex.

Так разные установки и разные прокси могут получать разные сетевые отпечатки, но один конкретный адрес прокси не прыгает между профилями каждую секунду. Android Chrome, Android OkHttp и Desktop Firefox оставлены в ручном режиме для сравнительных тестов. Android OkHttp пока не входит в Auto, потому что этот профиль нужно дополнительно проверить на стабильность с разными MTProxy.

В настройках списка прокси добавлен выбор JA4 / TLS-профиль.

  • Auto — режим по умолчанию. Клиент сам выбирает стабильный профиль для конкретного адрес:порт:секрет;
  • Auto rotate — стабильный профиль для адреса прокси, но после подозрительного FakeTLS-сбоя клиент переключает этот адрес на другой профиль из безопасного пула. Ошибки до открытия TCP не вращают JA4, потому что ClientHello в таком случае ещё не участвовал. dropped_early_after_appdata ускоряет fallback и endpoint-backoff, но тоже не вращает JA4: первые MTProto-данные уже пошли, значит проблему надо искать в post-handshake lifecycle, размерах и таймингах, а не в ClientHello. Поздний dropped_after_appdata остаётся диагностикой позднего разрыва без смены рецепта;
  • Chrome, Ffox A, OkHttp, Firefox, Yandex — ручной режим для тестов. В этом режиме один выбранный профиль принудительно используется для всех FakeTLS-прокси, а служебная проверка прокси использует тот же профиль.

Это нужно, чтобы на одном APK быстро сравнивать, какой отпечаток лучше проходит у конкретного провайдера или набора прокси, не пересобирая приложение.

Защитные проверки ClientHello

Перед отправкой ClientHello проверяется на базовую совместимость с MTProxy:

  • корректная TLS-запись и длины;
  • ожидаемое смещение списка наборов шифров;
  • первый шифр не из GREASE относится к TLS 1.3 TLS_AES_*;
  • наличие SNI-домена из ee-секрета;
  • размер в пределах серверного лимита.

Это защищает от красивого, но несовместимого ClientHello.

Рандомизация внутри ClientHello

Оставлены низкорисковые элементы маскировки:

  • случайные поля ClientHello;
  • случайные ECH-данные там, где профиль их использует;
  • случайная цель расширения заполнения вместо старого фиксированного размера;
  • безопасная генерация GREASE-значений без выхода за границы массива.

Управляемая мягкая фрагментация ClientHello

В настройках списка прокси добавлен переключатель Мягкая фрагментация ClientHello. Он действует только на MTProxy FakeTLS (ee-секреты).

Когда режим включён, уже собранный и проверенный ClientHello отправляется не одним логическим залпом, а двумя небольшими TCP-отправками с коротким неблокирующим интервалом. TLS-структура, SNI, HMAC и MTProto-трафик после подключения не меняются.

Режим нужен для A/B-тестов против DPI: можно собрать один APK и сравнивать прокси с фрагментацией и без неё прямо из GUI, не откатывая код. По умолчанию режим выключен, чтобы рабочий транспорт оставался максимально близким к стабильному пути.

Управляемый размер TLS-записей

В настройках списка прокси добавлен режим Размер TLS-записей. Он действует только после успешного FakeTLS-рукопожатия и меняет размер следующих ApplicationData-записей, в которые упаковывается MTProto-трафик.

Доступные варианты:

  • Выкл — старое стабильное поведение с верхней границей 2878 байт;
  • Мягко — выбирает размер из небольшого набора безопасных значений;
  • Разно — использует более широкий диапазон размеров.

Этот слой не перепаковывает MTProto и не держит отдельное состояние "продолжения" TLS-записи. Сначала собирается полная TLS-запись в pending-буфере, потом она досылается до конца, и только после полной отправки полезная нагрузка выбрасывается из очереди MTProto. Это снижает риск поломать partial-send и позволяет тестировать размер записей прямо из GUI. Завершённая FakeTLS-запись логируется как mtproxy_data tls_frame_complete; этот marker пишется только после полной отправки frame и списания payload из очереди.

Управляемые тайминги данных

В настройках списка прокси добавлен режим Тайминги трафика. Он добавляет короткую неблокирующую паузу только между полностью отправленными FakeTLS ApplicationData-записями.

Доступные варианты:

  • Выкл — без пауз;
  • Мягко — короткие интервалы для осторожного A/B-теста;
  • Средне — более заметное растягивание последовательности записей.

Неполная TLS-запись не задерживается и всегда досылается целиком. Это важно, чтобы не ломать MTProto и не превращать сетевой поток в состояние, где часть payload уже выкинута из очереди, но не дошла до сокета.

Стартовая маскировка MTProxy

В настройках списка прокси добавлен режим Стартовая маскировка MTProxy. Он включается только для ee FakeTLS и только после успешного server_hello_hmac_ok, то есть рукопожатие ClientHello/ServerHello не трогается.

Доступные варианты:

  • Выкл — не меняет первые ApplicationData-записи;
  • Мягко — временно включает осторожные размеры TLS-записей и короткие неблокирующие паузы для первых записей;
  • Строго — сильнее меняет первые записи, но только в коротком стартовом окне.

Этот слой не создаёт фоновые проверки прокси, не открывает сторонние сайты и не генерирует отдельный "веб-трафик". Он меняет только упаковку первых реальных MTProto-данных внутри уже установленного FakeTLS-соединения, чтобы старт меньше выглядел как типичный MTProxy-залп.

Мягкое сокращение параллельных каналов MTProxy

Когда включён MTProxy, загрузки и отправки файлов больше не создают отдельный набор параллельных каналов скачивания и выгрузки для каждого запроса. Они мягко сводятся к основному каналу скачивания/выгрузки, а прямые соединения без MTProxy сохраняют прежнее распределение.

В настройках списка прокси слой называется Меньше параллельных каналов MTProxy. По умолчанию он включён, но его можно выключить без пересборки, если нужно сравнить поведение конкретного прокси.

Это не настоящее мультиплексирование протокола и не перепаковка MTProto. Это первый безопасный слой против сетевого шаблона, где клиент почти одновременно создаёт несколько похожих потоков к одному прокси. Цель — уменьшить залп и проверить эффект на блокировках, не меняя формат MTProto-пакетов и не ломая сервер MTProxy.

Паттерн MTProxy-подключений

Для ee FakeTLS добавлен управляемый планировщик открытия соединений к одному адресу прокси. Он ограничивает не скорость уже установленного MTProto-трафика, а только залп одинаковых попыток DNS/resolve -> connect -> ClientHello -> server_hello_hmac_ok.

Ключ планировщика: адрес прокси:порт:SNI.

В настройках списка прокси слой называется Паттерн подключений MTProxy. Это не флажок, а режим:

  • Обычный — без очереди, максимально близко к базовому транспорту;
  • Мягкий — лёгкий контроллер допуска: до двух активных FakeTLS-рукопожатий на адрес прокси без штрафной паузы;
  • Браузерный — сначала одно настоящее FakeTLS-рукопожатие, после первого успешного server_hello_hmac_ok плавно допускается второй слот; тяжёлые соединения скачивания и выгрузки стартуют позже обычных и медийных;
  • Тихий — одно активное рукопожатие и более медленная последовательная выдача слотов; после недавних успехов допускается два;
  • Строгий — одно активное рукопожатие, ограничение быстрых последовательных стартов connect(), более длинные паузы для очереди и короткая ограниченная штрафная пауза после таймаута или зависания рукопожатия.

Старый флаг mtProxyHandshakeAdmission мигрируется в Мягкий режим, если он был включён. Новый режим хранится как mtProxyConnectionPatternMode, передаётся в родной C++ слой через setProxySettings и перезапускает соединения при изменении.

Когда режим не Обычный, задача планировщика:

  • начинать delegate DNS-разрешение доменных ee FakeTLS-прокси только после допуска в очередь, чтобы DNS/TCP-отказы не обходили браузерный backoff;
  • держать максимум два активных FakeTLS-рукопожатия в Мягком режиме и максимум одно в Браузерном/Тихом/Строгом, кроме недавних успешных server_hello_hmac_ok, после которых Браузерный и Тихий режимы могут открыть второй слот;
  • ставить новые рукопожатия в очередь по приоритету: Generic/Temp -> GenericMedia -> Push -> Download -> Upload;
  • не пропускать служебную проверку прокси через жёсткую очередь, чтобы не получать ложный результат "прокси не работает" из-за ожидания слота;
  • освобождать слот сразу после server_hello_hmac_ok, чтобы загрузка и отправка файлов после подключения шли параллельно как раньше.

Зависания в фазе client_hello_sent в Обычном и Мягком режимах в основном логируются. В Браузерном, Тихом и Строгом режимах они дополнительно дают короткую ограниченную штрафную паузу для низкоприоритетных попыток, чтобы клиент не создавал быстрый повторяемый шум на одном адресе прокси. Успешный server_hello_hmac_ok сбрасывает штраф, чтобы рабочий прокси не оставался искусственно медленным.

Отдельно обрабатываются повторы tcp_not_connected, когда попытка умерла до отправки ClientHello. Общий endpoint-слой ставит короткую паузу для всех режимов, включая Обычный, а Браузерный, Тихий и Строгий режимы делают эту паузу заметнее. Это снижает шторм повторных DNS/connect() по одному адресу прокси, но не смешивает сетевое душение до ClientHello с JA4/ServerHello-ошибками.

Это сделано против узнаваемого залпового шаблона, когда клиент почти одновременно открывает много одинаковых FakeTLS-соединений к одному прокси. Приложение не управляет TCP SYN-пакетами напрямую: оно управляет стартом connect(), а retransmit на уровне TCP делает ОС. Поэтому строгий режим приближает поведение к редким открытиям соединений, но не обещает точное правило "один SYN в секунду".

Устойчивость endpoint'а

Поверх планировщика FakeTLS добавлен общий слой устойчивости endpoint'а. Он работает для всех MTProxy-секретов, включая обычные dd/legacy, и не меняет формат MTProto-пакетов.

У слоя два разных ключа, потому что разные фазы видят разный объём данных:

  • сетевой ключ host:port используется для DNS, tcp_not_connected и ограничения одновременных TCP connect-попыток. На этой фазе ещё нет ClientHello, SNI и JA4, поэтому нельзя дробить state по секрету или TLS-профилю;
  • FakeTLS-рецепт использует ключ host:port:тип секрета:SNI. Этот state нужен только после post-ClientHello сбоев, где уже действительно участвовали SNI, ClientHello и выбранный TLS-профиль.

Что делает слой:

  • запоминает последний успешно разрешённый IPv4 для доменного прокси по ключу host:port и может использовать его как fallback, если следующий DNS lookup не дал адрес. Этот DNS-кэш намеренно не зависит от ee/dd, секрета и SNI: если один вариант того же прокси уже дал IP, другой вариант может переиспользовать его без лишнего DNS-шума;
  • коротко склеивает одновременные холодные DNS-разрешения одного host:port: первая попытка идёт в resolver, следующие ждут dns_coalesce_wait, затем сначала пробуют свежий кэш и только потом снова обращаются к DNS;
  • после фаз host_resolve_failed, tcp_not_connected, client_hello_sent_no_server_hello и mtproxy_packet_sent_no_response ставит короткую паузу именно на том уровне, где произошёл сбой: DNS/TCP ошибки идут на host:port, а FakeTLS/post-ClientHello ошибки — на host:port:тип секрета:SNI. В Обычном/Мягком режимах пауза короче, в Браузерном/Тихом/Строгом — длиннее;
  • не запускает пачку одновременных TCP connect-попыток к одному MTProxy host:port. tcp_connect_gate удерживает только pre-TCP фазу и освобождается на socket_connected или closeSocket, поэтому уже установленная MTProto сессия и передача файлов не ограничиваются этим слоем;
  • для FakeTLS после повторяющихся post-ClientHello сбоев мягко меняет следующий рецепт запуска: сначала включает мягкую фрагментацию ClientHello, затем переключает Auto/Auto rotate на другой Android TLS-профиль, затем включает более тихий темп открытия соединений. Ручной профиль из GUI не перетирается;
  • сбрасывает штраф после реального ответа прокси: server_hello_hmac_ok, first_tls_app_recv или first_mtproxy_packet_recv.

Это не DRS и не мультиплексирование. Слой не трогает уже установленный MTProto-трафик и не держит слот до конца TCP-сессии. Его задача — уменьшить шум повторных попыток и дать GUI/логам понятную фазу: endpoint_cooldown, dns_cache_hit, dns_cache_store, phase_adaptive_recipe.

Java-планировщик проверок хранит свой endpoint-state для фоновых и ручных proxy-check. Автопереключение прокси учитывает этот state: fallback-ротация не выбирает endpoint, который находится в свежем failure-backoff после проверки или в свежем native endpoint_cooldown. Короткая пауза после успешной проверки нужна только чтобы не перепроверять рабочий прокси слишком часто; она не считается failure-backoff и не мешает выбрать свежий рабочий прокси. Если scheduler пропускает повторную проверку из-за backoff, строка прокси получает тот же видимый статус endpoint_cooldown, чтобы список не выглядел как "не проверено" во время намеренной паузы.

Java backoff использует ту же фазовую идею ключей: pre-TLS, dd no-response и ранние post-appdata срывы (host_resolve_failed, tcp_not_connected, network_block_suspected, tcp_connected_no_pong, mtproxy_packet_sent_no_response, dropped_early_after_appdata) пишутся в host:port state, чтобы не проверять тот же сетевой endpoint пачкой через другой секрет. При этом активные proxy-check запросы склеиваются по полному ключу host:port:username:password:secret, чтобы результат проверки одного секрета не применился к другому секрету.

Если текущее подключение публикует terminal-фазу вроде host_resolve_failed, tcp_not_connected, client_hello_sent_no_server_hello или mtproxy_packet_sent_no_response, автопереключение не ждёт общий длинный таймаут статуса "подключаемся". Оно ставит короткий delayed fallback и выбирает другой кандидат через те же фильтры freshness/failure/cooldown/backoff. Это особенно важно для dd/legacy MTProxy: mtproxy_packet_sent_no_response возникает уже после TCP и первого MTProxy-пакета, поэтому JA4 и ClientHello тут не помогут. Native-слой дополнительно ограничивает ожидание первого ответа для plain MTProxy: если после отправки первого MTProxy-пакета входящих байтов нет, фаза быстро становится terminal-ошибкой и попадает в общий backoff/fallback, вместо того чтобы несколько секунд выглядеть как обычное "подключаемся". Такая live terminal-фаза также попадает в endpoint-state ProxyCheckScheduler, чтобы fallback не перескочил на другую строку списка с тем же endpoint'ом. Повтор той же фазы в пределах короткого окна de-dup не увеличивает backoff повторно: это защищает от двойного счёта, когда native сначала публикует фазу, а затем почти сразу закрывает socket с тем же diagnostic. Общее состояние Telegram Connected считается более слабым сигналом, чем свежая конкретная proxy-фаза: generic Connected не затирает ни живые стадии вроде client_hello_sent / first_tls_app_sent, ни terminal-ошибки вроде host_resolve_failed, tcp_not_connected или mtproxy_packet_sent_no_response в GUI. Ошибку снимает только конкретный успешный proxy-check или новая data-path фаза этого же прокси, например first_tls_app_recv / first_mtproxy_packet_recv; server_hello_hmac_ok остаётся стадией рукопожатия, а не доказательством рабочего трафика. Явный новый старт подключения из GUI или rotation публикует connect_start, чтобы пользователь видел новую попытку вместо старой terminal-ошибки выбранной строки, но не стирает свежий usable success: в течение hold-окна после first_tls_app_recv / first_mtproxy_packet_recv локальный connect_start удерживается как live telemetry. Автопереключение также не выбирает кандидата со свежей незавершённой live-фазой: старый available=true не должен перебивать client_hello_sent, first_tls_app_sent или ожидание первого plain MTProxy-ответа.

Диагностика запуска подключения

Добавлены диагностические точки для MTProxy-подключения:

  • connect_start;
  • endpoint_cooldown;
  • tcp_connect_gate;
  • dns_coalesce_wait;
  • dns_cache_hit, dns_cache_store;
  • phase_adaptive_recipe;
  • socket_connected;
  • client_hello_sent;
  • server_hello_hmac_ok;
  • server_hello_hmac_timeout, если ServerHello структурно получен, но HMAC не совпал за короткое диагностическое окно;
  • on_connected;
  • first_tls_app_sent, когда первый MTProto-пакет ушёл внутри TLS ApplicationData;
  • first_tls_app_recv, когда от сервера пришла первая полезная TLS ApplicationData;
  • mtproxy_disconnect с причиной, состоянием и флагами первой MTProto-активности внутри TLS (first_tls_sent, first_tls_recv);
  • admission_grant, admission_queue, admission_release, admission_disabled, admission_freeze_detected, admission_freeze_observed, admission_tcp_failure_cooldown, admission_failure_cooldown. В admission-маркерах есть admission_mode и connection_pattern.

Эти же live-фазы прокидываются в Java для выбранного текущего прокси. Поэтому окно прокси показывает не один общий текст "подключаемся", а конкретную стадию: ожидание слота, DNS-разрешение, открытие TCP, отправка ClientHello, ожидание ServerHello, запуск MTProto и первые MTProto-данные. Служебные проверки списка прокси не меняют live-стадию выбранного подключения. Native live-stage передаётся с ключом host:port:тип секрета:SNI для MTProxy (host:port остаётся только fallback для сетевых слоёв), поэтому поздняя фаза от другого секрета на том же IP:port не перетирает текущий выбранный прокси.

Это нужно, чтобы отличать DPI-обрыв от багов в нашем транспортном пути.

Архитектура проверки прокси

Проверка прокси разделена на три слоя, чтобы GUI не запускал лишние соединения и не спорил с native-состоянием:

  • ProxyCheckScheduler в Java принимает ручные проверки из bottom sheet, фоновые проверки из списка прокси и проверки ротации. Он держит один активный check, склеивает одинаковые endpoint'ы, хранит владельцев проверки и отменяет active check через cancelProxyCheck().
  • Native-слой ConnectionsManager завершает проверку через единый finishProxyCheck: успех по TL_pong, сетевой обрыв, timeout, start-fail и явная отмена проходят через одну точку очистки request/token/socket.
  • Tools/analyze_mtproxy_markers.py сводит Java scheduler, native proxy-check, rotation и FakeTLS-маркеры в один отчёт. Важный verdict: connected_without_socket_connected_marker означает, что лог дошёл до on_connected, но в этом срезе нет маркера socket_connected; такой случай нельзя считать TCP-недоступностью.

В отчёте есть два разных среза:

  • FakeTLS endpoint phases — фазы настоящего FakeTLS-подключения по адресу прокси: TCP/connect, отправка ClientHello, проверка ServerHello/HMAC, первая MTProto ApplicationData и поздний disconnect;
  • Plain MTProxy lifecycle — обычные dd/legacy MTProxy-секреты отдельно от FakeTLS. Этот срез группируется по endpoint, аккаунту, DC и типу соединения, чтобы отличать "проверка прокси доступна" от ситуации, где, например, Generic получает RPC-ответы, а Download много отправляет и почти ничего не получает;
  • Native endpoint outcomes — результат служебной проверки прокси. Значение fail:tcp_not_connected относится к TCP/connect-слою, а fail:tcp_connected_no_pong означает, что TCP открылся, но MTProxy ping не завершился.

Tools/collect_mtproxy_logs.ps1 строит основной mtproxy_markers.txt только по текущему logcat.txt. Сырые device logs всё ещё подтягиваются в папку сессии, но не смешиваются с live-статистикой, чтобы старые файлы не портили выводы.

Единая карта ошибок прокси

Статус прокси теперь передаётся не числовыми кодами, а строковой фазой. Один и тот же словарь используется в native-логе, Java scheduler, GUI и анализаторе:

  • tcp_not_connected — TCP-соединение не открылось;
  • tcp_connected_no_pong — TCP открылся, но MTProxy ping не завершился;
  • mtproxy_packet_sent_no_response — для обычного dd/legacy MTProxy: TCP открылся, первый MTProxy-пакет отправлен, но ответ сервера не пришёл;
  • endpoint_cooldown — клиент выдерживает короткую паузу перед новой попыткой к этому же endpoint'у после свежего фазового сбоя;
  • tcp_connect_gate — клиент ждёт, пока закончится уже активная TCP connect-попытка к тому же MTProxy endpoint'у;
  • dns_coalesce_wait — клиент ждёт уже запущенное DNS-разрешение того же доменного прокси, чтобы не создавать пачку одинаковых DNS-запросов;
  • dns_cache_hit / dns_cache_store — клиент использовал или обновил сохранённый IP доменного прокси;
  • phase_adaptive_recipe — клиент изменил следующий мягкий рецепт FakeTLS после сбоя на фазе рукопожатия или первых данных: фрагментация ClientHello, Android TLS-профиль или темп открытия соединения;
  • network_block_suspected — повторный TCP-срыв на том же endpoint, похожий на фильтрацию пути до ответа MTProxy;
  • waiting_tcp — текущий выбранный прокси ещё ждёт TCP-подключение;
  • client_hello_sent_no_server_hello — ClientHello отправлен, валидный ServerHello не пришёл;
  • server_hello_hmac_mismatch — ServerHello пришёл, но не прошёл HMAC-проверку MTProxy;
  • post_handshake_no_appdata — рукопожатие прошло, но первые MTProto-данные не пришли;
  • dropped_early_after_appdata — первые MTProto-данные пришли, но соединение быстро умерло; это post-handshake lifecycle/endpoint-сигнал, а не повод менять JA4;
  • dropped_after_appdata — соединение поднялось и отвалилось уже после первых MTProto-данных.

Единый источник GUI-текстов — ProxyCheckDiagnostics. Поэтому список прокси, bottom sheet проверки и логи используют одинаковые имена фаз. Это нужно, чтобы по экрану и по logcat было видно, где именно умирает прокси и похоже ли это на недоступный сервер, несовместимость транспорта или фильтрацию пути.

Корректная досылка неполной TLS-записи

Стартовый FakeTLS ClientHello отправляется через отдельный ожидающий буфер: если неблокирующий send() принял только часть hello, остаток досылается на следующем EPOLLOUT, а состояние client_hello_sent логируется только после полной отправки.

Если peer закрыл TCP-соединение (recv() == 0), сокет закрывается сразу с меткой recv_eof, а не ждёт общего таймаута подключения.

Если после полной отправки ClientHello нет корректного server_hello_hmac_ok за время freeze-таймера, попытка закрывается с меткой server_hello_timeout_close, чтобы не держать мёртвое рукопожатие до общего таймаута.

Проверка HMAC для ServerHello теперь привязана к границам TLS-записей, а не к случайному размеру одного recv(). Это сохраняет совместимость с официальным MTProxy, где ответ состоит из ServerHello + ChangeCipherSpec + ApplicationData, и не ломается на telemt-подобных ответах с дополнительными профилированными ApplicationData-записями в серверном handshake flight.

После установленного FakeTLS-соединения reader принимает только полезные ApplicationData-записи MTProto, но корректно пропускает служебный ChangeCipherSpec, не отдаёт пустые TLS-записи в MTProto-парсер и закрывает соединение с отдельной меткой tls_alert, если peer прислал TLS Alert.

Для обёрнутых TLS-записей добавлена очередь ожидающей отправки записи: если send() отправил только часть TLS-записи, остаток досылается как продолжение той же записи, а полезная нагрузка MTProto не выбрасывается раньше полной отправки. Успешная досылка обновляет таймер активности соединения, чтобы активная отправка не считалась простым бездействием.

Сборка

Закоммиченного APK здесь намеренно нет. APK собирается из исходников локально или через GitHub Actions.

  1. Получи api_id и api_hash на my.telegram.org.
  2. Укажи свои значения в проекте Telegram Android.
  3. Подставь свой ключ подписи релизной сборки.
  4. Собери через Android Studio или запусти workflow Build ZaStoGram APK в GitHub Actions.

GitHub Actions настроен на ccache с CCACHE_COMPILERCHECK=content, чтобы повторные сборки нативного кода были заметно быстрее после прогрева кэша. Workflow собирает отдельные standalone APK под ABI:

  • ZaStoGram-standalone-arm64-v8a.apk;
  • ZaStoGram-standalone-armeabi-v7a.apk;
  • ZaStoGram-standalone-x86.apk;
  • ZaStoGram-standalone-x86_64.apk.

После успешного запуска создаётся отдельный prerelease для этого run'а, и APK прикладываются к нему напрямую. GitHub artifacts всё ещё скачиваются как ZIP, а release asset — это уже сам .apk.

На время диагностики MTProxy GitHub Actions включает сетевые логи Telegram прямо в обычных ABI-артефактах. После установки такого APK можно собрать маркеры:

D:\bin\platform-tools\adb.exe devices -l

Из WSL:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$(wslpath -w Tools/collect_mtproxy_logs.ps1)" -Adb "D:\bin\platform-tools\adb.exe" -Package org.zastogram.messenger -Seconds 180

Во время записи надо открыть приложение и повторить подключение к MTProxy. Главный файл для разбора: mtproxy_markers.txt. Скрипт также создаёт mtproxy_analysis.txt: он группирует попытки по ConnectionSocket и показывает, где остановилось подключение: TCP/connect, отправка ClientHello, проверка server_hello_hmac_ok, переход в on_connected или первые TLS ApplicationData-записи MTProto.

Рядом создаётся mtproxy_runtime_contract.txt. Это строгая проверка live-лога: в mtproxy_markers.txt должны быть transport_state=..., endpoint_handshake_ok и endpoint_data_path_success, причём endpoint_data_path_success не может идти с reason=server_hello_hmac_ok. endpoint_data_path_success должен появляться только после первого first_tls_app_recv или first_mtproxy_packet_recv на том же native connection: успешный server_hello_hmac_ok сам по себе подтверждает только рукопожатие, а не рабочий data-path. Если надо перепроверить уже сохранённую сессию вручную:

python3 Tools/verify_mtproxy_runtime_logs.py mtproxy-logs-live/<session>

В отчёте есть блок Layer recommendations. Он не делает вид, что автоматически доказал DPI, а раскладывает фазы по слоям ремонта: dns_endpoint_stability для DNS/TCP до ClientHello, faketls_handshake_recipe для pre-ServerHello FakeTLS-сбоев, plain_dd_endpoint_backoff для обычных dd no-response и faketls_data_path для обрывов после завершённых FakeTLS ApplicationData. В этот же endpoint-слой попадают native proxy-check отказы fail:tcp_not_connected и fail:tcp_connected_no_pong, чтобы GUI-ошибка "TCP не открылся" учитывалась в общей картине, даже если полноценная ConnectionSocket-попытка ещё не дошла до FakeTLS.

Для сравнения профилей и endpoint'ов рядом создаются CSV:

  • mtproxy_attempts.csv — каждая FakeTLS-попытка: endpoint, профиль, фаза отказа, задержка до HMAC, close/error и набор увиденных маркеров;
  • mtproxy_endpoint_profile_stats.csv — агрегат по endpoint + profile: количество попыток, процент успеха, топ отказов, медиана HMAC и максимальный burst рукопожатий за 1 и 5 секунд. В этом же файле есть tls_frames_completed, чтобы отличать обрывы до data-path от обрывов после реально завершённых FakeTLS ApplicationData-записей. Для длинных сессий счётчик берётся из итогового mtproxy_disconnect tls_frames_completed=..., поэтому анализатор не зависит только от первых подробных per-frame markers;
  • mtproxy_plain_account_stats.csv — отдельный срез обычных dd/legacy MTProxy по account/DC/type: первый пакет отправлен, пришёл ли первый ответ, были ли invalid_packet_length, auth_404 и ранние disconnect;
  • mtproxy_proxy_check_stats.csv — агрегат native proxy-check по endpoint: ok, tcp_not_connected, tcp_connected_no_pong и close-reasons. Этот файл нужен, когда GUI показывает "TCP не открылся" ещё до полноценной FakeTLS или MTProto-попытки;
  • mtproxy_scheduler_stats.csv — агрегат Java scheduler по endpoint: enqueue, start, finish_ok, finish_fail, backoff, skip_backoff, finish_keep_connected и phase_*. Этот файл показывает, создаёт ли список прокси лишний шум проверками или, наоборот, правильно сдерживает повторные попытки после сетевых фаз.

Именно эти файлы нужны, чтобы отделить нашу ошибку транспорта от внешней блокировки или нестабильного прокси. Перед публичным релизом это нужно выключить обратно, чтобы не оставлять подробные сетевые логи в обычной сборке.

Как пользоваться

  1. Подними MTProxy с FakeTLS вне зоны блокировки.
  2. Используй ee-секрет с доменом.
  3. В приложении добавь MTProto-прокси обычным способом: Настройки -> Данные и память -> Прокси -> Добавить прокси -> MTProto.

Маскировка из этого форка применяется именно к FakeTLS/ee-секрету. Для обычных dd-секретов и других вариантов без FakeTLS эти изменения не дают того же эффекта.

Для WSS режима открой: Настройки -> Данные и память -> Прокси -> WSS-транспорт. Официальный WSS использует kws2/kws4.web.telegram.org для DC2/DC4, не требует MTProxy-секрета и не требует своего сервера. Для своего gateway укажи host/port/path, а SOCKS5 upstream выбери отдельно в WSS-секции списка прокси, если он нужен.

Для плагинов exteraGram открой: Настройки -> Плагины -> + и выбери локальный .plugin файл. Ставь только доверенные плагины: они выполняются внутри процесса приложения и могут рефлексией или hooks менять поведение клиента.

Честные ограничения

  • SNI не ротируется сам по себе. Он берётся из ee-секрета.
  • Фаза данных пока не имитирует настоящий HTTP/2 или HTTP/1.1.
  • WSS — отдельный транспорт, а не волшебная замена MTProxy. Официальные WSS route'ы ограничены теми DC, где они реально поддержаны; для остальных путей нужен fallback или свой gateway.
  • Управляемый размер TLS-записей и тайминги данных включаются вручную и нужны для диагностики, а не как доказанная универсальная защита.
  • Стартовая маскировка действует только в начале установленного FakeTLS-сеанса и не добавляет внешний браузерный трафик.
  • Мягкое сокращение каналов уменьшает параллельность download/upload при активном MTProxy, но не превращает MTProto в браузерный HTTP-трафик.
  • Планировщик рукопожатий выключен по умолчанию и включается в GUI. Даже во включённом виде он ограничивает только фазу открытия FakeTLS-соединения и не маскирует всю статистику установленного MTProto-трафика.
  • ZaSto Приватность — это клиентские локальные override'ы. Они не меняют серверную политику Telegram и не являются полноценным архивным хранилищем для всех видов историй, сторис и прочих ephemeral-сценариев.
  • Совместимость с плагинами exteraGram покрывает реализованные helper-модули, lifecycle, settings, post-response hooks и Xposed-style method hooks, но не гарантирует работу любого публичного .plugin без адаптации.
  • Плагины выполняются внутри процесса приложения. Ошибочный или вредоносный плагин может ломать UI, сетевую логику или приватность аккаунта.
  • Фоновый keep-awake отключает паузу сети на стороне клиента, но Android всё равно может ограничивать процесс системными настройками батареи.
  • Это не гарантия вечного обхода блокировок: DPI-правила меняются.
  • Это неофициальный форк, использовать его нужно с пониманием рисков.

Потом реализуем

  • Расширить DRS из простого runtime-режима в фазовую модель: разные профили для старта, интерактивного трафика, простоя и передачи файлов, защита от повторяющихся шаблонов и сброс состояния после долгой паузы. Базовый guard на цепочку partial send -> complete TLS frame -> discard payload -> next record уже есть; следующий шаг — использовать его для более смелых DRS-профилей.
  • Расширить IPT из простых неблокирующих пауз между TLS-записями в модель межпакетных интервалов с разными правилами для рукопожатия, поддержания соединения, интерактивного трафика и передачи файлов. Этот слой должен быть data-aware: если есть ожидающие данные, разрешены только короткие burst-паузы между уже завершёнными TLS-записями; длинный idle-режим не должен задерживать активный MTProto-трафик.
  • Для startup-паттерна использовать ту же дисциплину, что в TeleMT-плане: сначала фиксировать измеримые фазы и инварианты, затем включать адаптацию. Любая новая задержка должна иметь источник сигнала: есть ожидающие данные, нет ожидающих данных, первый ответ получен, endpoint в cooldown. Случайные паузы без такого сигнала считаются небезопасными для MTProto.
  • Больше стабильных браузерных профилей из telemt/tdlib-obf: Android Brave, дополнительные Chrome/Firefox/Yandex-варианты, Windows/iOS профили, но только через защитные тесты совместимости с MTProxy.
  • Политика ECH с учётом маршрута и автоматическим отключением неудачного варианта, чтобы не долбить сеть ECH-профилем, если конкретный маршрут его режет.
  • Ограничение MSS как отдельный эксперимент после проверки фрагментации ClientHello: оно меняет TCP-поведение ниже уровня FakeTLS и поэтому требует отдельного сравнения с VPN/без VPN.
  • Маскировка прикладного уровня: обрамление HTTP/2 или HTTP/1.1 поверх TLS-подобного транспорта. Это самый глубокий и самый рискованный слой, его нельзя делать косметически.
  • Заполняющий трафик во время простоя и маскировка жизненного цикла соединений, если они не будут ломать MTProto и батарею на Android.

Основа и благодарности

  • DrKLO/Telegram — официальный Telegram Android.
  • tsrman/tg — рабочая база изменений FakeTLS, JA4 и временных интервалов.
  • telemt/tdlib-obf — идеи и референсы по профилям маскировки, DRS/IPT и реестру профилей.