В свете последних новостей вокруг 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, второй, по-видимому, просто игнорируется.

Пример Client Hello
Пример Client Hello
Пример Server Hello
Пример Server Hello

Содержимое первого пакета 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 одного из списков прокси.

Литература

  1. Telegram научился маскироваться под HTTPS

  2. Исходники прокси от автора статьи [1]

  3. RFC 5246 — The Transport Layer Security (TLS) Protocol. Version 1.2

  4. RFC 6066 — Transport Layer Security (TLS) Extensions: Extension Definitions

  5. RFC 9849 — TLS Encrypted Client Hello

  6. MTProto transports

Комментарии (15)


  1. Rubilnik
    25.05.2026 11:37

    А я просто добавил несколько прокси в клиент телеграма и установил auto switch на 15 секунд. Так что если один становится недоступен - подключается другой...


    1. dvkozyr
      25.05.2026 11:37

      А у вас они не протухают наглухо через час, через день? У меня через неделю ни одного рабочего - опять надо лезть искать рабочие и вручную их добавлять. Вот бы это автоматизировать, но клиент только переключаться по списку умеет, да и то как-то ненадежно. Может автор эту проблему снаружи решает?


      1. Rubilnik
        25.05.2026 11:37

        Нет, потому что я пользуюсь личным прокси, а в ротации стоят socks на localhost для работы через альтернативные обходичики на время включения белых списков.


        1. supercat1337
          25.05.2026 11:37

          Личные прокси блокируют? Долго живут?


          1. K0Jlya9
            25.05.2026 11:37

            Видимо зависит от места. У меня тут не блокируют даже чистый ваиргард, не то что какие то прокси.


            1. Rubilnik
              25.05.2026 11:37

              Wireguard чистый до домашнего ip работает кстати нормально, чем тоже иногда пользуюсь.


          1. Rubilnik
            25.05.2026 11:37

            Не знаю от чего зависит, сижу я и несколько родственников на одном и том же ip уже больше полугода)

            Для телеграма телемт использую, а для остального awg 2.0 (он же в роутере через podkop) и vless +warp если надо немного чище ip на выходе, но последняя связка бывает тормознуто себя ведёт. Может быть ещё из-за того, что там reality на icloud сделан и к последнему не всегда нормально подключается.

            А ещё забыл про slipstream для белых списков и yandex cloud bridge (через cloud functions).

            Сейчас перечитываю это все и в недоумении когда выход в интернет стал настолько сложным...


            1. Bobr-Kurwa Автор
              25.05.2026 11:37

              Идея проекта именно в том, что общедоступные прокси протухают.

              И хочется сделать "волшебную кнопку", чтобы всё работало. Не только для айтишников. (Хотя да, прототип сделан на Python, так что всё равно для айтишников получилось.)


    1. DmitryOlenin
      25.05.2026 11:37

      В iOS, например, нет переключателя AutoSwitch вообще :(


  1. dimonz80
    25.05.2026 11:37

    КДПВ
    КДПВ


    1. Bobr-Kurwa Автор
      25.05.2026 11:37

      Постеснялась картинку с членами в ленту выкатывать. Мало ли, кто на работе читает, а тут они :)

      А в целом картинка отображает суть проекта с перепрыгиванием с одной прокси на другую


  1. K0Jlya9
    25.05.2026 11:37

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


  1. rufus20145
    25.05.2026 11:37

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

    А ещё, полагаю, точно такое же переключение можно сделать на базе haproxy с роутингом по sni. Только надо для всех прокси указать не их адреса, а адрес haproxy.


    1. Bobr-Kurwa Автор
      25.05.2026 11:37

      Добавленные прокси довольно быстро протухают.

      Идея прокладки в автоматизации. Чтобы не заниматься каждый день добавлением новых прокси и чисткой старых.

      Ну и дополнительно, не все клиенты умеют автопереключаться. Unigram, кажется, не умеет.

      Хотя пока статья в песочнице лежала, я уже немножко притормозила с доработкой проекта, т.к. есть стабильно работающий Тор, и оказалось проще его использовать как socks proxy. Это на данный момент так, посмотрим, что будет дальше.


  1. andynvkz
    25.05.2026 11:37

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