syndata --- payload в TCP SYN (zapret2 / nfqws2)

Коротко и по-простому

Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке Host: запроса. ТСПУ (Технические средства противодействия угрозам — оборудование DPI, то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения с данными и по нему решает, что делать с соединением дальше.

Но ClientHello уходит не первым. Перед данными TCP устраивает «рукопожатие» (handshake) из трёх пакетов. Первый, SYN, клиент отправляет, чтобы попросить о соединении: в обычном виде в нём нет данных, только заголовки с флагом SYN. Сервер отвечает пакетом SYN-ACK, клиент подтверждает его пакетом ACK, и только после этого браузер отправляет ClientHello. Имени сайта в SYN нет, поэтому на этом этапе его не знает и сам Zapret: чем это грозит хостлистам, описано в разделе Работа с хостлистами.

Рукопожатие как звонок

SYN — «Алло, можем говорить?», SYN-ACK — «Да, говори», ACK — «Ок». Если начать рассказывать своё дело ещё до ответа «Да, говори», собеседник, скорее всего, пропустит эти слова мимо ушей. Примерно так обычно ведёт себя и сервер с данными, которые пришли в SYN.

Дальше данные идут кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер по этим номерам; почему это гарантирует именно TCP, разобрано в нюансе «Работает только с TCP» в статье про multisplit. Фейк — это пакет-обманка с ложными данными, например с поддельным ClientHello. Обычно фейк «портят»: искажают его заголовки так, чтобы сервер пакет отбросил, а DPI прочитал. Такая порча называется fooling, подробно — в разделе Что такое fooling. У syndata с fooling своя особенность: портить приходится пакет, который сервер обязан принять (см. нюанс 2).

syndata — функция «нулевой фазы»: она работает до рукопожатия, на самом первом пакете соединения. Движок Zapret 2 (nfqws2) перехватывает исходящий SYN, а syndata делает четыре шага. Сначала копирует перехваченный SYN, сам оригинал при этом не трогая. Потом дописывает к копии данные из blob — заготовленного куска данных с именем, например стандартного TLS-фейка fake_default_tls (про блобы — Блобы); если blob не указан, берутся 16 нулевых байт. Затем при желании портит копию по правилам fooling и правит TLS-поля через tls_mod, например заменяет SNI в фейке. И наконец отправляет копию вместо оригинала, а оригинал выбрасывает. Как только по соединению приходит пакет без флага SYN, то есть рукопожатие прошло, syndata отключает себя для этого соединения.

Формально TCP разрешает данные в SYN (RFC 793), но большинство TCP-стеков завершают рукопожатие ответом SYN-ACK и данные из SYN игнорируют: соединение ещё не установлено, и payload отбрасывается. Настоящий ClientHello браузер отправит после рукопожатия обычным порядком, и сервер прочитает именно его. DPI может повести себя иначе: принять содержимое SYN за начало потока данных, записать ложные сведения о соединении (например, фейковое имя сайта) и пропустить настоящие данные, не проверив имя в них. Это «может»: как поведёт себя конкретная система, зависит от её устройства.

Команда из быстрого старта syndata:blob=fake_default_tls приклеивает к первому пакету соединения (SYN) фейковый ClientHello с именем www.w3.org. Его байты приходятся на те же позиции потока, что и будущий настоящий ClientHello. Большинство TCP-стеков отвечают SYN-ACK и данные из SYN не принимают, а ТСПУ может счесть их началом потока и не проверить имя в настоящем ClientHello, который уходит после рукопожатия. Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.

syndata: фейковый ClientHello в SYN-пакете Карта потока для syndata с blob=fake_default_tls: к SYN приклеен фейковый ClientHello с именем www.w3.org на тех же позициях потока. Сервер отвечает SYN-ACK, данные из SYN игнорирует, после рукопожатия принимает настоящий ClientHello. пакет в порядке отправки место в потоке (seq) → сервер 0 26 #1 SYN+DATA blob ···········www.w3.org····· SYN принят #2 SYN-ACK ← сервер ответ сервера, данные из SYN не подтверждены #3 ORIG seq=0 len=26 ···········youtube.com···· принят буфер сервера ···········youtube.com···· поток собран DPI первые данные потока пришли в SYN; если DPI счёл их ClientHello, настоящий может не проверяться сервер данные из SYN большинство TCP-стеков игнорирует и ждёт их заново после рукопожатия

Самая короткая команда включает syndata без аргументов: в SYN уходят 16 нулевых байт. Строку --lua-desync=… пишут в профиль — набор правил Zapret 2 для определённого трафика:

--lua-desync=syndata

Вариант со схемы, syndata:blob=fake_default_tls, кладёт в SYN TLS-фейк. Остальные готовые команды собраны в разделах Быстрый старт и Практические примеры.

Функцию стоит пробовать, если DPI начинает следить за соединением уже с SYN и не реагирует на фейки после рукопожатия, а также когда нужно «отравить» DPI ложными данными до того, как пойдёт реальный трафик. Ограничения у неё такие. Данные в SYN должны поместиться в один пакет: разрезать их на TCP-сегменты на этом этапе нельзя (нюанс 1). Fooling здесь бьёт по SYN, который сервер должен принять, поэтому часть параметров, например badsum или tcp_seq, ломает рукопожатие. Хостлисты напрямую не работают: имени сайта ещё нет, и остаётся только --ipcache-hostname, а первое соединение к каждому IP пройдёт без syndata (Работа с хостлистами). Ту же нулевую фазу использует wssize, который управляет размером окна TCP (сколько байт получатель готов принять) и заставляет сервер слать маленькие сегменты; почему из-за нулевой фазы --wssize считается крайней мерой, объяснено в отдельной заметке.

Ближайший сосед syndata — fake. Он тоже подсовывает DPI ложные данные, но отдельным пакетом уже после рукопожатия, оригинал при этом уходит дальше, а fooling нужен, чтобы сервер фейк отбросил. syndata кладёт данные в сам SYN и заменяет им оригинал. Все различия сведены в таблицу в разделе Отличия от fake, а с точки зрения fake — в Сравнение с syndata и fakedsplit. С техниками сегментации syndata обычно стоит в одной цепочке: он занимается SYN, а multisplit режет настоящий ClientHello уже после рукопожатия (см. Комбинирование с другими функциями). Остальные функции, которые работают с данными после рукопожатия, — multidisorder, fakedsplit и fakeddisorder. Рабочую комбинацию для своего провайдера подбирают и проверяют по чек-листу честной проверки.


Оглавление


Справка: где функция в коде и родственники

Паспорт функции для тех, кто сверяется с исходниками zapret2: в каком файле и на какой строке лежит код, какой ключ первой версии zapret она заменяет и как объявлена.

Файл: lua/zapret-antidpi.lua:395 nfqws1 эквивалент: --dpi-desync=syndata Сигнатура: function syndata(ctx, desync)

syndata --- стратегия “нулевой фазы” в zapret2. Она добавляет произвольный payload в TCP SYN-пакет, применяет модификации (fooling, tls_mod) и отправляет его вместо оригинального SYN. Оригинальный пакет дропается (VERDICT_DROP). Работает до установления TCP-соединения --- на этапе, когда клиент ещё только отправляет SYN.

Родственные/сопутствующие функции: fake (фейковый пакет после SYN), multisplit (TCP-сегментация), wssize (управление размером окна), multidisorder, fakedsplit, fakeddisorder.

Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в жизненный цикл desync-функции; здесь описано только то, чем syndata от этого скелета отличается.

Проще говоря

«Нулевая фаза» — этап до завершения рукопожатия, когда у приложения ещё нет данных для отправки; подробнее в разделе Нулевая фаза. Fooling — намеренная порча заголовков пакета, разобранная в Что такое fooling. VERDICT_DROP — решение «перехваченный пакет не отправлять»: вместо оригинала уходит подделанная копия; как движок складывает такие решения от разных функций, описано в стадии 8 жизненного цикла. Блоб (blob) — заранее заготовленные данные, которые можно подсунуть в пакет; см. Блобы.


Зачем нужен syndata

Некоторые DPI начинают анализировать соединение уже с первого пакета --- с SYN. Они запоминают IP и порт клиента, ждут данные и сопоставляют их с сигнатурами. syndata атакует этот механизм на самом раннем этапе:

  1. Ложные данные в SYN: DPI видит SYN-пакет с payload (например, фейковый TLS ClientHello) и может принять его за начало реального соединения
  2. Сбой трекинга: если DPI привязывает сессию к содержимому первого пакета, подменный SYN может направить трекинг по ложному пути
  3. Обход до handshake: воздействие происходит до TCP handshake --- DPI ещё не видел реальных данных приложения

Важно: TCP SYN с payload --- это валидная TCP-операция (RFC 793 разрешает данные в SYN, хотя многие стеки их игнорируют до завершения handshake). Сервер примет SYN, но payload из него обычно отбросит --- DPI же может попытаться его проанализировать.

Проще говоря

Трекинг — слежение DPI за соединением: он запоминает IP и порт клиента, ждёт данные и сопоставляет их с сигнатурами, то есть заранее известными строками. syndata подсовывает ему данные раньше настоящих, ещё в SYN. Сервер их обычно не читает: разговор для него ещё не начался. Что такое рукопожатие (handshake) и почему данные в SYN обычно игнорируются, объяснено в начале статьи, в разделе Коротко и по-простому. Слова «может» и «обычно» в тексте выше не случайны: как поведёт себя конкретный DPI или конкретный сервер, зависит от их устройства.


Быстрый старт

Минимально (16 нулевых байт в SYN):

--lua-desync=syndata

С TLS-фейком:

--lua-desync=syndata:blob=fake_default_tls

С TLS-фейком и модификацией SNI:

--lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,rndsni

Типовая комбинация (wssize + syndata + multisplit):

--lua-desync=wssize:wsize=1:scale=6 \
--lua-desync=syndata \
--lua-desync=multisplit:pos=midsld

Четыре блока идут от простого к сложному: без аргументов, с TLS-фейком, с TLS-фейком и модификацией SNI и, наконец, цепочка из трёх функций. Аргументы blob и tls_mod разобраны в разделе A) Собственные аргументы syndata. Цепочка описана в разделе Типовая цепочка: wssize + syndata + multisplit, там же объяснено, почему wssize стоит первым. Строки --lua-desync=… пишут в профиль; порядок вызовов в цепочке важен, подробнее в последовательность аргументов.


Принцип работы

Что такое SYN с payload

Обычный TCP SYN-пакет не содержит данных --- только заголовки с флагом SYN. syndata берёт этот пакет, добавляет в него payload (указанный в blob или 16 нулевых байт по умолчанию), применяет модификации и отправляет через raw socket вместо оригинала.

Обычный SYN:
  [IP Header][TCP Header (SYN)]

SYN после syndata:
  [IP Header][TCP Header (SYN)][PAYLOAD (blob или 16x 0x00)]

Сервер получит SYN с данными. Большинство TCP-стеков:

  • Завершат handshake (SYN-ACK), проигнорировав payload в SYN
  • Payload будет отброшен, потому что соединение ещё не установлено

DPI же может:

  • Проанализировать payload из SYN как начало потока данных
  • Записать ложную информацию о соединении (фейковый SNI, фейковый HTTP Host)
  • Пропустить реальные данные, которые пойдут позже

Проще говоря

Payload — полезные данные пакета без сетевых заголовков. Обычный SYN — «конверт» без письма; syndata вкладывает в конверт письмо (содержимое blob или 16 нулей) и отправляет через raw socket, то есть сырым пакетом помимо очереди перехваченных пакетов; способы отправки разобраны в разделе Четыре способа выпустить пакет. Сервер это письмо обычно не прочтёт, а DPI может прочесть.

Нулевая фаза

syndata --- стратегия нулевой фазы. В терминологии zapret это означает:

ФазаКогдаЧто доступноПримеры функций
Фаза 0 (SYN)До TCP handshakeТолько SYN-пакет. Нет payload от приложенияsyndata, wssize
Фаза 1 (данные)После handshake, первые данныеРеальный payload (TLS ClientHello, HTTP request и т.д.)fake, multisplit, fakedsplit

Фаза здесь — этап жизни соединения. В фазе 0 идёт только рукопожатие, и у приложения ещё нет данных. В фазе 1 первые данные уже отправлены, и Zapret видит настоящий ClientHello или HTTP-запрос. Функции фазы 1 (fake, multisplit, fakedsplit) работают с этими данными, а функции фазы 0 их ещё не видят. К фазе 0 относится и wssize.

Следствия нулевой фазы:

  • Нет реального payload: приложение ещё ничего не отправило, поэтому syndata работает только с blob-данными или нулями
  • Нет hostname: на этом этапе zapret не знает, к какому домену обращается клиент --- хостлисты работают только через --ipcache-hostname
  • Нет payload filter: параметры --payload=... и payload=... не применимы --- SYN-пакет не содержит данных приложения
  • Воздействие на все ретрансмиссии SYN: если TCP-стек ретранслирует SYN (timeout), syndata обработает и его

Проще говоря

Пока идёт рукопожатие, Zapret не знает, какой сайт открывает клиент: имя появится только в ClientHello или в HTTP-запросе. Отсюда следствия. Нечего сверять с хостлистом по имени: остаётся кеш --ipcache-hostname, см. Работа с хостлистами. Нечего фильтровать по типу данных: фильтр payload (см. payload) для SYN не применим. Ретрансмиссия — повторная отправка SYN, если первый не дошёл или ответ потерялся; syndata обработает и её, подробнее в нюансе 4. Все ретрансмиссии SYN обрабатываются.

Логика обработки пакетов

Пакет пришёл в syndata
  |
  +-- Не TCP? --> instance_cutoff (кроме ICMP)
  |
  +-- TCP:
       |
       +-- Флаг SYN (не SYN+ACK)? --> deepcopy dis, добавить payload,
       |                               apply_fooling, tls_mod,
       |                               rawsend + VERDICT_DROP
       |
       +-- Не SYN? --> instance_cutoff (миссия завершена)

Ключевые моменты:

  • Проверяется именно TH_SYN без TH_ACK --- то есть только исходящий SYN от клиента, не SYN+ACK от сервера
  • deepcopy --- работа ведётся с копией dissect, оригинал не модифицируется
  • Если пакет не SYN (например, ACK, PSH+ACK) --- это значит, что handshake уже прошёл, и syndata отключает себя (instance_cutoff)
  • Для ICMP cutoff не выполняется --- ICMP может быть связан с SYN (например, ICMP Destination Unreachable)

Проще говоря

Dissect (диссект) — пакет, разобранный движком на поля: заголовки, флаги, данные; устройство этой таблицы описано в заметке структура desync и диссекта. deepcopy — полная копия такой таблицы: правки идут в копию, а оригинал остаётся нетронутым. instance_cutoff — «этот вызов функции больше не нужен для данного соединения»: после него движок перестаёт вызывать syndata для потока, см. стадию 1 жизненного цикла. VERDICT_DROP — «оригинальный пакет не отправлять». Логика в целом такая: пришёл исходящий SYN, значит подменяем его; пришёл любой другой TCP-пакет, значит рукопожатие уже прошло и функция отключается; ICMP-пакет сигналом к отключению не считается.


Полный список аргументов

Формат вызова:

--lua-desync=syndata[:arg1[=val1][:arg2[=val2]]...]

Все val приходят в Lua как строки. Если =val не указан, значение = пустая строка "" (в Lua это truthy), поэтому флаги пишутся просто как :tcp_md5, :badsum, :ipfrag.

Важно: syndata не поддерживает стандартные аргументы direction, payload (фильтр) и ipid. Это связано с тем, что syndata работает на фазе 0 (SYN), где нет данных приложения, нет понятия “payload type”, и apply_ip_id не вызывается.

Проще говоря

direction — фильтр «какое направление пакетов обрабатывать», payload — фильтр по типу данных, ipid — управление полем IP ID в заголовке пакета. В SYN нет ни данных приложения, ни выбора направления, поэтому этих аргументов у syndata нет; причины разобраны в нюансе 3. Нет direction, payload filter, ipid. Стандартные блоки B–E ниже общие для многих функций; их описание для другой функции можно сверить, например, с разделом Полный список аргументов статьи про fake.

A) Собственные аргументы syndata

blob

  • Формат: blob=<blobName>
  • Тип: имя blob-переменной
  • По умолчанию: 16 нулевых байт (\x00 x 16)
  • Описание: Payload, который будет добавлен в SYN-пакет. Должен помещаться в один пакет --- сегментация невозможна (используется rawsend_dissect_ipfrag, а не rawsend_payload_segmented). Если blob не задан, отправляются 16 нулевых байт
  • Примеры:
    • blob=fake_default_tls --- стандартный TLS-фейк из zapret
    • blob=fake_default_http --- стандартный HTTP-фейк
    • blob=0xDEADBEEF --- inline hex
    • blob=my_custom_payload --- предзагруженный blob
    • без blob= --- 16 нулей (дефолт)

Проще говоря

Blob — заранее заготовленный кусок данных с именем. Какие бывают стандартные и как объявить свой, объяснено в заметке Блобы и в разделе blob — источник фейковых данных статьи про fake. Inline hex — байты, записанные прямо в команде (0xDEADBEEF), без отдельного файла. Сегментация — разрезание данных на несколько TCP-пакетов, как в multisplit; на этапе SYN она невозможна, поэтому весь blob должен уйти одним пакетом (нюанс 1).

tls_mod

  • Формат: tls_mod=<mod1[,mod2,...]>
  • Тип: строка со списком модификаций через запятую
  • По умолчанию: не задан
  • Описание: Модификации TLS-данных в payload перед отправкой. Применяется после apply_fooling. Подробности --- в разделе tls_mod
  • Работающие значения: rnd, rndsni, sni=<str> (включая sni=%var)
  • Молча игнорируемые: dupsid, padencap
  • Примеры:
    • tls_mod=rnd --- рандомизация TLS-полей
    • tls_mod=rndsni --- рандомизация SNI
    • tls_mod=sni=google.com --- замена SNI
    • tls_mod=rnd,rndsni,sni=google.com --- комбинация
    • tls_mod=dupsid --- не даст ошибки, но молча проигнорируется

Проще говоря

tls_mod правит поля TLS-фейка перед отправкой: например, подменяет имя сайта (SNI) или заменяет случайные значения session_id и random. Работает только часть значений, и почему именно эта, объяснено в разделе tls_mod --- модификация TLS в syndata. Как tls_mod устроен в fake, где настоящий ClientHello уже есть, описано в разделах tls_mod — модификации TLS в фейке и Опции tls_mod.


B) Standard fooling

Модификации L3/L4 заголовков. В syndata применяются к копии dissect (через apply_fooling после deepcopy). Оригинальный пакет не модифицируется --- он дропается.

Проще говоря

Fooling — «порча» заголовков пакета (TTL, флагов, номеров seq и ack, контрольной суммы), из-за которой пакет либо не доходит до сервера, либо сервер его отбрасывает; идея разобрана в Что такое fooling, а способы — в каталоге способов. L3 и L4 в названии — это заголовки IP и TCP. Параметры ip_* и ip6_* относятся к IPv4 и IPv6, tcp_* — к TCP. TTL (у IPv6 Hop Limit) — предельное число хопов, то есть маршрутизаторов на пути, после которого пакет пропадает.

ПараметрОписаниеПример
ip_ttl=NУстановить IPv4 TTLip_ttl=6
ip6_ttl=NУстановить IPv6 Hop Limitip6_ttl=6
ip_autottl=delta,min-maxАвтоматический TTL (delta от серверного TTL)ip_autottl=-2,40-64
ip6_autottl=delta,min-maxАналогично для IPv6ip6_autottl=-2,40-64
ip6_hopbyhop[=HEX]Вставить extension header hop-by-hop (по умолчанию 6 нулей)ip6_hopbyhop
ip6_hopbyhop2[=HEX]Второй hop-by-hop headerip6_hopbyhop2
ip6_destopt[=HEX]Destination options headerip6_destopt
ip6_destopt2[=HEX]Второй destination optionsip6_destopt2
ip6_routing[=HEX]Routing headerip6_routing
ip6_ah[=HEX]Authentication headerip6_ah
tcp_seq=NСместить TCP sequence (+ или -)tcp_seq=-10000
tcp_ack=NСместить TCP ack (+ или -)tcp_ack=-66000
tcp_ts=NСместить TCP timestamptcp_ts=-100
tcp_md5[=HEX]Добавить TCP MD5 option (16 байт; по умолчанию случайные)tcp_md5
tcp_flags_set=LISTУстановить TCP-флагиtcp_flags_set=FIN,PUSH
tcp_flags_unset=LISTСнять TCP-флагиtcp_flags_unset=ACK
tcp_ts_upПоднять TCP timestamp option в начало заголовкаtcp_ts_up
tcp_nop_delУдалить все TCP NOP опцииtcp_nop_del
fool=<func>Кастомная Lua-функция foolingfool=my_fooler

Заметка: fooling в syndata имеет иной смысл, чем в fake или fakedsplit. В fake fooling нужен, чтобы сервер отбросил фейк. В syndata fooling модифицирует сам SYN-пакет, который сервер должен принять для установления соединения. Поэтому деструктивные fooling-параметры (tcp_seq, tcp_ack, badsum) сломают handshake --- сервер не получит валидный SYN. Безопасные варианты: ip_ttl/ip6_ttl (если TTL хватит до DPI, но не до сервера --- спорно для SYN), tcp_md5 (сервер без MD5 проигнорирует опцию), IPv6 extension headers.

Проще говоря

В fake порча нужна, чтобы сервер выбросил фейк. В syndata пакет один, и он же должен дойти до сервера: это SYN, без которого не будет рукопожатия. Поэтому портить его приходится осторожно. Параметры tcp_seq, tcp_ack и badsum лишают сервер валидного SYN, а tcp_md5 и IPv6 extension headers (дополнительные заголовки IPv6) в тексте выше названы безопасными. Про ip_ttl в заметке сказано «спорно для SYN», см. пример С TTL fooling (осторожно!). Подробнее — в нюансе 2. Fooling может сломать handshake.


C) Standard reconstruct

ПараметрОписание
badsumИспортить L4 (TCP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет

Предупреждение: badsum в syndata означает, что сервер не получит SYN вообще --- TCP handshake не состоится. Используйте только если хотите отправить “мусорный” SYN, за которым пойдёт ретрансмиссия.

Проще говоря

Reconstruct — сборка сырого пакета заново из полей перед отправкой. badsum портит контрольную сумму (checksum) TCP, по которой получатель проверяет пакет; с неверной суммой пакет отбрасывается, и сервер SYN просто не увидит. Про повторную отправку SYN — нюанс 4. Все ретрансмиссии SYN обрабатываются.


D) Standard rawsend

ПараметрОписание
repeats=NОтправить пакет N раз (идентичные повторы)
ifout=<iface>Интерфейс для отправки (по умолчанию определяется автоматически)
fwmark=NFirewall mark (только Linux, nftables/iptables)

Rawsend — этап отправки готового пакета в сеть (см. Четыре способа выпустить пакет). repeats отправляет копии подряд (пример — Отладка: повторы отправки), ifout выбирает сетевой интерфейс, fwmark ставит метку пакета для фаервола Linux.


E) Standard ipfrag

IP-фрагментация SYN-пакета. Каждый отправляемый SYN+payload фрагментируется на уровне IP.

ПараметрОписаниеПо умолчанию
ipfrag[=func]Включить IP-фрагментацию. Если без значения --- ipfrag2---
ipfrag_disorderОтправить IP-фрагменты в обратном порядке---
ipfrag_pos_tcp=NПозиция фрагментации TCP (кратно 8)32
ipfrag_pos_udp=NПозиция фрагментации UDP (кратно 8). Для syndata бесполезно --- он только TCP8
ipfrag_next=NIPv6: next protocol во 2-м фрагменте (penetration атака на фаерволы)---

IP-фрагментация — разбиение одного IP-пакета на несколько частей (фрагментов); получатель склеивает их обратно, а DPI, который фрагменты не собирает, не увидит пакет целиком. Это другой уровень, чем TCP-сегментация в multisplit: режется IP-пакет, а не поток данных TCP. Для syndata доступна только она, TCP-сегментации нет (нюанс 1). Примеры: С IP-фрагментацией и С IP-фрагментацией (кастомная позиция); те же параметры для fake — в разделе F) Standard ipfrag.


tls_mod --- модификация TLS в syndata

tls_mod позволяет модифицировать TLS-данные внутри payload перед отправкой. Вызывается после apply_fooling:

if desync.arg.tls_mod then
    dis.payload = tls_mod_shim(desync, dis.payload, desync.arg.tls_mod, nil)
end

Четвёртый аргумент --- nil (реальный payload от приложения). Это ключевой момент, определяющий какие модификации работают, а какие --- нет.

Проще говоря

Вызов tls_mod_shim получает четвёртым аргументом «настоящие данные клиента». В syndata их нет: клиент ещё ничего не отправил, поэтому передаётся nil, то есть «ничего». Модификации, которые только правят поля самого blob (случайные значения, имя сайта), работают. Модификации, которым нужен настоящий ClientHello, пропускаются. Как то же устроено в fake, где настоящий ClientHello есть, описано в разделе Когда tls_mod применяется, а когда нет.

Работающие модификации

МодОписаниеРаботает?Причина
rndРандомизация TLS-полей (session_id, random)ДаКод находится вне блока if(payload)
rndsniРандомизация SNI (замена на случайные символы)ДаКод находится вне блока if(payload)
sni=<str>Замена SNI на указанную строку. Поддерживает sni=%varДаКод находится вне блока if(payload)

Пример: если blob содержит TLS ClientHello с SNI www.example.com, а вы указали tls_mod=sni=google.com, в отправленном SYN-пакете SNI будет заменён на google.com.

dupsid и padencap --- молчаливое игнорирование

МодОписаниеРаботает?Причина
dupsidДублирование session_id из реального ClientHelloМолча игнорируетсяТребует реальный payload (4-й аргумент != NULL)
padencapУпаковка в padding extensionМолча игнорируетсяТребует реальный payload (4-й аргумент != NULL)

Поведение при использовании dupsid/padencap:

АспектРезультат
Вызывают ошибку?НЕТ
Выводят warning в лог?НЕТ
Возвращают false?НЕТ (возвращают true = “успех”)
Применяются?НЕТ --- код просто пропускается

Проще говоря: dupsid копирует идентификатор сессии (session_id) из настоящего ClientHello клиента в фейк, а padencap упаковывает настоящие данные в расширение padding. Если написать tls_mod=dupsid, ничего не сломается и ничего не произойдёт: ни ошибки, ни предупреждения, функция просто сделает вид, что всё выполнила.

Почему dupsid и padencap не работают

В C-коде (protocol.c) логика dupsid и padencap находится внутри блока if (payload):

// protocol.c
if (payload)  // <-- syndata передаёт NULL сюда
{
    if (tls_mod->mod & FAKE_TLS_MOD_DUP_SID)
        // ... копирование session_id из реального ClientHello
        // этот код НЕ выполнится
 
    if (tls_mod->mod & FAKE_TLS_MOD_PADENCAP)
        // ... упаковка реального payload в padding
        // и этот тоже
}
return bRes;  // возвращает true --- "всё ОК"

Причина: dupsid копирует session_id из реального ClientHello клиента в фейк. На фазе SYN реального ClientHello ещё не существует (клиент его не отправлял). Аналогично padencap упаковывает реальный payload в padding extension --- на фазе SYN упаковывать нечего.

Это дизайн-решение, а не баг. Но отсутствие warning в логе может ввести в заблуждение --- конфигурация с tls_mod=dupsid будет молча работать “без dupsid”.

Проще говоря

В коде выше if (payload) значит «если есть настоящие данные». syndata передаёт туда NULL, то есть данных нет, и весь блок пропускается, а функция всё равно возвращает true, «успех». Поэтому предупреждения в логе нет. Хотите использовать dupsid или padencap — берите fake, где настоящий ClientHello есть.


Место в общем скелете

Тело syndata построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в жизненный цикл desync-функции — здесь только отклонения:

Стадия общего скелетаЧто делает syndata
1. Отсев транспортанужен TCP и именно SYN без ACK; после прохождения фазы рукопожатия делает instance_cutoff с пометкой «mission complete»
2. Направлениеотклонение: аргумента dir нет вообще — SYN по определению исходящий
3. Аргументыобязательных нет: без blob подставляются 16 нулевых байт
4. Данныеотклонение: цепочка blob → reasm → payload не используется, берётся только blob
5. Гвардыотклонение: штатных гвардов нет, единственный фильтр — флаги TCP
6. replayне используется: SYN всегда одиночный пакет
7. Своя техникаподставить payload в SYN, применить fooling и tls_mod, отправить rawsend_dissect_ipfrag
8. ВердиктVERDICT_DROP при успешной отправке — оригинальный SYN заменяется подделанным

Отклонений здесь много именно потому, что syndata работает до появления прикладных данных. Отсюда же ограничение, о котором легко забыть: payload должен помещаться в один пакет — сегментации на этапе SYN не существует.

Проще говоря

Расшифровка терминов из таблицы. Гвард — проверка в начале функции, которая отсеивает ненужные пакеты: у большинства функций это проверки размера данных, направления и типа payload (Стадия 5). Reasm — данные, которые движок собрал из нескольких пакетов, придержав их (Стадия 4). Replay — повторный прогон собранных данных через desync-функции (Стадия 6). Вердикт — решение, что делать с перехваченным пакетом (Стадия 8). У SYN нет ни собранных данных, ни повторного прогона: он всегда одиночный пакет.


Псевдокод алгоритма

Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в жизненный цикл desync-функции:

function syndata(ctx, desync)
    -- 1. Проверка: только TCP
    if not desync.dis.tcp then
        -- ICMP пропускаем (не делаем cutoff), остальное --- cutoff
        if not desync.dis.icmp then instance_cutoff(ctx, desync) end
        return
    end
 
    -- 2. Проверка: только SYN (не SYN+ACK)
    if bitand(th_flags, TH_SYN + TH_ACK) == TH_SYN then
 
        -- 3. Глубокая копия dissect (оригинал не трогаем)
        dis = deepcopy(desync.dis)
 
        -- 4. Установка payload: blob или 16 нулей
        dis.payload = blob(desync, arg.blob, "\x00" * 16)
 
        -- 5. Применение fooling к копии
        apply_fooling(desync, dis)
 
        -- 6. tls_mod (если задан)
        if arg.tls_mod then
            dis.payload = tls_mod_shim(desync, dis.payload, arg.tls_mod, nil)
            --                                                           ^^^ payload от приложения = nil
        end
 
        -- 7. Отправка и фрагментация
        if rawsend_dissect_ipfrag(dis, desync_opts(desync)) then
            return VERDICT_DROP  -- оригинальный SYN дропается
        end
 
    else
        -- 8. Не SYN --- миссия завершена
        instance_cutoff(ctx, desync)
    end
end

Ключевые отличия от функций сегментации (multisplit, fakedsplit и т.д.):

  • Нет цикла по позициям --- payload отправляется одним пакетом целиком
  • rawsend_dissect_ipfrag вместо rawsend_payload_segmented --- нет TCP-сегментации, только IP-фрагментация
  • deepcopy --- полная копия dissect, а не создание нового пакета
  • Нет replay/reasm логики --- на фазе SYN нет многопакетных данных

Проще говоря

Псевдокод читается как рецепт. Проверить, что пакет TCP. Проверить, что это SYN без ACK: в комментарии к коду эта проверка так и названа, а выражение bitand(th_flags, TH_SYN + TH_ACK) == TH_SYN оставляет из флагов пакета только SYN и ACK и сравнивает результат с одним SYN. Скопировать пакет, вложить payload, применить fooling и tls_mod. Отправить копию и выбросить оригинал. Если пакет не SYN, отключить функцию для этого соединения. Сегментированную отправку (rawsend_payload_segmented) используют функции вроде multisplit, там же есть цикл по позициям разреза: см. Стадия 7: нарезка и отправка.


Отличия от fake

Обе функции подсовывают DPI ложные данные, но в разное время и по-разному. В таблице слева названа строка сравнения, посередине стоит значение для syndata, справа для fake.

Аспектsyndatafake
Фаза0 (SYN, до handshake)1 (после handshake, есть реальные данные)
Что отправляетSYN-пакет с payloadОтдельный data-пакет (не SYN)
Дефолтный blob16 нулевых байтОбязательный --- без blob ничего не отправится
СегментацияНевозможна (один пакет)Невозможна (один пакет)
Заменяет оригиналДа (VERDICT_DROP на SYN)Нет (оригинал проходит дальше)
FoolingМодифицирует сам SYN --- осторожно!Модифицирует фейк --- сервер должен его отбросить
tls_mod=dupsidМолча игнорируетсяРаботает (есть реальный ClientHello)
tls_mod=padencapМолча игнорируетсяРаботает (есть реальный payload)
ХостлистыТолько --ipcache-hostnameРаботают напрямую (payload уже содержит hostname)
Воздействие на ретрансмиссииДа --- каждый SYN обрабатываетсяНет --- работает с конкретным data-пакетом
directionНе поддерживаетсяПоддерживается
payload filterНе поддерживаетсяПоддерживается
ipidНе поддерживаетсяПоддерживается

Проще говоря

fake — отдельный дополнительный пакет после рукопожатия, который сервер должен отбросить, а оригинальные данные уходят дальше. syndata подменяет сам SYN, который сервер должен принять. Из этого следуют остальные строки таблицы: у syndata нет настоящих данных, поэтому dupsid и padencap не работают, нет hostname, нет фильтров. Подробнее о fake — в разделе Зачем нужен fake, сравнение с его стороны — в Сравнение с syndata и fakedsplit.

Когда использовать syndata вместо fake:

  • DPI начинает трекинг с SYN и не реагирует на фейки после handshake
  • Нужно “отравить” DPI ложными данными до того, как пойдёт реальный трафик
  • В комбинации с wssize и multisplit для многоуровневой атаки

О связке с wssize и multisplit — раздел Комбинирование с другими функциями, а пример с фейком после рукопожатия — Боевая комбинация: syndata + fake + multisplit.


Работа с хостлистами

На фазе 0 (SYN) zapret не знает hostname --- приложение ещё не отправило HTTP-запрос или TLS ClientHello. Поэтому стандартная фильтрация по хостлистам (--hostlist=...) не работает с syndata напрямую.

Единственный способ --- --ipcache-hostname:

nfqws2 --ipcache-hostname \
  --hostlist=blocked.txt \
  --lua-desync=syndata:blob=fake_default_tls

Как это работает:

  1. Первое соединение к IP-адресу проходит без syndata (hostname ещё не известен)
  2. Из первого соединения zapret узнаёт hostname (из TLS ClientHello или HTTP Host) и кеширует привязку IP → hostname
  3. Последующие SYN к этому IP уже проходят через syndata, потому что zapret знает hostname из кеша

Следствие: syndata с хостлистом не подействует на первое соединение к данному IP. Это может быть проблемой для сайтов с уникальными IP или CDN с ротацией адресов.

Та же проблема нулевой фазы у классического --wssize в nfqws — почему из-за неё wssize считается крайней мерой, разобрано в отдельной заметке.

Проще говоря

Хостлист — файл со списком доменов, к которым нужно применять технику (см. Хостлисты). Чтобы сверить соединение со списком, нужно знать имя сайта, а в SYN его нет. Выход — кеш: Zapret запоминает, какому имени соответствовал IP при прошлом соединении. Поэтому первое соединение к IP пройдёт без syndata, а следующие уже с ним. CDN (сеть серверов, раздающих сайт с разных адресов) с ротацией адресов для такой схемы неудобны: каждый новый IP для Zapret «первый». Ключ --ipcache-hostname описан в Zapret flags, а хостлист вместе с ним в профиле показан в примере Боевая комбинация: syndata + fake + multisplit.


Комбинирование с другими функциями

syndata часто используется в цепочке с другими функциями. Порядок инстансов важен!

Инстанс — один вызов --lua-desync=… в профиле. Каждая строка в блоках ниже — отдельный инстанс, и порядок записи определяет порядок, в котором они увидят пакет; подробнее в последовательность аргументов.

Типовая цепочка: wssize + syndata + multisplit

--lua-desync=wssize:wsize=1:scale=6 \
--lua-desync=syndata \
--lua-desync=multisplit:pos=midsld

Что происходит:

  1. SYN-пакет приходит:

    • wssize модифицирует TCP Window Size в SYN (wsize=1, scale=6)
    • syndata добавляет payload в SYN, дропает оригинал, отправляет модифицированный
    • multisplit ничего не делает (SYN, нет данных для нарезки)
  2. Первый data-пакет (например, TLS ClientHello) приходит:

    • wssize --- миссия уже выполнена (cutoff или пропуск)
    • syndata делает instance_cutoff (пакет не SYN)
    • multisplit нарезает payload по позиции midsld

Почему wssize перед syndata: wssize модифицирует TCP Window Size в SYN-пакете. Если поставить после syndata, wssize увидит VERDICT_DROP и не получит SYN. Если перед --- wssize модифицирует dissect, затем syndata использует deepcopy этого (уже модифицированного) dissect.

Проще говоря

Сначала wssize меняет размер окна в SYN (окно — сколько байт получатель готов принять, см. Что делает wssize; в цепочке оно заставляет сервер слать маленькие сегменты), потом syndata копирует уже изменённый SYN, дописывает данные и отправляет вместо оригинала. Если поставить их наоборот, wssize увидит уже принятое решение «оригинал выбросить», и SYN до него не дойдёт. midsld — маркер «середина домена второго уровня», все маркеры перечислены в разделе Маркеры позиций (pos).

Цепочка: syndata + fake + multisplit

--lua-desync=syndata:blob=fake_default_tls \
--lua-desync=fake:blob=fake_default_tls:tcp_md5 \
--lua-desync=multisplit:pos=1,midsld

Многоуровневая атака:

  1. Фаза 0: SYN с фейковым TLS
  2. Фаза 1: фейковый пакет с fooling (tcp_md5)
  3. Фаза 1: реальный payload нарезан на сегменты

Проще говоря

Три функции работают в разное время. syndata действует на SYN. fake после рукопожатия отправляет отдельный пакет-обманку, испорченный fooling tcp_md5 (см. Что такое fooling). multisplit режет настоящий ClientHello на сегменты по pos=1,midsld: после первого байта и посередине домена второго уровня. Оговорка про tcp_md5. В fake его берут, чтобы сервер отбросил фейк: так сказано в статье про fake, где серверы на Linux отбрасывают пакеты с MD5-опцией, если MD5-аутентификация у них не настроена. А в syndata тот же tcp_md5 назван безопасным для SYN, потому что сервер без MD5 опцию игнорирует (нюанс 2). Эти два описания поведения сервера в источниках стоят порознь и не сведены к одному; как поведёт себя конкретный сервер, здесь не утверждается, а подобранную комбинацию стоит проверять по чек-листу.


Нюансы и подводные камни

1. Payload должен помещаться в один пакет

syndata использует rawsend_dissect_ipfrag, а не rawsend_payload_segmented. Это означает, что TCP-сегментация невозможна. Если blob слишком большой и не влезает в один Ethernet frame (с учётом MTU), пакет может быть отброшен сетевым стеком или фрагментирован на IP-уровне (если включён ipfrag).

Проще говоря

SYN — один пакет, и разрезать его на TCP-сегменты нельзя. MTU — наибольший размер пакета, который проходит по каналу за один раз. Другие функции сегментации сами доводят куски до размера MSS (наибольший объём данных в одном TCP-сегменте, см. Автосегментация по MSS); syndata отправляет через rawsend_dissect_ipfrag, где такого разбиения нет (Четыре способа выпустить пакет).

2. Fooling может сломать handshake

В отличие от fake, где fooling должен заставить сервер отбросить пакет, в syndata fooling применяется к пакету, который сервер должен принять. Деструктивные параметры:

  • tcp_seq=N --- сервер не увидит SYN с правильным sequence
  • tcp_ack=N --- невалидный ack в SYN
  • badsum --- сервер отбросит пакет с плохой checksum
  • tcp_flags_unset=SYN --- пакет перестанет быть SYN

Безопасные параметры:

  • tcp_md5 --- сервер без MD5 проигнорирует опцию (RFC 2385)
  • tcp_ts_up --- перестановка timestamp option (не влияет на валидность)
  • tcp_nop_del --- удаление NOP (не влияет на валидность)
  • IPv6 extension headers --- для обхода DPI/фаерволов на пути

Проще говоря

Ломать нельзя то, без чего сервер не примет SYN: seq, ack, контрольную сумму, флаг SYN. Безопасными названы tcp_md5, tcp_ts_up, tcp_nop_del (NOP — пустая опция-заполнитель в заголовке TCP) и IPv6 extension headers. Про tcp_md5 есть оговорка в цепочке с fake. Сверьте с таблицей в разделе B) Standard fooling; каталог способов порчи — в ts-and-fooling.

3. Нет direction, payload filter, ipid

syndata не вызывает direction_cutoff_opposite, direction_check, payload_check или apply_ip_id. Эти стандартные блоки просто отсутствуют в коде функции:

  • direction --- SYN всегда исходящий, фильтрация по направлению бессмысленна
  • payload filter --- в SYN нет данных приложения, фильтровать нечего
  • ipid --- apply_ip_id не вызывается, IP ID не контролируется

Проще говоря

Это стандартные проверки, которые есть у остальных функций: см. Стадию 2 и Стадию 5 жизненного цикла. У syndata они не нужны, поэтому их в коде нет.

4. Все ретрансмиссии SYN обрабатываются

Если первый SYN не дошёл (или SYN+ACK потерялся), TCP-стек отправит ретрансмиссию SYN. syndata обработает каждую ретрансмиссию --- добавит payload, применит модификации, дропнет оригинал. Это полезно: DPI может обрабатывать только первый SYN, а ретрансмиссия с payload “перезапишет” данные в трекере.

Проще говоря

Ретрансмиссия — повторная отправка SYN средствами TCP-стека самой системы, если ответа нет. syndata подменяет каждый такой SYN, поэтому, если DPI обрабатывает только первый SYN, повторный тоже придёт с данными и может «перезаписать» то, что DPI уже запомнил. Трекер здесь — «память» DPI о соединении.

5. deepcopy защищает оригинал

syndata делает deepcopy(desync.dis) и работает с копией. Это значит, что apply_fooling и замена payload не затрагивают оригинальный dissect. Если после syndata стоят другие инстансы и SYN не дропнут (rawsend failed), они увидят немодифицированный пакет.

Проще говоря

Копия — черновик, оригинал — чистовик. Правки идут в копию, оригинал остаётся как был. Оригинал выбрасывается только после успешной отправки копии. Если отправка не удалась, оригинальный SYN остаётся и попадает к следующим инстансам без изменений.

6. instance_cutoff на не-SYN

Как только syndata видит пакет без флага SYN (ACK, PSH+ACK и т.д.), он делает instance_cutoff --- отключает себя для этого потока навсегда. Это правильно: миссия syndata --- только SYN-пакеты. Последующие данные должны обрабатываться другими функциями (multisplit, fake и т.д.).

Как работает cutoff для всех функций сразу, описано в стадии 1 жизненного цикла.

7. ICMP не вызывает cutoff

Если в цепочке инстансов через syndata проходит ICMP-пакет (например, ICMP Destination Unreachable в ответ на SYN), syndata не делает instance_cutoff. Это защита от преждевременного отключения --- ICMP может быть связан с SYN и не означает конец потока.

ICMP — служебный протокол сети, которым сообщают об ошибках доставки, например «получатель недоступен».

8. Дефолтный blob --- 16 нулей, а не обязательный параметр

В отличие от fake, где blob обязателен, syndata имеет fallback --- 16 нулевых байт. --lua-desync=syndata без blob= отправит SYN с 16 байтами 0x00. Это может быть достаточно, чтобы сбить DPI, который не ожидает payload в SYN.

Про обязательный blob у fake — нюанс 5. blob обязателен — без optional будет Lua exception в статье про fake.

9. tls_mod без blob бессмыслен

Если вы указали tls_mod=rnd,rndsni без blob=, tls_mod попытается модифицировать 16 нулевых байт как TLS --- результат будет непредсказуемым. Всегда используйте tls_mod вместе с blob, содержащим валидный TLS ClientHello.

Проще говоря

tls_mod рассчитан на данные, которые выглядят как TLS ClientHello, а 16 нулей таким ClientHello не являются, поэтому результат непредсказуем. Стандартный blob с TLS-фейком, который подходит для пары blob и tls_mod, описан в заметке Блобы.


Миграция с nfqws1

nfqws1 — движок первой версии zapret: там техники включаются флагами --dpi-desync-*, а в nfqws2 — вызовами --lua-desync. Ниже таблица соответствия для syndata и примеры перехода. Общая таблица всех флагов — в разделе Соответствие флагов nfqws1 → nfqws2, аналогичный раздел для сегментации — Миграция с nfqws1 в статье про multisplit.

Соответствие параметров

nfqws1nfqws2
--dpi-desync=syndata--lua-desync=syndata
--dpi-desync-fake-tls=<file>--blob=name:@file + :blob=name
--dpi-desync-fake-tls-mod=rnd,rndsni:tls_mod=rnd,rndsni
--dpi-desync-fooling=md5sig:tcp_md5
--wssize 1:6--lua-desync=wssize:wsize=1:scale=6 (отдельный инстанс, перед syndata)

Проще говоря: в nfqws1 --wssize — глобальный флаг, а в nfqws2 это отдельный вызов wssize, который нужно поставить в цепочке перед syndata; причина описана в разделе Типовая цепочка: wssize + syndata + multisplit.

Пример полной миграции

# nfqws1:
nfqws --dpi-desync=syndata,multisplit \
  --dpi-desync-split-pos=midsld \
  --wssize 1:6
 
# nfqws2 (порядок инстансов важен!):
nfqws2 \
  --lua-desync=wssize:wsize=1:scale=6 \
  --lua-desync=syndata \
  --lua-desync=multisplit:pos=midsld
# nfqws1:
nfqws --dpi-desync=syndata \
  --dpi-desync-fake-tls-mod=rnd,rndsni
 
# nfqws2:
nfqws2 \
  --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,rndsni
# nfqws1:
nfqws --dpi-desync=syndata \
  --dpi-desync-fooling=md5sig
 
# nfqws2:
nfqws2 \
  --lua-desync=syndata:tcp_md5

Практические примеры

Примеры идут от простых к сложным. Каждый блок кода — строки для профиля Zapret 2; аргументы разобраны в разделе Полный список аргументов.

Минимальный (дефолт: 16 нулей в SYN)

--lua-desync=syndata

Отправляет SYN с 16 нулевыми байтами вместо обычного SYN. Простейший вариант --- может сбить DPI, не ожидающий payload в SYN.

С TLS-фейком

--lua-desync=syndata:blob=fake_default_tls

SYN содержит стандартный TLS ClientHello из zapret. DPI может принять это за начало TLS-сессии.

С кастомным blob из файла

--blob=mysyndata:@custom_syn_payload.bin \
--lua-desync=syndata:blob=mysyndata

Загружает произвольный payload из файла и вставляет в SYN.

С inline hex blob

--lua-desync=syndata:blob=0x160301000100

6 байт inline hex --- минимальный TLS record header.

Проще говоря: TLS record — «запись», в которую TLS упаковывает передаваемые данные, а её заголовок стоит в начале записи.

С рандомизацией TLS

--lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,rndsni

TLS-фейк с рандомизированными полями и SNI. Каждый SYN будет содержать уникальные random/session_id/SNI.

С заменой SNI

--lua-desync=syndata:blob=fake_default_tls:tls_mod=sni=google.com

SYN содержит TLS ClientHello с SNI google.com. DPI может записать в трекер “это соединение к google.com” и не блокировать.

С TCP MD5

--lua-desync=syndata:tcp_md5

SYN с 16 нулями и TCP MD5 option. Сервер без MD5 проигнорирует опцию, DPI может быть сбит нестандартным TCP-заголовком.

С TTL fooling (осторожно!)

--lua-desync=syndata:blob=fake_default_tls:ip_ttl=5:ip6_ttl=5

SYN с TTL=5. Если DPI ближе 5 хопов --- увидит фейк. Если сервер дальше 5 хопов --- SYN не дойдёт, handshake не состоится. Ретрансмиссия SYN тоже получит TTL=5 и тоже не дойдёт. Используйте только если точно знаете расстояние до DPI и сервера.

Проще говоря: хоп — один переход пакета между маршрутизаторами. Пакет с TTL=5 успеет пройти пять хопов и дальше пропадёт. Про TTL как способ fooling и его слабые места — каталог способов в заметке про fooling.

С IP-фрагментацией

--lua-desync=syndata:blob=fake_default_tls:ipfrag:ipfrag_disorder

SYN с TLS-фейком, фрагментированный на IP-уровне в обратном порядке. DPI, не собирающий IP-фрагменты, не увидит полный пакет.

С IP-фрагментацией (кастомная позиция)

--lua-desync=syndata:blob=fake_default_tls:ipfrag:ipfrag_pos_tcp=24

Фрагментация по позиции 24 байта (кратно 8). Первый фрагмент содержит TCP-заголовок, второй --- payload.

Типовая боевая комбинация: wssize + syndata + multisplit

--filter-tcp=443 \
  --lua-desync=wssize:wsize=1:scale=6 \
  --lua-desync=syndata \
  --lua-desync=multisplit:pos=midsld

Трёхуровневая атака на DPI: маленькое окно (заставляет сервер слать маленькие сегменты) + payload в SYN + нарезка реального ClientHello.

Боевая комбинация: syndata + fake + multisplit

--filter-tcp=443 --hostlist=blocked.txt --ipcache-hostname \
  --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,rndsni \
  --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
  --lua-desync=multisplit:pos=1,midsld

Максимальная комбинация: фейк в SYN, фейк после handshake (с dupsid --- здесь он работает, потому что это fake, а не syndata), нарезка реального payload.

Проще говоря: --hostlist=blocked.txt ограничивает профиль доменами из списка, а --ipcache-hostname включает кеш имён, без которого syndata хостлист не увидит (см. Работа с хостлистами).

Отладка: повторы отправки

--lua-desync=syndata:blob=fake_default_tls:repeats=3

Каждый SYN с payload отправляется 3 раза. Полезно для отладки или если DPI обрабатывает только N-й пакет.

IPv6: extension headers

--lua-desync=syndata:blob=fake_default_tls:ip6_hopbyhop:ip6_destopt

SYN с hop-by-hop и destination options extension headers. Некоторые DPI/фаерволы не умеют парсить IPv6 extension headers и пропускают пакет.


📚 См. также

  • desync — обзор всех функций --lua-desync и общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты
  • жизненный цикл desync-функции — восемь стадий, общих для всех техник дурения
  • структура desync и диссекта — подробное устройство таблицы desync и диссекта пакета
  • fake — отдельный фейковый пакет после рукопожатия; сравнение с syndata
  • blob — как объявлять и передавать данные для аргумента blob
  • payload — распознавание типов протоколов и фильтр --payload
  • multisplit — нарезка настоящего ClientHello, с которой syndata обычно стоит в одной цепочке
  • ts-and-fooling — что такое fooling и как им портят пакеты
  • verify-strategy — как честно проверить, работает ли подобранная комбинация
  • последовательность аргументов — как выстраивается цепочка инстансов в профиле
  • wssize в zapret — та же нулевая фаза и та же проблема хостлистов
  • Хостлисты — списки доменов, с которыми связан --ipcache-hostname
  • DPI и ТСПУ — как устроена инспекция трафика, против которой работает syndata

Источники: lua/zapret-antidpi.lua:385, lua/zapret-lib.lua, docs/manual.md:3944-3961 из репозитория zapret2.


🤖 Эти статьи открыты — можно обучать на них ИИ

При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: исходник этой заметки · скачать весь репозиторий одним zip-архивом.