- Java 59.6%
- C++ 25.3%
- C 10.6%
- Kotlin 2.5%
- Python 1.2%
- Other 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Introduce MtProxyProbeCoordinator and wire probe-keyed probe coordination into native and Java stacks. Probe coordinator owns FakeTLS recipe progression, grease probing state, and terminal/working decisions. ConnectionSocket gained probe join/wait logic, timers, and probe-key bookkeeping; ServerHello wait EOF/timeout diagnostics split (faketls_server_hello_wait_timeout / server_closed_after_client_hello). JNI wrapper and ConnectionsManager API extended to pass probeKey; Java runtime/telemetry updated to carry probeKey. Added offline GeoIP asset support and live proxy speed sampling UI, plus build/verification tools and guards updated to validate the new probe coordinator behavior. |
||
| .github/workflows | ||
| buildSrc | ||
| docs/superpowers/specs | ||
| gradle/wrapper | ||
| TMessagesProj | ||
| TMessagesProj_App | ||
| TMessagesProj_AppHockeyApp | ||
| TMessagesProj_AppHuawei | ||
| TMessagesProj_AppStandalone | ||
| TMessagesProj_AppTests | ||
| Tools | ||
| .gitignore | ||
| apkdiff.py | ||
| apkfrombundle.py | ||
| build.gradle | ||
| Dockerfile | ||
| gradle.properties | ||
| gradlew | ||
| LICENSE | ||
| README.md | ||
| settings.gradle | ||
ZaStoGram — Telegram для Android с усиленной маскировкой MTProxy
Экспериментальный форк официального 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_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; - хуки 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 выбирается в списке прокси отдельной секцией:
Выкл, официальный WSS Telegram или свой WSS gateway; - 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 есть отдельный
wssUseForMiniAppshook, который использует тот же выбранный 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-разрешение доменных
eeFakeTLS-прокси только после допуска в очередь, чтобы 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.
- Получи
api_idиapi_hashна my.telegram.org. - Укажи свои значения в проекте Telegram Android.
- Подставь свой ключ подписи релизной сборки.
- Собери через 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 от обрывов после реально завершённых FakeTLSApplicationData-записей. Для длинных сессий счётчик берётся из итогового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_*. Этот файл показывает, создаёт ли список прокси лишний шум проверками или, наоборот, правильно сдерживает повторные попытки после сетевых фаз.
Именно эти файлы нужны, чтобы отделить нашу ошибку транспорта от внешней блокировки или нестабильного прокси. Перед публичным релизом это нужно выключить обратно, чтобы не оставлять подробные сетевые логи в обычной сборке.
Как пользоваться
- Подними MTProxy с FakeTLS вне зоны блокировки.
- Используй
ee-секрет с доменом. - В приложении добавь MTProto-прокси обычным способом: Настройки -> Данные и память -> Прокси -> Добавить прокси -> MTProto.
Маскировка из этого форка применяется именно к FakeTLS/ee-секрету. Для обычных
dd-секретов и других вариантов без FakeTLS эти изменения не дают того же
эффекта.
Для WSS режима открой: Настройки -> Данные и память -> Прокси -> WSS-транспорт. Официальный WSS не требует 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 и реестру профилей.