Салют, Хабр!

Я думаю многие из вас слышали про NoDPI — open‑source инструмент на Python для... кхм... изменения сетевого трафика (ну вы поняли). В последний месяц мне стали массово жаловаться (раз, два и ещё в личке), что некоторые сайты перестали открываться. К сожалению, у меня долго не доходили руки разобраться с этим, но в эти выходные я наконец взялся за дело.

Под катом расскажу, что случилось и как получилось это побороть.


Дисклеймер: Данная статья носит исключительно ознакомительный, научно‑исследовательский и академический характер. Автор не занимается пропагандой, рекламой или популяризацией средств обхода блокировок, а также не призывает к нарушению действующего законодательства. Все описанные технологии, протоколы и программные решения рассматриваются исключительно с точки зрения их технического устройства, архитектуры современных сетей и информационной безопасности. Применение полученных знаний в практических целях остается на личной ответственности читателя.

Я долго думал, нужно ли писать об этом на «Хабре» и в каком формате. Всё же решил оформить публикацию в виде статьи, так как многие не смотрят на обновления и могут подумать, что инструмент просто сломался и умер.

Как работало раньше

В применении к HTTPS, DPI узнаёт, куда вы идёте, по полю SNI в ClientHello — первом пакете TLS‑хендшейка, где доменное имя сайта лежит просто открытым текстом. Видим там youtube.com — дропаем соединение, и клиент уходит в таймаут.

NoDPI резал ClientHello на мелкие TLS‑записи так, что имя домена оказывалось разбито по кускам в пару байт. Сервер склеивает такие записи штатно, а DPI сигнатуру не находил и пропускал соединение. В принципе, было ожидаемо, что это, скорее всего, вынужденное ограничение со стороны DPI просто из‑за нехватки мощности и рано или поздно эту лавочку прикроют. Так и случилось.

Что сломалось

В последний месяц пользователи стали мне жаловаться на то, что YouTube перестал открываться — соединения идут, ошибок нет, но страница виснет. На самом деле это коснулось не только YouTube, но и остальных сайтов на большинстве провайдеров. Сам я этой проблемы, к сожалению, не заметил, так как прошёл год с тех пор, как я перешёл на собственный VPN (да, вот так бывает), а NoDPI использую только на даче на мобильном интернете.

Итак, DPI, судя по экспериментам, теперь собирает TCP‑поток целиком и ищет SNI в собранных данных, поэтому дробление пакетов больше не помогает. Проблема была воспроизведена на нескольких машинах с разными операторами. Попутно выяснилась любопытная деталь:

  • IP Google с чужим SNI — проходит;

  • чужой IP с SNI youtube — проходит;

  • IP Google с SNI youtube — обрыв.

Значит, ТСПУ смотрит на связку «IP + SNI». Дальше были проверены несколько вариантов дробления:

  • фрагментация на TLS‑записи, даже по 1 байту — блок;

  • каждый фрагмент отдельным TCP‑сегментом — блок;

  • задержки между сегментами — проходит 1–2 соединения из 10.

В tcpdump это выглядит так: ClientHello уходит отдельными сегментами, SNI — кусками по 7 байт (заголовок записи + 2 байта имени). Мелкие сегменты доходят, а последний, после которого DPI уже собрал имя, уходит в бесконечные ретрансмиты. Затем соединение закрывают «от имени» Google. Подделку выдаёт TTL: в одном соединении FIN приходит с TTL 122 без флага DF, а RST — с TTL 110 и DF. ServerHello не приходит вообще.

Disorder: ломаем порядок пакетов

После относительно недолгих экспериментов, которые я описывать не буду, наконец удалось разобраться что к чему.

Раз DPI собирает поток, напрашивается закономерное решение: нужно сломать ему порядок сегментов. Серверу это не мешает, так как TCP собирает переставленные сегменты по номерам последовательности. Выглядит это так:

  1. Первый сегмент с началом SNI уходит с TTL=1 и умирает на первом хопе, не доходя до ТСПУ.

  2. Остаток ClientHello (всё так же порезанный) сразу уходит с нормальным TTL.

  3. Ядро ОС не получает подтверждения на первый сегмент и через ~200 мс само переотправляет его, уже с нормальным TTL.

  4. Сервер получает всё и собирает нормальный ClientHello, а ТСПУ видит только середину потока без начала и SNI не распознаёт.

Всё сделано на стандартной библиотеке через setsockopt: без драйверов, raw‑сокетов и прав администратора. Минус: около 200 (приблизительно!) мс к установке соединения, на скорость загрузки это не влияет.

Сама реализация:

Код
    async def _send_disorder(
        self, writer: asyncio.StreamWriter, first: bytes, rest: bytes
    ) -> None:

        sock = writer.get_extra_info("socket")
        if sock.family == socket.AF_INET6:
            level = socket.IPPROTO_IPV6
            option = getattr(socket, "IPV6_UNICAST_HOPS", 16)
        else:
            level, option = socket.IPPROTO_IP, socket.IP_TTL

        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        default_ttl = sock.getsockopt(level, option)
        sock.setsockopt(level, option, 1)
        try:
            writer.write(first)
            await writer.drain()
            await asyncio.sleep(0.01)
        finally:
            sock.setsockopt(level, option, default_ttl)

        writer.write(rest)
        await writer.drain()

TCP_NODELAY нужен, чтобы ядро не склеило сегменты по алгоритму Нейгла. Пауза в 10 мс даёт первому сегменту уйти до того, как TTL вернётся к обычному.

Что нового в v2.3

Релиз с этим фиксом был выпущен вчера, 25 октября на GitHub. Теперь:

  • Метод disorder включён по умолчанию. Устаревшие методы sni и random удалены вместе с параметром --fragment-method. (Хотя, строго говоря, sni стал единственным методом фрагментации)

  • Новый флаг --no-disorder отключает disorder и оставляет только фрагментацию SNI.

  • ClientHello читается целиком по длине записи. Раньше ClientHello современных браузеров с пост‑квантовым обменом ключей мог прочитаться не целиком, так как он не влезает в один TCP‑сегмент.

К сожалению, все это — гонка вооружений: если ТСПУ научится собирать переупорядоченные потоки (а это рано или поздно случится), придётся искать следующий метод.

Что дальше

Ещё год назад я обещал прикрутить к проекту новых фич. Каюсь — так и не успел этого сделать. Но обещаю в ближайшее время взяться за дело всерьез) Я наконец постараюсь сделать:

  • Встроенный резолв через DNS‑over‑HTTPS с фолбэком на системный DNS. Спасает от подмены DNS у провайдера.

  • Блэклист по IP и подсетям.

  • Метод OOB (TCP urgent byte).

  • SOCKS5-прокси.

  • Сборки для роутеров

  • И может ещё чего‑нибудь...

Также поисследую, но не обещаю:

  • Попробовать разблокировать Telegram (MTProto) и другие соц. сети

  • UDP/QUIC, звонки (это прям совсем маловероятно)

Все новые фичи и фиксы, естественно, будут переноситься и на порт под Android — NoDPI4Android. Там также возможно поработаю над интерфейсом.

Буквально пару слов про поддержку проекта

Многие спрашивают меня, не планирую ли я собирать донаты, и как можно поддержать проект материально. Ответ — пока не планирую. За все статьи с рейтингом 30 и выше я получаю от Хабра отчисления по ППА и довольно немаленькие (за 30 — 3K₽, за 50 — 5K₽ и плюсом сверху). Это покрывает все расходы на VPS и прочие штуки.


Заключение

На этом все, спасибо за внимание! Надеюсь, было интересно и хоть сколько‑то полезно. Буду рад сообщениям об ошибках, предложениям, критике. Проект, как всегда, доступен здесь:

А здесь можно скачать последний релиз.

Также выражаю благодарность всем, кто принимает участие в обсуждениях, в первую очередь на GitHub - ваш вклад действительно бесценен!

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


  1. Lord_of_Rings Автор
    26.10.2026 07:01

    Кстати, на GitHub пишут о том, что с Kaspersky утилита не работает (а точнее не открываются сайты), поэтому если он у кого-то есть, стоит иметь ввиду. Я сам, к сожалению, проверить не могу.


    1. ksbes
      26.10.2026 07:01

      Касперкий может “самовольно” склеивать/упорядочивать пакеты при проверке исходящего трафика - ему ж надо знать не ботнет ли там что-то шаманит?. Т.е. работать как (условно) утилита “4DPI!!!”, обнуляя все усилия.


    1. softcoder2020
      26.10.2026 07:01

      Так касперский же по дефолту свой локальный прокси вешает и лезет в защищенные соединения (подменяет серт чтобы трафик сканить).

      получается твоя тулза режет пакеты и крутит ttl на локалхосте, а каспер их тут же у себя перехватывает, заботливо склеивает обратно в нормальный стрим, проверяет и в сеть наружу выплевывает уже чистый целый ClientHello от своего имени. лечится только отключением проверки защищенных соединений в настройках ав, по-другому тут никак не обойти


      1. Lord_of_Rings Автор
        26.10.2026 07:01

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


        1. softcoder2020
          26.10.2026 07:01

          а, ну если даже галка не спасает, значит у них перехват ниже сидит, на уровне их ndis-драйвера (сетевого экрана). он тупо буферизует весь tcp-поток в ядре до отправки в сетевуху, и твой setsockopt с ttl=1 до реального интерфейса просто не долетает в нужном виде. тут походу только всю “веб-защиту” целиком вырубать или процесс питона в исключения антивируса закидывать


      1. Verona90210
        26.10.2026 07:01

        Проще вынести утилиту на домашний роутер перед компом, тогда антивирус на рабочей станции вообще не будет видеть сетевую магию


        1. max9
          26.10.2026 07:01

    1. vesper-bot
      26.10.2026 07:01

      каспер прокси на дороге ставит. решение - поднимать сабж на апстриме.


  1. sanchas
    26.10.2026 07:01

    Интересно, что будет если слать фрагментированные кусочки SNI c некоторым интервалом? По идее DPI стараясь собрать их будет выделять под это некоторый буфер. Есть ли у DPI некоторый таймер после которого он очищает этот буфер? Если есть, можно попытаться обмануть его растянув рукопожатие во времени, а если нет, то можно переполнить память на DPI системе посылая много фрагментов с длинными интервалами.


    1. ksbes
      26.10.2026 07:01

      Главное чтобы злоумышленники не попытались использовать это для нарушения работы критической инфраструктуры (это - уголовная статья!) засылая много кусочков большого размера с целью переполнения этого буфера, возможно, даже, исполнения произвольного кода при выходе за его границы. Это ж так можно будет рунет пололжить! (злоумышленникам)


      1. xenon
        26.10.2026 07:01

        ну или положить ТСПУ и освободить рунет.... но свобода, это же плохо, сразу начнется треш, угар и содомия (вы, конечно, как знаете, а многих вот только усилия РКН и ТСПУ и удерживают).

        Будем надеяться на лучше!


    1. Lord_of_Rings Автор
      26.10.2026 07:01

      Да, я пробовал так (в статье это упомянуто). Если делать небольшой интервал (до 1 с), то иногда, в 10% случаях, может проскочить, но в целом не работает. Если увеличить интервал, то происходит обрыв соединения.


    1. xenon
      26.10.2026 07:01

      Тут есть тонкость в том, что
      1. Пересборка может выполняться по разному на ТСПУ и на сервере (что я вижу), значит они могут увидеть и понять разный трафик:
      1.1 Удаленный сервер (условный ютуб) и ТСПУ могут иметь разные таймауты. Ютуб ждет пакета 10 секунд а ТСПУ 20, к примеру. Или наоборот.
      1.2 Возможно они по разному решают проблему overlap пакетов. Например, в некотором пакете идет sequence от 1000 до 1500, а в следующем идет от 1100 до 1600. Они перекрываются. Как поступить? Какой-то из них отбросить? Принять оба? А какие данные взять на интервал 1100-1500 - из первого или из второго?
      1.3 Переполнение памяти, да. Сначала пакет со смещением 0, а потом сразу с window scaling куда-нибудь на терабайты вперед. Как ТСПУ отнесется? Какие-то пакеты будет игнорировать? это уже интересно!

      2. TTL !! Это же вообще шикарно! Это отличный способ показать ТСПУ какой-то трафик, но не показывать его ютубу. Например, послать RST или трафик с другим SNI (который до ютуба даже и не дойдет).

      В общем, мне кажется, это битва щита и меча в которой промежуточный фильтр в конечном итоге обречен проиграть (ну или тупо слить сложную игру до файрвола из 90-ых годов, который только сетку сбербанка и озона дозволяет и все). Но они ведь так не делают - значит, есть на то причины, ограничения.


      1. oheret
        26.10.2026 07:01

        2. TTL !! Это же вообще шикарно! Это отличный способ показать ТСПУ какой-то трафик, но не показывать его ютубу. Например, послать RST или трафик с другим SNI (который до ютуба даже и не дойдет).

        Так вовсю же уже используется.

        Да и переупорядочивание посредством задействования TTL=1 давно уже присутствует в других подобных прогах.

        В чём новость не совсем понял.


    1. Verona90210
      26.10.2026 07:01

      В статье автор как раз писал про такие задержки. Пробивается только одно соединение из десяти, остальные уходят в дроп


  1. dekeyro
    26.10.2026 07:01

    Очень рад что вы живы и развиваете проект.

    Очень полезная утилита. Для академических исследований сети конечно


    1. Lord_of_Rings Автор
      26.10.2026 07:01

      что вы живы

      Я тоже этому рад...


  1. LeToan
    26.10.2026 07:01

    А с чем может быть связано такое поведение на ютубе:

    Видео загружается и показывается и потом затыкается на случайной метке времени, кратной примерно 20 с. Чаще всего на 20 и затыкается. Потом варианты 39-41 и тд, но это уже редко. Но можно перегрузить и сдвинуть на 21 и дальше идет, а может и не пойти. С вэпеном такого нет.


    1. Lord_of_Rings Автор
      26.10.2026 07:01

      Ну похоже на обрыв соединений к googlevideo.com. По всей видимости, плеер качает видео кусками примерно по 20 с, и каждый новый кусок может идти новым соединением, которое режется. Проверьте, что в blacklist.txt есть googlevideo.com, и посмотрите лог (--log-access, --log-error) в момент остановки (желательно прервать сразу после этого по ctrl+c, чтобы буфер гарантированно сбросился). Пришлите в issues, поразбираемся.


      1. qwertypromes
        26.10.2026 07:01

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


        1. Lord_of_Rings Автор
          26.10.2026 07:01

          Отключите QUIC в браузере, чтобы всё шло по TCP: в Chrome chrome://flags/#enable-quic Disabled, в Firefox about:config > network.http.http3.enable = false

          Если не поможет запустите с --no-blacklist


    1. Akr0n
      26.10.2026 07:01

      del


    1. rm-rf-all
      26.10.2026 07:01

      Такое бывает у меня. Обновляю cidr для заворачивания в тунель подсетей Гугла и ютуба


  1. disp06
    26.10.2026 07:01

    Хотелось бы чтобы эта игра в кошки-мышки уже прекратилась. Я не готов регулярно отдавать свое время на настройку того что раньше работало без костылей! Есть конечно свои плюсы, можно стать сетевым инженером и пойти в It, но в нынешних реалиях я уже начинаю сомневаться в том что бизнесу нужны люди, и возможно, китайский "клод код" на виртуалке с бэкапами будет более дешёвый и эффективный вариант.


    1. thelittlewitch1
      26.10.2026 07:01

      Судя по новостям со всего мира, запретов, ограничений и прочих палок в колёса интернете меньше не станет уже никогда. Наверху уже чухнулись, что надо контролить всё, и не отпустят.


    1. net_racoon
      26.10.2026 07:01

      Ну тут тогда только AviaSales


    1. xenon
      26.10.2026 07:01

      Есть прекрасное шоу, КВН... местами дешево показывают, в районе 100р и вполне годное качество. Я бесплатно смотрел пару дней когда у нас были БСки.

      Есть просто психологическая проблема впервые заплатить за такое (если раньше не требовалось платить), но если подумать, 100р - не деньги, да и добрым людям помочь - всегда хорошо. Проще им дать и все. И дальше - Свободный Интернет, как при Борисе Николаевиче, батюшке!


      1. Mixael_sas
        26.10.2026 07:01

        Свободным интернет был и посое него, причём достаточно долгое время. Золотыми были времена, когда появился DSL, а сам интернет был ещё для модемов. Такого сочетания скорости, свободы общения и начинавшегося разнообразия контента уже не будет никогда. А БН батюшкой не был никогда, так себе он персонаж был.


  1. rus-p
    26.10.2026 07:01

    Браво,

    вы даете возможность заниматься ниокр-ом на стороне рдп.ру, можно сказать выбиваете деньги из правительства, если конечно вы сами не оттуда)


    1. Lord_of_Rings Автор
      26.10.2026 07:01

      Наивно пологать, что они не знают этого без меня.

      если конечно вы сами не оттуда

      Ага, двойной агент)


      1. rus-p
        26.10.2026 07:01

        Ну знать и закодировать на ограниченном ресурсе fpga те костыли, что вы им соорудили, и чтобы при этом не только не упал Интернет всея Руси, но и выдержать pps это конечно не лезвие бритвы, но у нас в стране и этот опыт многого стоит.

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


      1. LinkToOS
        26.10.2026 07:01

        Наивно пологать, что они не знают этого без меня.

        А вдруг? Может они только из статей на Хабре узнают что их опять опередили )
        Они вообще могут как-то отслеживать изменение трафика с ютуба, заметить что количество обходов увеличилось, и сделать вывод что появилось новое решения для обхода?


        1. LF69ssop
          26.10.2026 07:01

          Это работает по другому. Задача не в уменьшить обход ограничений, задача отчитаться о новых усилиях по предотвращению.


    1. Kenya-West
      26.10.2026 07:01

      Новая статья про обход блокировок, и вот обязательно появятся очередные "всёпропальщики", которые предлагают методы инквизиции, стреляя себе в ногу и рекомендуя всем делать то же самое.

      Поймите же, "security through obscurity" - это невыгодный вариант ведения информационной битвы практически во всех случаях!


  1. LinkToOS
    26.10.2026 07:01

    К сожалению, все это — гонка вооружений: если ТСПУ научится собирать переупорядоченные потоки (а это рано или поздно случится)

    Тут все упирается в железо, даже если известен метод. Можно все надежно проанализировать и перехватить, но стоимость оборудования ТСПУ будет запредельной.


    1. xenon
      26.10.2026 07:01

      Мне кажется, не научится. Эта задача в пределе - нерешаемая. Это не просто "дописать код" и даже не просто "добить памяти в ТСПУ", это - принять много решений, каждое из которых противоречивое. Я выше привел примеры, которые я вижу. Любое решение каким-то способом автоматически даст возможность обхода другим способом.


      1. rogoz
        26.10.2026 07:01

        Эта задача в пределе - нерешаемая.

        С чего это? Спецификации конечны, конкретные веб-сервера тоже определены. Просто при создании ТСПУ была несколько облегчена задача.

        Рано или поздно (имхо скорее рано) будет состояние "или ТСПУ ловит соединение, или стандартный Apache/nginx не может нормально обработать слишком модифицированные пакеты".


        1. Lord_of_Rings Автор
          26.10.2026 07:01

          Майкрософтский IIS, кстати, уже не может)


  1. Verona90210
    26.10.2026 07:01

    Тру киберпанк-рутина : чтобы включить ребенку мультики, настраиваем перестановку сегментов на питоне)


    1. xenon
      26.10.2026 07:01

      Хорошо же, что только сегменты переставляют...
      Хорошо же, что только сегменты переставляют...


  1. JustMoose
    26.10.2026 07:01

    Мне интересно, а те разрабы, которые пилят этот самый ди-пи-ай, у них какая мотивация? Просто работают, потому что хочется кушать?


    1. Lord_of_Rings Автор
      26.10.2026 07:01

      Деньги не пахнут (c)

      Скрытый текст


      1. vadimk91
        26.10.2026 07:01

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


    1. Natsuru
      26.10.2026 07:01

      Знакомый работает в субподряде ркн техником/наладчиком. Зп на старте без опыта сильно выше, чем у меня (системный/асутп инженер 5 лет). Сделка с совестью, как есть