🧨 Десинхронизация: флаг --lua-desync

О чём заметка

Обзор --lua-desync — главного механизма обхода DPI в Zapret 2: что это за флаг, как он вызывается для каждого пакета и какие бывают функции-стратегии. Это заметка-хаб: здесь общая картина и каталог функций со ссылками, а детальные аргументы каждой функции — в её собственной заметке (multisplit, fake и т. д.). Устройство входных данных функции (ctx, desync, диссект, track) вынесено в структура desync и диссекта. Где --lua-desync стоит среди других полей — см. профиль и пресет, а где вызов происходит в общем конвейере — схема обработки трафика.

Что такое --lua-desync

DPI (Deep Packet Inspection) блокирует соединения, распознавая в трафике сигнатуры — домен в SNI у TLS, Host: у HTTP. Чтобы обойти блокировку, пакеты нужно так изменить или дополнить, чтобы DPI не собрал корректную картину, а сервер-получатель — собрал. В Zapret 2 вся эта логика вынесена из C-ядра в Lua-функции (почему так — структура проекта), и подключаются они флагом --lua-desync.

--lua-desync вызывает одну Lua-функцию для каждого пакета, прошедшего через фильтры профиля. Синтаксис:

--lua-desync=<функция>[:параметр1=значение1[:параметр2=значение2]]

Флаг можно указать несколько раз — каждый вызов называется инстансом, и инстансы выполняются строго по порядку задания (порядок важен: сначала фейк, потом нарезка — не то же самое, что наоборот; см. последовательность аргументов). Параметры пишутся через двоеточие: param=value — со значением, param без значения = true.

Порядок инстансов — это порядок отправки Если fake стоит перед multisplit, в сеть уходит сначала фейк, потом три куска настоящего запроса. Если наоборот, фейк уходит после кусков — настоящие данные к этому моменту уже ушли. время отправки 1. fake 2. multisplit фейк кусок 1 кусок 2 кусок 3 DPI сначала видит фейк, потом куски настоящего запроса. 1. multisplit 2. fake кусок 1 кусок 2 кусок 3 фейк Фейк опоздал: настоящие куски уже ушли, и DPI их видел.

Как это работает по шагам

Пакет проходит через фильтры профиля (--filter-tcp, --filter-l7, --hostlist) → если соответствует, вызывается Lua-функция инстанса → функция выполняет действие (отправляет фейк, разбивает пакет, модифицирует заголовки) → возвращает вердикт (VERDICT_PASS, VERDICT_MODIFY, VERDICT_DROP), который агрегируется по всей цепочке инстансов.

Путь одного пакета через цепочку инстансов Пакет проходит перехват и разбор в ядре, фильтры профиля, первый инстанс fake и второй инстанс multisplit. Каждая функция отправляет свои пакеты в сеть сразу, а оригиналу возвращает вердикт: fake — PASS, multisplit — DROP. Итог по цепочке — DROP: оригинал выброшен, в сети уже фейк и три куска. Ядро (C) перехват и разбор Фильтры наш ли это пакет? Инстанс 1 fake Инстанс 2 multisplit Вердикт итог по цепочке в сеть: уходит сразу, не дожидаясь вердикта к серверу фейк 1 2 3 пакет совпал: передаём в инстансы возвращает PASS возвращает DROP DROP сильнее PASS Фейк и три куска уже ушли в сеть: их отправили сами функции, пока работали. Оригинал выброшен: иначе сервер получил бы эти же данные второй раз.

🔗 Что функция получает, что отдаёт и как связана с C-ядром

Это раздел про контракт desync-функции: где она живёт, кто её вызывает, что подаётся ей на вход, что она возвращает и как это стыкуется с C-ядром nfqws2. Понимание этого контракта — ключ ко всем отдельным техникам (fake, multisplit, multidisorder и др.): все они устроены по одной схеме.

Где desync-функция стоит в структуре проекта

Zapret 2 состоит из двух половин (подробно — в структура проекта): быстрое C-ядро nfqws2 и Lua-код с логикой обхода. desync-функции — это как раз Lua: они лежат в файле zapret-antidpi.lua (плюс базовые в zapret-lib.lua). Само C-ядро обхода не делает — оно только перехватывает, разбирает и отправляет пакеты, а решение, что с пакетом сделать, отдаёт в Lua.

Момент вызова — предпоследняя стадия конвейера обработки пакета. К этому времени C-ядро уже перехватило пакет из ядра ОС, разобрало его (диссекция), привязало к потоку (conntrack), определило тип пейлоада и выбрало профиль. Дальше ядро идёт по инстансам профиля (каждый --lua-desync=... — один инстанс) строго по порядку и для каждого вызывает Lua-функцию.

Инстанс — это один вызов

Инстанс = один экземпляр вызова Lua-функции из профиля, заданный одним флагом --lua-desync. Одна и та же функция может быть вызвана несколько раз с разными параметрами — это разные инстансы. Порядок инстансов принципиален (см. последовательность аргументов).

Сигнатура: два параметра на входе

Каждая desync-функция объявляется с двумя аргументами:

function fake(ctx, desync)   -- пример: функция fake
  • ctx — «контекст», непрозрачный мост к C-коду. Сам по себе он ничего не значит для чтения; его передают обратно в C-функции (отправка пакетов, cutoff), чтобы те знали, к какому пакету/очереди относится вызов. Грубо говоря, ctx — это «телефонная линия обратно в ядро».
  • desync — большая Lua-таблица со всеми данными обрабатываемого пакета и его потока. Это главный вход: из неё функция читает всё, что ей нужно. Полный разбор всех полей (arg, dis, track, reasm/replay и пр.) — в отдельной заметке структура desync и диссекта.

Проще говоря

desync — это «что за пакет и что мы про него знаем» (данные, которые C передал в Lua). ctx — это «как достучаться обратно до C, чтобы что-то отправить или отключиться». Первое читают, второе используют как ручку для команд.

Что функция делает в середине

Получив desync, инстанс может:

  • читать поля пакета и потока (диссект, тип пейлоада, счётчики conntrack, собранный reasm);
  • создавать копии текущего диссекта, менять в них поля (sequence number, TTL, флаги, payload) и генерировать собственные диссекты (например, фейковый ClientHello);
  • отправлять эти диссекты сырыми пакетами прямо в сеть — через C-функции вроде rawsend_* (именно тут используется ctx). Это происходит немедленно, ещё до того как функция вернёт вердикт;
  • хранить состояние: в самой таблице desync — для передачи данных следующим инстансам этого же пакета; в desync.track.lua_state — для данных, живущих между пакетами одного потока (эту таблицу C выдаёт одну и ту же на каждый пакет потока).

Что функция отдаёт: два вида выхода

У desync-функции, как и у multisplit, два «выхода», и их важно различать.

1. Побочный эффект — уже отправленные пакеты. Основную работу фейка или нарезки функция делает не через return, а вызовами отправки (rawsend_*) прямо посреди тела. К моменту return фейковые/нарезанные пакеты уже улетели в сеть.

2. Возвращаемое значение — вердикт оригинальному пакету. Перехваченный пакет всё ещё ждёт решения. Функция возвращает одно из:

ВердиктЧто значит
VERDICT_PASSне делать с оригиналом ничего (пропустить как есть)
VERDICT_MODIFYв конце всей цепочки отправить изменённое содержимое диссекта
VERDICT_DROPвыбросить оригинал (например, потому что данные уже ушли нарезанными)
nil (без return)бездействие — функция решила не вмешиваться

Агрегация по цепочке. Вердикты всех инстансов профиля объединяются по приоритету: MODIFY перебивает PASS, а DROP перебивает и PASS, и MODIFY. Достаточно одному инстансу вернуть DROP — оригинал будет выброшен, что бы ни вернули соседи (иначе, например, при нарезке сервер получил бы данные дважды).

Как вердикты инстансов сводятся в один Три сценария. Все инстансы вернули PASS — оригинал уходит как есть. Один вернул MODIFY — уйдёт изменённый. Хоть один вернул DROP — оригинал выброшен, что бы ни вернули остальные. Приоритет: DROP сильнее MODIFY, MODIFY сильнее PASS. вердикты инстансов профиля приоритет вердиктов PASS пропустить MODIFY изменить DROP выбросить чем выше, тем сильнее случай 1 из 3 инстанс 1PASS инстанс 2PASS инстанс 3PASS итог для оригиналаPASS Все пропустили — оригинал уходит как есть. случай 2 из 3 инстанс 1PASS инстанс 2MODIFY инстанс 3PASS итог для оригиналаMODIFY MODIFY сильнее PASS — уйдёт изменённый пакет. случай 3 из 3 инстанс 1PASS инстанс 2MODIFY инстанс 3DROP итог для оригиналаDROP DROP сильнее всех: оригинал выброшен, что бы ни вернули соседи.

Управляющий «выход» — cutoff. Кроме вердикта функция может управлять своим будущим: instance_cutoff отключает этот инстанс от дальнейших пакетов потока по направлению, lua_cutoff отключает направление потока от всей Lua-обработки, а инстанс-оркестратор может вовсе взять управление остальной цепочкой на себя. Это способ сэкономить CPU и строить динамические сценарии (подробнее — схема обработки трафика, стадия про cutoff).

Связь C ↔ Lua одной картинкой

Связь ядра на C и Lua-функции Ядро вызывает Lua-функцию, передавая таблицу desync с данными о пакете и потоке. Функция читает данные, через ctx просит ядро отправить сырые пакеты (они уходят в сеть сразу) и в конце возвращает вердикт для оригинала. Ядро сводит вердикты всех инстансов и решает судьбу оригинала. Ядро nfqws2 (C) перехват, разбор, поток Lua-инстанс fake(ctx, desync) Сеть пакеты уходят адресату вызов fake(ctx, desync) desync — данные о пакете и потоке читает desync, делает копиии меняет в них поля ctx: отправь этот пакет (rawsend) команда ядру, ответа ждать не нужно пакет уходит в сеть сразу фейк return VERDICT_PASS | MODIFY | DROP что сделать с оригиналом сводит вердикты всехинстансов и решает судьбу данные — вниз, в Lua команды и вердикт — вверх, в C
  • Из C в Lua передаётся desync — данные: диссект, conntrack, тип пейлоада, аргументы инстанса, replay-инфо. C уже сделал всю «тяжёлую» работу (перехват, разбор, отслеживание потока) и сложил результат в таблицу.
  • Из Lua в C идут команды — через ctx: отправить сырой пакет, испортить checksum, поставить cutoff. Плюс вердикт через return — что C сделать с оригиналом в конце.

Такое разделение и есть главная идея Zapret 2: медленную логику обхода можно править в текстовых Lua-файлах без перекомпиляции C-ядра.


📦 Доступные функции из zapret-antidpi.lua

Карта функций --lua-desync: что каждая делает с трафиком Матрица функций и действий: какие функции отправляют лишний пакет, режут данные, меняют порядок кусков, правят или убирают оригинал, работают с началом соединения. лишний пакет режет данные обратный порядок меняет или убирает оригинал начало соединения Базовые drop send pktmod HTTP http_domcase http_hostcase http_methodeol Нарезка multisplit multidisorder tcpseg Fake fake fakedsplit fakeddisorder hostfakesplit SYN syndata synack synack_split Окно wsize wssize Прочее rst udplen dht_dn основное действие функциитолько если включить параметром

Аргументы

КатегорияАргументОписание
Directiondirin | out | any
Foolingip_ttl=NTTL для IPv4
ip6_ttl=NTTL для IPv6
ip_autottl=delta,min-maxАвто-определение TTL
ip6_autottl=delta,min-maxАвто-определение TTL для IPv6
ip6_hopbyhop[=hex]Добавить hop-by-hop заголовок
ip6_hopbyhop2[=hex]Второй hop-by-hop
ip6_destopt[=hex]Destopt заголовок
ip6_destopt2[=hex]Второй destopt
ip6_routing[=hex]Routing заголовок
ip6_ah[=hex]Authentication заголовок
tcp_seq=NДобавить N к tcp.th_seq
tcp_ack=NДобавить N к tcp.th_ack
tcp_ts=NДобавить N к timestamp
tcp_md5[=hex]Добавить MD5 опцию
tcp_flags_set=Установить TCP флаги
tcp_flags_unset=Снять TCP флаги
tcp_ts_upПереместить timestamp наверх
fool=Кастомная функция fooling
ReconstructbadsumНевалидная L4 checksum
RawsendrepeatsСколько раз отправить пакет
ifoutOverride исходящего интерфейса
fwmarkOverride fwmark
PayloadpayloadСписок разрешённых типов payload
IP_IDip_idseq|rnd|zero|none
ip_id_connСохранять ip_id между пакетами
IPfragipfrag[=func]Функция фрагментации (default: ipfrag2)
ipfrag_disorderОтправить фрагменты в обратном порядке
ipfrag_pos_udpПозиция UDP фрагмента (default: 8)
ipfrag_pos_tcpПозиция TCP фрагмента (default: 32)
ipfrag_nextNext protocol для второго фрагмента IPv6

Базовые:

ФункцияОписание
dropОтбросить пакет
sendОтправить пакет как есть (с возможной модификацией заголовков)
pktmodМодифицировать заголовки пакета (fooling)
ФункцияStd argsСпецифичные args
dropdirection, payload-
senddirection, fooling, ip_id, ipfrag, rawsend, reconstruct-
pktmoddirection, fooling, ip_id-

HTTP модификации:

ФункцияОписание
http_domcaseИзменить регистр домена (HoSt)
http_hostcaseИзменить регистр заголовка Host
http_methodeolМодифицировать конец строки метода

TCP сплит и disorder:

ФункцияОписание
multisplitРазбить пакет на несколько TCP сегментов
multidisorderРазбить + отправить в обратном порядке
tcpsegTCP сегментация
ФункцияStd argsСпецифичные args
multisplitdirection, payload, fooling, ip_id, rawsend, reconstruct, ipfragpos= (default: “2”), seqovl=N, seqovl_pattern=, blob=, nodrop
multidisorderdirection, payload, fooling, ip_id, rawsend, reconstruct, ipfragpos= (default: “2”), seqovl=N, seqovl_pattern=, blob=, nodrop
tcpsegdirection, payload, fooling, ip_id, rawsend, reconstruct, ipfragpos= (обязательный, 2 позиции), seqovl=N, seqovl_pattern=, blob=

Fake-атаки:

ФункцияОписание
fakeОтправить fake пакет
fakedsplitFake + сплит оригинала
fakeddisorderFake + disorder оригинала
hostfakesplitFake только для хоста + сплит
ФункцияStd argsСпецифичные args
fakedirection, payload, fooling, ip_id, rawsend, reconstruct, ipfragblob= (обязательный), tls_mod= (rnd,rndsni,sni=,dupsid,padencap)
fakedsplitdirection, payload, fooling, ip_id, rawsend, reconstructpos= (default: “2”), nofake1, nofake2, nofake3, nofake4, pattern=, seqovl=N, seqovl_pattern=, blob=, nodrop
fakeddisorderdirection, payload, fooling, ip_id, rawsend, reconstructpos= (default: “2”), nofake1-4, pattern=, seqovl=N, seqovl_pattern=, blob=, nodrop
hostfakesplitdirection, payload, fooling, ip_id, rawsend, reconstructhost= (шаблон хоста), midhost=, nofake1, nofake2, disorder_after=, blob=, nodrop

SYN-атаки:

ФункцияОписание
syndataОтправить SYN с данными
synackРабота с SYN/ACK
synack_splitСплит по SYN/ACK

Window size:

ФункцияОписание
wsizeИзменить window size на SYN-ACK
wssizeИзменить window size на всех пакетах

Прочее:

ФункцияОписание
rstОтправить RST
udplenИзменить длину UDP
dht_dnDHT domain name injection

Из zapret-lib.lua:

ФункцияОписание
passНичего не делать (для отладки)
pktdebugВывести содержимое desync в лог
argdebugВывести аргументы в лог
posdebugВывести позиции conntrack
luaexecВыполнить произвольный Lua код

💡 Примеры использования

Простой fake:

--lua-desync=fake:blob=fake_default_tls

Fake с параметрами:

--lua-desync=fake:blob=fake_default_tls:tcp_md5:ip_ttl=3:repeats=5

Multisplit:

--lua-desync=multisplit:pos=1,midsld

Комбинация функций:

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

С фильтром payload:

--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls \
--payload=http_req --lua-desync=fake:blob=fake_default_http

📊 Структура параметров

Как разобрать строку --lua-desync Строка --lua-desync=fake:blob=fake_default_tls:tcp_md5:ip_ttl=3 состоит из флага, имени функции и параметров через двоеточие. Параметр со значением пишется как имя=значение, параметр без значения — просто имя, это то же самое, что true. : : : --lua-desync= вызвать Lua-функцию fake имя функции blob=fake_default_tls параметр со значением tcp_md5 флаг без значения = true ip_ttl=3 параметр со значением Инстансов может быть несколько: каждая строка --lua-desync — отдельный вызов, и идут они по порядку.

Типы параметров:

  • param=value — параметр со значением
  • param — булевый параметр (без значения = true)

🔍 Что получает функция — кратко

Второй параметр — таблица desync — это весь вход функции. Коротко её ключевые части:

  • desync.arg — аргументы этого инстанса (всё после двоеточий в --lua-desync); значения приходят строками, флаг без значения = "" (в Lua истинно).
  • desync.dis — диссект: разобранный на поля текущий пакет (ip/ip6, tcp/udp, payload).
  • desync.l7payload / desync.outgoing — тип содержимого пакета и направление.
  • desync.track — данные потока из conntrack (может отсутствовать!); внутри track.lua_state — память, живущая между пакетами потока.
  • desync.reasm_data / decrypt_data + поля replay* — собранные из нескольких пакетов пейлоады.

Увидеть полное содержимое desync на конкретном пакете можно инстансом pktdebug.

Полный справочник по всем полям — в отдельной заметке

Прототип функции, все вердикты (включая VERDICT_PRESERVE_NEXT), полная структура desync, диссекта (ip/ip6/tcp/udp/icmp, exthdr, tcp options), track со счётчиками, механика reasm/replay, работа с 32-битными sequence и обработка ICMP/raw IP — всё это подробно разобрано в структура desync и диссекта.


🎯 Полный пример

winws2 ^
  --wf-tcp-out=80,443 ^
  --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua ^
  --filter-tcp=80 --filter-l7=http ^
  --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 ^
  --lua-desync=multisplit:pos=method+2 ^
  --new ^
  --filter-tcp=443 --filter-l7=tls ^
  --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni ^
  --lua-desync=multidisorder:pos=1,midsld

✅ Итог

--lua-desync — это сердце nfqws2:

  • 🔹 Вызывает Lua функцию для каждого пакета
  • 🔹 Передаёт параметры через двоеточие
  • 🔹 Можно указывать несколько раз (выполняются последовательно)
  • 🔹 Работает с фильтрами --payload, --out-range, --in-range
  • 🔹 Можно писать свои функции

📋 Полная таблица функций --lua-desync

🔹 Базовые функции (zapret-lib.lua)

ФункцияОписаниеПараметры
passНичего не делает (для отладки)—
pktdebugВыводит содержимое desync в лог—
argdebugВыводит аргументы функции в лог—
posdebugВыводит позиции conntrack в лог—
luaexecВыполняет произвольный Lua кодcode=<lua_code>

🔹 Функции из zapret-antidpi.lua

Базовые действия

ФункцияАналог nfqws1ОписаниеПараметры
drop—Отбросить пакетdir, payload
send--dupОтправить копию пакетаdir, fooling, ip_id, ipfrag, rawsend, reconstruct
pktmod--origМодифицировать текущий пакетdir, fooling, ip_id

HTTP модификации

ФункцияАналог nfqws1ОписаниеПараметры
http_domcase--domcaseИзменить регистр домена (HoSt)dir
http_hostcase--hostcaseИзменить регистр заголовка Hostdir, spell=<str> (4 символа)
http_methodeol--methodeolМодифицировать EOL методаdir, method=cr|lf|crlf|lfcr, no_space

SYN-атаки

ФункцияАналог nfqws1ОписаниеПараметры
syndata--dpi-desync=syndataОтправить SYN с даннымиblob=<blob>, tls_mod=<list>, fooling, rawsend, reconstruct, ipfrag
synack—Отправить SYN-ACKfooling, rawsend, reconstruct, ipfrag
synack_split—Сплит по SYN-ACKpos=<posmarker>, seqovl=N, seqovl_pattern=<blob>

Window size

ФункцияАналог nfqws1ОписаниеПараметры
wsize--wssizeИзменить window size на SYN-ACKwsize=N, scale=N
wssize--wssizeИзменить window size на всех пакетахwsize=N, scale=N

Ограничения у них те же, что у классического --wssize в nfqws: подмена окна происходит на этапе установления соединения, поэтому фильтрация возможна только по l3/l4 (IP-адреса и порты), хостлисты — лишь через --ipcache-hostname, а старт всех отфильтрованных соединений замедляется. Разбор, почему wssize — крайняя мера, — в отдельной заметке.


RST

ФункцияАналог nfqws1ОписаниеПараметры
rst--dpi-desync=rstОтправить RSTdir, payload, fooling, ip_id, rawsend, reconstruct, ipfrag, rstack

Fake-атаки

ФункцияАналог nfqws1ОписаниеПараметры
fake--dpi-desync=fakeОтправить fake пакетblob=<blob> (обязательный), tls_mod=<list>, dir, payload, fooling, ip_id, rawsend, reconstruct, ipfrag

Сплит и disorder

ФункцияАналог nfqws1ОписаниеПараметры
multisplit--dpi-desync=multisplitРазбить на TCP сегментыpos=<posmarker_list>, seqovl=N, seqovl_pattern=<blob>, blob=<blob>, nodrop
multidisorder--dpi-desync=multidisorderРазбить + обратный порядокpos=<posmarker_list>, seqovl=<posmarker>, seqovl_pattern=<blob>, blob=<blob>, nodrop
tcpseg—TCP сегментация по диапазонуpos=<range> (обязательный), seqovl=N, seqovl_pattern=<blob>, blob=<blob>

Fake + сплит комбинации

ФункцияАналог nfqws1ОписаниеПараметры
hostfakesplit--dpi-desync=hostfakesplitFake только для хоста + сплитhost=<template>, midhost=<posmarker>, nofake1, nofake2, disorder_after=<posmarker>, blob=<blob>, nodrop
fakedsplit--dpi-desync=fakedsplitFake + сплит оригиналаpos=<posmarker>, nofake1, nofake2, nofake3, nofake4, pattern=<blob>, seqovl=N, seqovl_pattern=<blob>, blob=<blob>, nodrop
fakeddisorder--dpi-desync=fakeddisorderFake + disorder оригиналаpos=<posmarker>, nofake1, nofake2, nofake3, nofake4, pattern=<blob>, seqovl=<posmarker>, seqovl_pattern=<blob>, blob=<blob>, nodrop

UDP

ФункцияАналог nfqws1ОписаниеПараметры
udplen--dpi-desync=udplenИзменить длину UDP пакетаdir, payload, min=N, max=N, increment=N (по умолч. 2), pattern=<blob>, pattern_offset=N
dht_dn--dpi-desync=tamper (dht)DHT domain name injectiondir, dn=N (по умолч. 2)

🔧 Стандартные параметры (применимы ко многим функциям)

Direction (направление)

ПараметрОписание
dir=inТолько входящие пакеты
dir=outТолько исходящие (по умолчанию)
dir=anyОба направления

Payload фильтр

ПараметрОписание
payload=<type_list>Фильтр по типу payload

Fooling (обманки)

Три способа сделать пакет, который читает DPI, но не принимает сервер Фейковый пакет идёт к серверу через DPI. Первый способ: ip_ttl или ip_autottl — TTL заканчивается в пути, до сервера пакет не доходит. Второй: badsum или tcp_md5 — пакет доходит, но сервер отбрасывает его из-за неверной контрольной суммы или подписи. Третий: tcp_seq или tcp_ack — сервер отбрасывает пакет, потому что номера не сходятся с состоянием соединения. ip_ttl, ip_autottl TTL кончается в пути: до сервера пакет не доходит клиент DPI сервер фейк разобрал фейк TTL 4 TTL 3 TTL 2 TTL 1 TTL 0 фейк ✕ TTL = 0: выброшен в пути badsum, tcp_md5 пакет доходит, сервер отбрасывает: сумма или подпись не сходятся клиент DPI сервер фейк разобрал фейк фейк ✕ сервер отбросил:сумма или подпись неверны tcp_seq, tcp_ack пакет доходит, сервер отбрасывает: номера не из этого соединения клиент DPI сервер фейк разобрал фейк фейк ✕ сервер отбросил:номера не сходятся Во всех трёх случаях DPI фейк разбирает, а серверу он не достаётся или не принимается.
ПараметрОписание
ip_ttl=NУстановить TTL IPv4
ip6_ttl=NУстановить Hop Limit IPv6
ip_autottl=delta,min-maxАвтоматический TTL
ip6_autottl=delta,min-maxАвтоматический Hop Limit
ip6_hopbyhop[=hex]Добавить Hop-by-Hop заголовок
ip6_hopbyhop2[=hex]Второй Hop-by-Hop
ip6_destopt[=hex]Добавить Destination Options
ip6_destopt2[=hex]Второй Destination Options
ip6_routing[=hex]Добавить Routing заголовок
ip6_ah[=hex]Добавить Authentication Header
tcp_seq=NДобавить к TCP sequence
tcp_ack=NДобавить к TCP ack
tcp_ts=NДобавить к timestamp
tcp_md5[=hex]Добавить TCP MD5 опцию
tcp_flags_set=<list>Установить TCP флаги
tcp_flags_unset=<list>Снять TCP флаги
tcp_ts_upПереместить timestamp наверх
fool=<function>Пользовательская функция обманки

Reconstruct

ПараметрОписание
badsumНевалидная контрольная сумма L4

Rawsend

ПараметрОписание
repeats=NКоличество повторов
ifout=<iface>Интерфейс отправки
fwmark=Nfwmark пакета

IP ID

ПараметрОписание
ip_id=seq|rnd|zero|noneПолитика IP ID
ip_id_connСохранять IP ID между пакетами

IP фрагментация

ПараметрОписание
ipfrag[=function]Включить IP фрагментацию
ipfrag_disorderОтправить фрагменты в обратном порядке
ipfrag_pos_tcp=NПозиция фрагментации TCP (кратно 8)
ipfrag_pos_udp=NПозиция фрагментации UDP (кратно 8)
ipfrag_next=NNext proto для второго фрагмента

TLS модификации

ПараметрОписание
tls_mod=rndРандомизировать поле random
tls_mod=rndsniСлучайный SNI
tls_mod=sni=<domain>Установить конкретный SNI
tls_mod=dupsidКопировать Session ID
tls_mod=padencapPadding encapsulation

📍 Position markers (маркеры позиций)

Маркеры позиций pos: где именно резать запрос Верхняя схема — HTTP-запрос по символам: маркеры method+2, host, sld, midsld, endsld и endhost указывают места разреза. Из pos=host,midsld,endhost запрос делится на четыре сегмента. Нижняя схема — TLS ClientHello без масштаба: маркеры extlen, sniext, host, midsld и endhost. HTTP-запрос по символам G E T / H T T P / 1 . 1 ␍ ␊ H o s t : w w w . y o u t u b e . c o m ␍ ␊ www.youtube.com method+2 method+2 — метод и ещё 2 байта: GE|T host host — начало имени сайта, сразу после «Host: » sld sld — начало домена второго уровня (youtube) midsld midsld — середина домена второго уровня: you|tube endsld endsld — конец домена второго уровня endhost endhost — конец имени сайта pos=host,midsld,endhost — четыре сегмента GET / HTTP/1.1␍␊Host: сегмент 1 www.youсегмент 2 tube.comсегмент 3 ␍␊сегмент 4 TLS ClientHello — схема без масштаба заголовки версия, random, шифры … длина другие расширения заг. SNI www.youtube.com остальное extlen extlen — где записана длина блока расширений sniext sniext — начало расширения SNI, в нём лежит имя сайта host host — начало имени сайта внутри SNI midsld midsld — середина you|tube, как и в HTTP endhost endhost — конец имени сайта Маркеры привязаны к устройству запроса: одна и та же запись pos работает для HTTP и TLS.
МаркерОписание
NАбсолютная позиция (число)
-NПозиция с конца
hostНачало хоста
endhostКонец хоста
sldSecond-level domain
midsldСередина SLD
endsldКонец SLD
methodHTTP метод
extlenTLS extensions length
sniextTLS SNI extension
marker+NМаркер + смещение
marker-NМаркер - смещение

💡 Примеры

# Простой fake
--lua-desync=fake:blob=fake_default_tls:tcp_md5
 
# Fake с TTL и повторами
--lua-desync=fake:blob=fake_default_tls:ip_ttl=3:repeats=5:tls_mod=rnd,rndsni
 
# Multisplit с seqovl
--lua-desync=multisplit:pos=1,midsld:seqovl=10
 
# Fakedsplit
--lua-desync=fakedsplit:pos=midsld:nofake1:tcp_md5
 
# Комбинация fake + multidisorder
--lua-desync=fake:blob=fake_default_tls:tcp_md5 --lua-desync=multidisorder:pos=1,midsld
 
# UDP length
--lua-desync=udplen:increment=4:min=50

Соответствие флагов nfqws1 → nfqws2

В nfqws1 были готовые флаги --dpi-desync-fooling=. В nfqws2 их нет — вместо этого используются отдельные параметры для каждого действия.


📋 Таблица соответствия

nfqws1 флагnfqws2 параметрОписание
md5sigtcp_md5Добавить TCP MD5 signature опцию
badsumbadsumНевалидная контрольная сумма L4
badseqtcp_seq=-10000Сдвинуть TCP sequence (для SYN)
badseqtcp_ack=-66000Сдвинуть TCP ack (для данных)
datanoacktcp_flags_unset=ackСнять флаг ACK
badacktcp_ack=-66000Сдвинуть TCP ack
hopbyhopip6_hopbyhopIPv6 Hop-by-Hop header
hopbyhop2ip6_hopbyhop2Второй Hop-by-Hop header
destoptip6_destoptIPv6 Destination Options
destopt2ip6_destopt2Второй Destination Options
ipfrag1ipfragIP фрагментация

🔧 Детали каждого флага

md5sig → tcp_md5

Добавляет TCP опцию MD5 signature (RFC 2385). DPI не может проверить подпись.

# nfqws1
--dpi-desync-fooling=md5sig
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:tcp_md5

badsum → badsum

Делает контрольную сумму TCP/UDP невалидной. Сервер отбросит пакет.

# nfqws1
--dpi-desync-fooling=badsum
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:badsum

badseq → tcp_seq / tcp_ack

В nfqws1 badseq применял разные значения для SYN и обычных пакетов:

  • SYN пакеты: tcp_seq=-10000
  • Обычные пакеты: tcp_ack=-66000
# nfqws1
--dpi-desync-fooling=badseq
 
# nfqws2 (для обычных пакетов)
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000
 
# nfqws2 (для SYN)
--lua-desync=fake:blob=fake_default_tls:tcp_seq=-10000

badack → tcp_ack

Сдвигает TCP acknowledgment number.

# nfqws1
--dpi-desync-fooling=badack
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000

Важно! Для Linux нужен tcp_ts_up чтобы работало без badseq:

--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_ts_up

datanoack → tcp_flags_unset=ack

Снимает флаг ACK с пакета.

# nfqws1
--dpi-desync-fooling=datanoack
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack

hopbyhop → ip6_hopbyhop

Добавляет IPv6 Hop-by-Hop extension header.

# nfqws1
--dpi-desync-fooling=hopbyhop
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip6_hopbyhop

hopbyhop2 → ip6_hopbyhop2

Добавляет второй Hop-by-Hop header (нестандартно, ломает обработку).

# nfqws1
--dpi-desync-fooling=hopbyhop2
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip6_hopbyhop2

destopt → ip6_destopt

Добавляет IPv6 Destination Options header.

# nfqws1
--dpi-desync-fooling=destopt
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip6_destopt

ipfrag1 → ipfrag

IP фрагментация пакета.

# nfqws1
--dpi-desync-fooling=ipfrag1
 
# nfqws2
--lua-desync=send:ipfrag

💡 Примеры комбинаций

Классический fake с md5sig:

# nfqws1
--dpi-desync=fake --dpi-desync-fooling=md5sig
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:tcp_md5

Fake с badseq + md5sig:

# nfqws1
--dpi-desync=fake --dpi-desync-fooling=badseq,md5sig
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_md5

Fake с TTL:

# nfqws1
--dpi-desync=fake --dpi-desync-ttl=3
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip_ttl=3

Fake с autottl:

# nfqws1
--dpi-desync=fake --dpi-desync-autottl=-1,3-20
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip_autottl=-1,3-20:ip6_autottl=-1,3-20

Fakedsplit с badseq + md5sig:

# nfqws1
--dpi-desync=fakedsplit --dpi-desync-fooling=badseq,md5sig --dpi-desync-split-pos=2
 
# nfqws2
--lua-desync=fakedsplit:pos=2:tcp_ack=-66000:tcp_md5

IPv6 hopbyhop + destopt:

# nfqws1
--dpi-desync=fake --dpi-desync-fooling=hopbyhop,destopt
 
# nfqws2
--lua-desync=fake:blob=fake_default_tls:ip6_hopbyhop:ip6_destopt

📝 Полный список fooling параметров nfqws2

ПараметрЗначениеОписание
tcp_md5[=hex]TCP MD5 signature (16 байт)
tcp_seq=NСдвиг TCP sequence
tcp_ack=NСдвиг TCP ack
tcp_ts=NСдвиг TCP timestamp
tcp_flags_set=listУстановить TCP флаги
tcp_flags_unset=listСнять TCP флаги
tcp_ts_up—Переместить timestamp наверх
badsum—Невалидная контрольная сумма
ip_ttl=NУстановить TTL
ip6_ttl=NУстановить Hop Limit
ip_autottl=delta,min-maxАвто TTL
ip6_autottl=delta,min-maxАвто Hop Limit
ip6_hopbyhop[=hex]Hop-by-Hop header
ip6_hopbyhop2[=hex]Второй Hop-by-Hop
ip6_destopt[=hex]Destination Options
ip6_destopt2[=hex]Второй Destination Options
ip6_routing[=hex]Routing header
ip6_ah[=hex]Authentication header

⚡ Быстрая шпаргалка

# md5sig
:tcp_md5
 
# badsum
:badsum
 
# badseq (для данных)
:tcp_ack=-66000
 
# badseq (для SYN)
:tcp_seq=-10000
 
# badack
:tcp_ack=-66000:tcp_ts_up
 
# datanoack
:tcp_flags_unset=ack
 
# TTL
:ip_ttl=3:ip6_ttl=3
 
# autottl
:ip_autottl=-1,3-20:ip6_autottl=-1,3-20
 
# IPv6 headers
:ip6_hopbyhop:ip6_destopt

Нет, badseq и badack — это разные вещи!


📊 Разница

ФлагЧто меняетПоле TCPТипичное значение
badseqSequence numbertcp.th_seq-10000
badackAcknowledgment numbertcp.th_ack-66000
badseq и badack меняют разные поля TCP Сверху: tcp_seq сдвигает номер последовательности — настоящий пакет попадает в окно приёма сервера и принимается, фейк со сдвигом на −10000 оказывается далеко за окном и отбрасывается. Снизу: tcp_ack сдвигает номер подтверждения на −66000 — подтверждение не сходится с тем, что отправлял сервер. badseq: tcp_seq=-10000 сдвигает номер последовательности (seq) номера байтов потока → окно приёма сервера: сюда пускают настоящий: seq=1000 принят фейк: seq − 10000 далеко за окномотброшен badack: tcp_ack=-66000 сдвигает номер подтверждения (ack) что сервер отправил клиенту ack: «получил всё до сюда» нормальный ack ack − 66000 подтверждает то,чего сервер не слал Это разные поля: badseq — seq, badack — ack. Для ack на Linux нужен ещё tcp_ts_up — схема в разделе ниже.

🔬 Как работают

badseq (tcp_seq)

Сдвигает номер последовательности (sequence number) пакета.

Оригинальный пакет:  seq=1000, ack=5000
С badseq (-10000):   seq=-9000 (в 32-битном счёте 4294958296), ack=5000
  • Сервер видит пакет с “неправильным” seq
  • Пакет не попадает в окно приёма → отбрасывается
  • DPI может обработать пакет, но сервер его игнорирует

badack (tcp_ack)

Сдвигает номер подтверждения (acknowledgment number) пакета.

Оригинальный пакет:  seq=1000, ack=5000
С badack (-66000):   seq=1000, ack=-61000
  • Сервер видит пакет с “неправильным” ack
  • Пакет может быть отброшен или обработан (зависит от ОС)
  • На Linux с опцией timestamp пакет может быть принят!

⚠️ Важный нюанс для Linux

На Linux badack без badseq может не работать, если есть TCP timestamp опция!

Linux отбрасывает пакеты с плохим ack только если timestamp опция идёт первой в списке TCP опций.

Поэтому в nfqws2 есть параметр tcp_ts_up — он перемещает timestamp наверх:

# Чтобы badack работал на Linux:
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_ts_up

📝 В nfqws1

В nfqws1 флаг badseq был универсальным и применял разные значения в зависимости от типа пакета:

Тип пакетаЧто менялосьЗначение
SYNtcp_seq-10000
Данныеtcp_ack-66000

Поэтому --dpi-desync-fooling=badseq фактически менял либо seq, либо ack в зависимости от контекста.


🎯 В nfqws2

В nfqws2 нет такой “магии” — вы явно указываете что хотите изменить:

# Изменить sequence (для SYN)
:tcp_seq=-10000
 
# Изменить ack (для данных)
:tcp_ack=-66000
 
# Изменить оба
:tcp_seq=-10000:tcp_ack=-66000

💡 Рекомендации

Для fake пакетов (данные):

# Вариант 1: badack с tcp_ts_up (надёжнее на Linux)
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_ts_up
 
# Вариант 2: md5sig (проще, работает везде)
--lua-desync=fake:blob=fake_default_tls:tcp_md5
 
# Вариант 3: TTL (если известно расстояние до DPI)
--lua-desync=fake:blob=fake_default_tls:ip_ttl=3

Для syndata (SYN пакеты):

--lua-desync=syndata:blob=fake_default_tls:tcp_seq=-10000

✅ Итог

badseqbadack
ПолеSequenceAcknowledgment
nfqws2 параметрtcp_seq=Ntcp_ack=N
Типичное значение-10000-66000
Для SYN✅ Да❌ Нет
Для данных⚠️ Редко✅ Да
Нужен tcp_ts_upНетДа (на Linux)

Короткий ответ: badseq меняет seq, badack меняет ack — это разные поля TCP заголовка.

tcp_ts_up — нужен не всегда, но рекомендуется для Linux


🔍 Когда tcp_ts_up НУЖЕН

Только на Linux, и только если:

  1. Вы используете tcp_ack (badack) без tcp_seq (badseq)
  2. В пакете есть TCP timestamp опция

Почему?

Linux проверяет timestamp опцию для валидации пакетов. Но есть баг/особенность:

  • Linux отбрасывает пакеты с плохим ack только если timestamp идёт первой в списке TCP опций
  • Если timestamp не первая — пакет может быть принят несмотря на плохой ack!

tcp_ts_up перемещает timestamp в начало списка опций → Linux корректно отбрасывает пакет.

Зачем tcp_ts_up: Linux смотрит на первую опцию Linux отбрасывает пакет с плохим ack, только если опция timestamp стоит первой в списке TCP-опций. Без tcp_ts_up timestamp не первая, и пакет принимается. С tcp_ts_up она переносится на первое место, и пакет отбрасывается. без tcp_ts_up TCP-опции пакета с плохим ack опция 1 опция 2 timestamp опция 4 Linux смотрит сюда timestamp не первая → плохой ack не проверяется → пакет принят: обман не сработал с tcp_ts_up TCP-опции опция 1 опция 2 timestamp опция 4 Linux смотрит сюда timestamp первая → ack проверяется → он плохой → пакет отброшен: обман сработал

✅ Когда tcp_ts_up НЕ нужен

  1. Целевой сервер — не Linux (Windows, BSD и др.)
  2. Используете tcp_seq (badseq) — тогда пакет отбрасывается по другой причине
  3. Используете tcp_md5 — md5sig работает независимо
  4. Используете ip_ttl — пакет не дойдёт до сервера
  5. В пакете нет timestamp опции — нечего перемещать

📊 Таблица: когда нужен tcp_ts_up

FoolingLinuxWindows/BSDtcp_ts_up нужен?
tcp_ack только⚠️ Может не работать✅ РаботаетДа (для Linux)
tcp_seq только✅ Работает✅ РаботаетНет
tcp_ack + tcp_seq✅ Работает✅ РаботаетНет
tcp_md5✅ Работает✅ РаботаетНет
ip_ttl✅ Работает✅ РаботаетНет

💡 Рекомендация

Если не уверены — добавляйте tcp_ts_up. Он не навредит, но гарантирует работу на Linux:

# Безопасный вариант (работает везде)
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_ts_up
 
# Или просто используйте md5sig — проще и надёжнее
--lua-desync=fake:blob=fake_default_tls:tcp_md5

🎯 Короткий ответ

Нет, не обязательно, но:

  • На Linux без tcp_ts_up badack может не сработать
  • На Windows/BSD работает и без него
  • Рекомендуется добавлять для универсальности

autottl — формат обязателен, значений по умолчанию нет!


❌ Нельзя использовать без цифр

# ОШИБКА! Так нельзя:
--lua-desync=fake:blob=fake_default_tls:ip_autottl
 
# ОШИБКА! Так тоже нельзя:
--lua-desync=fake:blob=fake_default_tls:ip_autottl=

Будет ошибка:

parse_autottl: invalid value '...'

✅ Обязательный формат

ip_autottl=delta,min-max
ip6_autottl=delta,min-max
ПараметрОписание
deltaСдвиг от вычисленного hop count (может быть отрицательным)
minМинимальное значение TTL
maxМаксимальное значение TTL

💡 Примеры

# Стандартный вариант: delta=-1, диапазон 3-20
:ip_autottl=-1,3-20:ip6_autottl=-1,3-20
 
# Более агрессивный: delta=-2
:ip_autottl=-2,3-20:ip6_autottl=-2,3-20
 
# Осторожный: delta=0 (точно до DPI)
:ip_autottl=0,3-20:ip6_autottl=0,3-20

🔬 Как работает autottl

  1. Получает incoming TTL из ответа сервера (SYN-ACK)
  2. Угадывает начальный TTL сервера (64, 128 или 255)
  3. Вычисляет hop count = начальный TTL - incoming TTL
  4. Применяет формулу: fake_ttl = hop_count + delta
  5. Ограничивает результат диапазоном min-max

Пример расчёта:

Incoming TTL = 52
Угаданный начальный TTL = 64
Hop count = 64 - 52 = 12
delta = -1
Результат = 12 + (-1) = 11

Если min=3, max=20: итоговый TTL = 11 ✓
Как ip_autottl считает TTL фейка Из TTL ответа сервера (52) угадывается стартовое значение 64, число хопов до сервера равно 64 − 52 = 12. К нему прибавляется delta (−1), получается 11, результат зажимается в диапазон min–max. Фейк с TTL 11 умирает на 11-м хопе — на один раньше сервера, но после DPI. 1. TTL ответа сервера (SYN-ACK) → стартовое значение старт 64 старт 128 старт 255 0 64 128 255 52пришёл с TTL 52 угадали: сервер стартовал с 64 2. Считаем хопов до сервера = 64 − 52 = 12 TTL фейка = 12 + (−1) = 11← delta из ip_autottl=-1,3-20 рамки 3–20: 11 помещается → TTL = 11 3. Что это значит в пути клиент 1 2 3 4 DPI 5 6 7 8 9 10 11 сервер 12 хопов TTL 11 TTL 0 ✕ умер за хоп до сервера DPI на пути успел его прочитать

⚠️ Важно

  • Требует conntrack — нужно видеть входящие пакеты для определения TTL
  • Не работает если incoming TTL не попадает в диапазоны (32-64, 96-128, 223-255)
  • Fallback на ip_ttl — если autottl не сработал, используется ip_ttl если указан
# С fallback на фиксированный TTL:
:ip_ttl=5:ip_autottl=-1,3-20

📝 Типичное использование

# Полный пример с autottl и fallback
--lua-desync=fake:blob=fake_default_tls:tcp_md5:ip_ttl=5:ip_autottl=-1,3-20:ip6_ttl=5:ip6_autottl=-1,3-20

📚 См. также

Где --lua-desync в общей картине:

  • Что такое профиль — блок, внутри которого живёт --lua-desync; как перебирать стратегии под свой DPI
  • Пресет — файл стратегии целиком (набор профилей)

Соседние поля профиля:

  • filter — фильтры (--filter-tcp/udp/l7, --hostlist, --ipset): какой трафик попадёт в desync
  • payload — типы содержимого пакета (--payload)
  • out-range — на каких по счёту пакетах срабатывать (--out-range)
  • blob — именованные заготовки пакетов (blob=…), на которые ссылаются fake/syndata

Отдельные техники десинхронизации:

Как это встроено в общую картину:

  • Жизненный цикл desync-функции — общий скелет всех техник: восемь стадий от отсева чужого транспорта до вердикта, и чем каждая функция от скелета отличается
  • Прототип и структура desync — полный справочник входных данных функции: ctx/desync, вердикты, диссект, track, reasm/replay, ICMP/raw IP
  • Структура проекта — почему логика обхода вынесена в Lua, а C-ядро её только вызывает
  • Схема обработки трафика — где именно в конвейере пакета вызываются desync-инстансы
  • multisplit — детальный разбор одной функции: что она получает в desync и что возвращает

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

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