
В свете последних новостей вокруг Telegram провела некоторые эксперименты с протоколом MTProxy.
Основная идея: сделать ПО, выглядящее для Telegram-клиента как MTProxy-сервер, и осуществляющее дальнейший обмен данными со сторонними MTProxy-серверами.
В идеале, эти сторонние серверы должны обнаруживаться автоматически, и переключение между ними тоже должно происходить автоматически.
Постановка задачи
Код пишется на Python. Как экспериментальный вариант для быстрой реализации сойдёт, а если ПО окажется востребованным, то можно будет переписать на Си.
От протокола MTProxy реализуется только та часть, которая относится к соединениям, имитирующим TLS (FakeTLS, ключ прокси начинается с “ee”). Другие виды соединений кажутся более уязвимыми к DPI-вредительству, и оттого не очень полезными.
Предполагается, что ПО запускается на компьютере. Проверка работоспособности осуществляется в клиентах Telegram Desktop и Unigram под Windows.
С точки зрения клиента ПО выглядит как обычный MTProxy-сервер, подключение осуществляется штатным способом. Приняв соединение от клиента, ПО тут же пытается установить соединение со сторонним MTProxy-сервером. Если это удалось, то далее ПО все приходящие от клиента данные пересылает на сторонний сервер и наоборот, все приходящие от стороннего сервера данные пересылает на клиент. При этом происходит перекодирование, так как протокол шифрованный.
Если установить соединение со сторонним сервером не удалось, либо если соединение прервалось, соединение с клиентом тут же закрывается. Ожидаем, что обнаружив это, клиент Telegram пробует пересоединиться с нашим ПО, и всё повторяется.
Сторонний MTProxy-сервер выбирается как один из списка. Этот список может быть либо задан заранее, либо скачиваться с заданного URL.
Тестовый стенд
Для экспериментов был нужен заведомо работающий прокси. Самый надёжный вариант — развернуть его на своей же машине. Был взят mtg (https://github.com/9seconds/mtg), который запускался через mtg run config.toml, а конфигурационный файл config.toml был копией example.config.toml с минимальными изменениями:
secret = "ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d" bind-to = "127.0.0.1:3128" [network] proxies = [ "socks5://127.0.0.1:9150" ] [defense.blocklist] enabled = false
В такой конфигурации mtg принимает соединения на порту 3128 по протоколу MTProxy и обеспечивает обмен с серверами Telegram через SOCKS5-прокси на порту 9150.
Висящий запущенным Tor Browser как раз поднимает SOCKS5-прокси на порту 9150, которое может использоваться и другими приложениями. В нашем случае он нужен для обеспечения надёжной связи с серверами Telegram в обстановке DPI-вредительства.
О ключах прокси
Ключи прокси бывают:
“простые” — 32 шестнадцатеричных цифры ключа (т.е. 16 байтов)
“dd” — сначала буквы “dd”, далее 32 шестнадцатеричных цифры ключа
“ee” — сначала буквы “ee”, далее 32 шестнадцатеричных цифры ключа, далее закодированное в шестнадцатеричном виде имитируемое доменное имя (Telegram Desktop хочет, чтобы в нём было не менее четырёх букв после раскодирования)
“странные” — формат не соответствует ни одному из вышеперечисленных
При соединении клиент выбирает используемый протокол в зависимости от вида ключа. Ключи “ee” соответствуют протоколу с использованием FakeTLS. Исключительно им мы и уделим внимание.
Остальные виды ключей соответствуют “голому” протоколу MTProxy, без обёртки в TLS. Формат “dd” предполагает обфускацию, но, говорят, сейчас это не очень работоспособно. Со “странными” форматами я не разбиралась, там бывает что-то похожее на Base64. Протокол, по-видимому, тоже “голый”.
Несмотря на сомнения, отметим, что, не-“ee” ключи довольно распространены. При парсинге одного из списков прокси из 125 записей обнаружилось: “простых” - 16, “dd” - 29, “ee” - 34, “странных” - 46. Наводит на мысли, что раз их делают, то какой-то смысл в них, возможно, есть.
О протоколе MTProxy с FakeTLS
Документация от разработчиков Telegram вызывает печаль, поэтому, возможно, этот параграф стоило бы развернуть в отдельную статью. Пока же кратко об основных принципах.
В варианте с FakeTLS протокол MTProxy мимикрирует под HTTPS (HTTP/2 через TLS 1.2).
Клиент посылает TLS-пакет
Client Hello. По сути, важны там только полеRandomи расширениеserver_name.Randomиспользуется для хранения дайджеста, вычисляемого на основе содержимого пакета, 16-байтного ключа прокси и текущего времени. Сервер проверяет его соответствие.server_nameсодержит имитируемое доменное имя, и сервер может сверить его соответствие тому, что указано в текстовом представлении ключа (хотя особого смысла в этой сверке нету).Прокси-сервер посылает сразу три TLS-пакета:
Server Hello,Change Cipher Spec,Application Data. Тут важно полеRandom, используемое для хранения дайджеста, вычисляемого на основе содержимого всех трёх пакетов, а также дайджеста из пакетаClient Hello. Не очень понятно, почему для вычисления используется не толькоServer Hello, а все три пакета. Также не ясно, всегда ли их обязано быть именно три и именно таких.Далее клиент и сервер условно-бесконечно гоняют туда-сюда пакеты
Application DataиChange Cipher Spec. В первый оборачиваются данные “голого” протокола MTProxy, второй, по-видимому, просто игнорируется.


Содержимое первого пакета Application Data (который приходит совместно с Server Hello) игнорируется. Во втором пакете Application Data первые 64 байта совместно с 16-байтным ключом прокси используются для генерации ключей, используемых для шифрования остатков второго пакета, а также содержимого всех последующих пакетов Application Data.
Принцип работы ПО
ПО выступает в роли посредника между клиентом Telegram и сторонним MTProxy-сервером. Таким образом, дя клиента оно выглядит как MTProxy-сервер, а для стороннего сервера — как клиент.
ПО ожидает входящие соединения от клиента. Для каждого принятого соединения:
от клиента принимается пакет
Client Hello, осуществляется его валидацияв пакете заменяется поле расширения
server_name, пересчитывается полеRandomустанавливается соединение со сторонним прокси-сервером (про принцип выбора из списка - см. ниже)
изменённый пакет
Client Helloотправляется серверуот сервера принимается пакет
Server Hello(плюс ещё два - см. предыдущий параграф), осуществляется его валидацияв пакете пересчитывается поле
Randomизменённый пакет
Server Hello(плюс ещё два) отправляется клиентудалее в цикле, в направлениях от клиента к серверу и от сервера к клиенту пересылаются пакеты
Application DataиChange Cipher Spec-Change Cipher Specнапрямую,Application Dataпосле расшифрования и последующего зашифрования нужным ключомпри обрыве соединения с сервером, либо при получении невалидных данных соединения с клиентом и с сервером разрываются; предполагается, что в этом случае клиент повторит попытку установки соединения
Выбор прокси из списка
Простейший алгоритм выбора прокси-сервера из имеющегося списка - просто выбирать следующий по списку, а когда список закончится, возвращаться в начало. На практике это не очень хорошо работает, т.к. в публичных списках работающими оказываются лишь немногие прокси, и при разрыве соединения пройдёт значительное время, пока будет идти перебор серверов.
На данный момент реализован следующий алгоритм. При получении от сервера валидного пакета Server Hello увеличивается на 1 счётчик для данного сервера. Когда устанавливается новое соединение с клиентом и требуется выбрать прокси-сервер из списка, берётся какой-то из серверов, имеющих положительное значение счётчика, а счётчик уменьшается на 1. Если нету ни одного положительного счётчика, сервер выбирается по простейшему алгоритму.
Такой алгоритм позволяет при случайном разрыве соединения пытаться установить соединение в первую очередь с теми серверами, с которыми уже случались успешные соединения.
О проблеме “fe02”
Про это уже много писали — статья на Хабре, pull request к официальному клиенту и commit с исправлением
Суть проблемы — в Client Hello хотели из клиента передавать расширение TLS encrypted_client_hello с кодом 0xfe0d, но из-за ошибки в коде передавали несуществующее 0xfe02, по которому злоумышленники и могли отследить, что это именно Telegram (см. выше на картинке среди обведённого).
В ПО добавила workaround для починки пакета. Однако, никакой разницы не обнаружила. Те серверы, которые блокировались по DPI (в логах это выглядит как разрыв соединения после отправки Client Hello на сервер), так и продолжили блокироваться. Видимо, в моём случае либо блокируется по IP, либо DPI реагирует на другие признаки. Тут можно продолжать эксперименты. Менять можно практически все поля, благо, для протокола MTProxy в Client Hello ничего, кроме поля Random, не важно.
Возможные проблемы
При использовании сторонних прокси-серверов возможны проблемы:
ненадёжность (и непредсказуемость) соединения
низкая (и непредсказуемая) скорость
утечка метаданных к владельцу стороннего сервера (хотя для самих сообщений обещают шифрование)
у прокси-серверов есть возможность принудительно добавлять “спонсорский канал”, а при автоматическом переключении прокси это ещё и происходит внезапно (но, хотя бы, их не будет более одного, т.к. при отключении от прокси соответствующий канал пропадает)
ну и звонки в Telegram ходят мимо прокси, поэтому если поломаны, то не починятся
Как это пощупать своими руками
Репозиторий проекта тут: https://codeberg.org/bokurwa/mtjumper-py.git
Нужен Python.
git clone https://codeberg.org/bokurwa/mtjumper-py.git cd mtjumper-py pip install pycryptodome python mtjumper.py
Если заглянуть в исходники, то можно увидеть там такие строки, которые может оказаться полезным поменять:
OUR_CONFIG_URL = "tg://proxy?server=127.0.0.1&port=13128&type=mtproto&secret=ee0000000000000000000000000000000078787878" TARGET_CONFIG_URLS = [ "tg://proxy?server=127.0.0.1&port=3128&type=mtproto&secret=ee367a189aee18fa31c190054efd4a8e9573746f726167652e676f6f676c65617069732e636f6d", "@ https://raw.githubusercontent.com/SoliSpirit/mtproto/refs/heads/master/all_proxies.txt’, ]
OUR_CONFIG_URL — ссылка, которую можно открыть в Telegram, чтобы не вводить руками. Тут можно поменять порт, если не устраивает 13128, и адрес на 0.0.0.0, чтобы к нашему прокси можно было обращаться с других машин в локальной сети.
TARGET_CONFIG_URLS — список ссылок на прокси-серверы. Если в начале стоит @, то это ссылка для скачивания списка прокси (в текущей версии список скачивается только один раз при старте). По умолчанию там забиты URL тестового стенда и URL одного из списков прокси.
Литература
Комментарии (15)

dimonz80
25.05.2026 11:37
КДПВ 
Bobr-Kurwa Автор
25.05.2026 11:37Постеснялась картинку с членами в ленту выкатывать. Мало ли, кто на работе читает, а тут они :)
А в целом картинка отображает суть проекта с перепрыгиванием с одной прокси на другую

K0Jlya9
25.05.2026 11:37Зачем делать один mtproxy вместо нескольких, что бы товарищу майору удобнее было банить что ли.

rufus20145
25.05.2026 11:37Проект из времени, когда десктопный клиент еще не умел автоматически переключаться между добавленными прокси? Что ж, теперь он это умеет и смысла в таких прокладках нет.
А ещё, полагаю, точно такое же переключение можно сделать на базе haproxy с роутингом по sni. Только надо для всех прокси указать не их адреса, а адрес haproxy.

Bobr-Kurwa Автор
25.05.2026 11:37Добавленные прокси довольно быстро протухают.
Идея прокладки в автоматизации. Чтобы не заниматься каждый день добавлением новых прокси и чисткой старых.
Ну и дополнительно, не все клиенты умеют автопереключаться. Unigram, кажется, не умеет.
Хотя пока статья в песочнице лежала, я уже немножко притормозила с доработкой проекта, т.к. есть стабильно работающий Тор, и оказалось проще его использовать как socks proxy. Это на данный момент так, посмотрим, что будет дальше.

andynvkz
25.05.2026 11:37ну во первых встроенное переключение тупит по причине нет таймаута на подключения, во вторых прокси больше не работают, даже нет проверки подключения, сразу не доступен
Rubilnik
А я просто добавил несколько прокси в клиент телеграма и установил auto switch на 15 секунд. Так что если один становится недоступен - подключается другой...
dvkozyr
А у вас они не протухают наглухо через час, через день? У меня через неделю ни одного рабочего - опять надо лезть искать рабочие и вручную их добавлять. Вот бы это автоматизировать, но клиент только переключаться по списку умеет, да и то как-то ненадежно. Может автор эту проблему снаружи решает?
Rubilnik
Нет, потому что я пользуюсь личным прокси, а в ротации стоят socks на localhost для работы через альтернативные обходичики на время включения белых списков.
supercat1337
Личные прокси блокируют? Долго живут?
K0Jlya9
Видимо зависит от места. У меня тут не блокируют даже чистый ваиргард, не то что какие то прокси.
Rubilnik
Wireguard чистый до домашнего ip работает кстати нормально, чем тоже иногда пользуюсь.
Rubilnik
Не знаю от чего зависит, сижу я и несколько родственников на одном и том же ip уже больше полугода)
Для телеграма телемт использую, а для остального awg 2.0 (он же в роутере через podkop) и vless +warp если надо немного чище ip на выходе, но последняя связка бывает тормознуто себя ведёт. Может быть ещё из-за того, что там reality на icloud сделан и к последнему не всегда нормально подключается.
А ещё забыл про slipstream для белых списков и yandex cloud bridge (через cloud functions).
Сейчас перечитываю это все и в недоумении когда выход в интернет стал настолько сложным...
Bobr-Kurwa Автор
Идея проекта именно в том, что общедоступные прокси протухают.
И хочется сделать "волшебную кнопку", чтобы всё работало. Не только для айтишников. (Хотя да, прототип сделан на Python, так что всё равно для айтишников получилось.)
DmitryOlenin
В iOS, например, нет переключателя AutoSwitch вообще :(