Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

NoDPI v2.3: они научились собирать фрагментированный ClientHello, а мы научились это исправлять

Дата публикации: 26-10-2026 06:01:11

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

Основное содержимое страницы с новостью.

f131c01020fc9297201d66f10bbd4935.png

Салют, Хабр!

Я думаю многие из вас слышали про 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 - GVCoder09/NoDPI: NoDPI is a utility for bypassing the DPI (Deep Packet Inspection)

github.com

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

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

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

7.69%… нейрослоп!4

7.69%… реклама!4

84.62%… торт!44

Проголосовали 52 пользователя. Воздержались 14 пользователей.

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

11.9%Обязательно5

19.05%Не факт8

52.38%Просто скроют статью22

16.67%Даже не парься7

Проголосовали 42 пользователя. Воздержались 9 пользователей.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Фильтрация ТСПУ у серверов Таймвеб Cloud: диагностика, обход через reverse-proxy и СDN08.4801-08-2026
2Топ утилит и инструментов для проверки работоспособности VDS-16.6705-08-2026
3Считаем пакеты на Rust09.8704-07-2026
4Что видит DPI, и что мы попробовали у него отобрать08.4208-08-2026
5Перебор IP-адресов на хостинге — и как с этим бороться0514-07-2026
6Как я уведомления мониторинга в мессенджер национальный переносил0514-07-2026
7Писал мониторинг на Go «за выходные» — застрял на месяцы. Вот на чём06.9711-06-2026
8DPI для любопытствующих09.5809-08-2026
9Собрать прошлое: как архивировать весь трафик сборки SONiC08.9420-07-2026

Классификация: Мнения. Схожих патентов: 0. Схожих новостей: 9. Тональность: 0. Информативность: 7.4. Источник: habr.com.