fakeddisorder — TCP-сегментация с фейками в обратном порядке (zapret2 / nfqws2)
Коротко и по-простому
Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке Host: запроса. ТСПУ (Технические средства противодействия угрозам — оборудование DPI, то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения и по нему решает, что делать с соединением дальше.
HTTP-запросы и TLS-соединения передаются по протоколу TCP, а TCP отправляет данные кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер строго по этим номерам. Поэтому ему безразлично, в каком порядке куски доехали: если конец запроса пришёл раньше начала, сервер подержит его в буфере, дождётся начала и соберёт исходный поток. Почему это гарантирует именно TCP, разобрано в нюансе «Работает только с TCP» из статьи про multisplit.
fakeddisorder использует эту особенность TCP дважды, и понять его проще, если сначала разобрать два приёма по отдельности. Первый приём — фейки. Фейк — это пакет-обманка: он несёт мусорные данные (по умолчанию нули), но у него тот же размер и тот же seq, что у настоящего куска, поэтому со стороны он похож на повторную отправку этого куска. Если сервер примет такой пакет, мусор попадёт в поток и соединение сломается (нюанс 4). Поэтому фейкам нарочно портят заголовок так, чтобы сервер их отбросил, а DPI всё равно мог прочитать; эту порчу называют fooling (Что такое fooling). В большинстве команд этой статьи фейкам сдвигают номер подтверждения (ACK, поле «что подтверждено») на 66000: tcp_ack=-66000. Из опций порчи к настоящим кускам применяется только tcp_ts_up (плюс политика нумерации ip_id): он ничего не портит, а поднимает опцию TCP timestamp в начало заголовка, и без этого Linux-сервер ненадёжно отбрасывает фейки с неверным ACK; подробнее в нюансе 5 и в разделе Применение fooling и reconstruct.
Второй приём — обратный порядок (по-английски disorder): куски отправляются не от начала к концу, а наоборот. Серверный TCP-стек рассчитан на куски вразнобой, он буферизирует их и ждёт недостающие. DPI, как правило, такой роскоши не имеет: получив конец запроса первым, он видит данные без начала и может не распознать протокол; DPI с пересборкой потока теоретически справится, но у многих ограниченный буфер или таймаут, и они не дожидаются всех частей. Подробнее — в разделе Почему disorder ломает DPI статьи про multidisorder.
Теперь шаги самого fakeddisorder. Движок Zapret 2 (nfqws2) перехватывает исходящий пакет с ClientHello или HTTP-запросом, и функция делает четыре вещи. Сначала режет данные пакета в одном месте на две части: место задаёт аргумент pos, это может быть число или маркер, привязанный к устройству запроса, например midsld — середина домена второго уровня, то есть главной части имени сайта (у www.youtube.com это youtube); по умолчанию разрез после второго байта, а список позиций, как у multisplit, здесь не принимается — только один маркер (Маркер позиции (pos)). Потом готовит по два фейка на каждую часть с тем же размером и тем же seq. Потом отправляет шесть пакетов, начиная со второй части: FAKE2, REAL2, FAKE2, FAKE1, REAL1, FAKE1, где REAL — настоящий кусок, FAKE — фейк, а цифра — номер части запроса. В конце выбрасывает перехваченный оригинал, чтобы те же данные не ушли второй раз. Подробная раскладка по пакетам — в разделе Порядок отправки сегментов (6 пакетов).
Сервер получает все шесть пакетов, четыре фейка отбрасывает из-за испорченного ACK, принимает REAL2, держит его в буфере до прихода REAL1 и по номерам seq собирает ровно тот запрос, который отправил браузер, поэтому отвечает как обычно. DPI видит другое: по три сегмента на каждую позицию (настоящий и два фейка, со стороны они похожи на повторные отправки одного и того же) и конец запроса раньше начала. Какой из трёх сегментов настоящий, DPI не знает, и он может проглотить фейки как реальные данные. Как это выглядит с обеих сторон, показано в разделах Что видит DPI и Что видит сервер статьи про fakedsplit, где схема та же, но порядок прямой. Сработает ли это на конкретном провайдере, заранее сказать нельзя: результат зависит от конкретного DPI, способ порчи фейков выбирают по месту запуска и подтверждают тестами (каталог способов), а итог проверяют на своём провайдере по чек-листу честной проверки.
Команда из быстрого старта fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up режет ClientHello посередине домена второго уровня, но первой отправляет вторую часть, и каждая часть окружена фейками того же размера и с тем же seq: FAKE2, REAL2, FAKE2, FAKE1, REAL1, FAKE1. ТСПУ получает по три сегмента на каждую позицию, да ещё конец запроса раньше начала; сервер отбрасывает фейки по испорченному номеру подтверждения (ACK сдвинут на 66000) и держит REAL2 в буфере до прихода REAL1. Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.
Самая короткая команда включает fakeddisorder со значениями по умолчанию: один разрез после второго байта (pos=2), обработка только распознанных протоколов (payload=known) и только исходящих пакетов (dir=out), а фейкам портится ACK. Строку --lua-desync=… пишут в профиль — набор правил Zapret 2 для определённого трафика:
--lua-desync=fakeddisorder:tcp_ack=-66000Статья советует добавлять к tcp_ack ещё и tcp_ts_up (см. нюанс 5). Для HTTPS обычно берут вариант из анимации: --payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up; здесь --payload=tls_client_hello значит «срабатывать только на TLS ClientHello». Остальные готовые команды собраны в разделах Быстрый старт и Практические примеры.
К разрезу можно добавить приём seqovl. Тогда перед второй частью, которая уходит первой из настоящих, дописывается несколько байт заполнителя (seqovl_pattern, по умолчанию нули), а номер seq этой части сдвигается назад на ту же длину. Сервер эти байты отбрасывает как вышедшие за левую границу окна приёма, а DPI видит цельный сегмент вместе с заполнителем и может не распознать в нём имя сайта; общий принцип с раскладкой по байтам разобран в разделе seqovl — скрытый фейк внутри сегмента статьи про multisplit. У fakeddisorder два отличия от multisplit и fakedsplit. Во-первых, seqovl здесь применяется ко второй части, а не к первой. Во-вторых, его значение не число, а маркер того же вида, что pos (seqovl=host, seqovl=midsld-2), как у multidisorder; результат (после вычитания единицы) должен быть строго меньше pos-1, иначе seqovl отменяется. Подробно — в разделах seqovl — скрытый фейк внутри реального сегмента и seqovl — маркер, а не число.
Ближайшие соседи fakeddisorder — три техники с одним из его приёмов. multisplit только режет и шлёт по порядку, без фейков. multidisorder режет и шлёт в обратном порядке, тоже без фейков. fakedsplit окружает части такими же фейками, но шлёт в прямом порядке. Сводная таблица по ним — в разделе Отличия от fakedsplit, multisplit и multidisorder, а подсказка «когда что брать» — в подразделе Когда что использовать: fakeddisorder рассчитан на DPI, который проверяет и порядок, и содержимое. Ограничения такие: pos — один маркер, а не список (нюанс 2); IP-фрагментации функция не поддерживает, для неё нужен multisplit или multidisorder (нюанс 6); работает только с TCP (нюанс 1). Флаги nofake1–nofake4 отключают отдельные фейки, а если отключить все четыре, останется multidisorder с одной позицией (пример 11).
Оглавление
- Коротко и по-простому
- Справка: где функция в коде и родственники
- Зачем нужен fakeddisorder
- Быстрый старт
- Откуда берутся данные для нарезки
- Маркер позиции (pos)
- seqovl — скрытый фейк внутри реального сегмента
- Фейковые сегменты и pattern
- Полный список аргументов
- Порядок отправки сегментов (6 пакетов)
- Применение fooling и reconstruct
- Поведение при replay / reasm
- Автосегментация по MSS
- Место в общем скелете
- Псевдокод алгоритма
- Нюансы и подводные камни
- Отличия от fakedsplit, multisplit и multidisorder
- Миграция с nfqws1
- Практические примеры
- 📚 См. также
Справка: где функция в коде и родственники
Паспорт функции для тех, кто сверяется с исходниками zapret2: в каком файле и на какой строке лежит код, какой ключ первой версии zapret она заменяет и как объявлена.
Файл: lua/zapret-antidpi.lua:931
nfqws1 эквивалент: --dpi-desync=fakeddisorder
Сигнатура: function fakeddisorder(ctx, desync)
fakeddisorder — функция TCP-десинхронизации с двойным запутыванием DPI: по содержимому (реальные vs фейковые сегменты) и по порядку (обратная последовательность — disorder). Payload разрезается по одной позиции на 2 части, и каждая часть обрамляется фейковыми сегментами. Отправка идёт в обратном порядке: сначала вторая часть (с фейками), затем первая (с фейками). Итого — 6 пакетов. После успешной отправки выносит VERDICT_DROP, чтобы оригинальный пакет не ушёл.
Проще говоря
Payload — полезные данные пакета без сетевых заголовков: сам HTTP-запрос или сам ClientHello. Reasm — те же данные, собранные движком из нескольких пакетов, если запрос в один пакет не влез. Blob — заранее заготовленные данные, которые можно подсунуть вместо настоящих. Какой из трёх источников выбирается, разобрано в разделе Откуда берутся данные для нарезки.
VERDICT_DROP— решение «перехваченный пакет не отправлять»; как движок складывает такие решения от разных функций, описано в стадии 8 жизненного цикла. «Двойное запутывание» — это два приёма сразу: фейки (запутывание по содержимому) и обратный порядок отправки (запутывание по порядку); оба разобраны в разделе Зачем нужен fakeddisorder.
Родственные функции: multisplit (прямой порядок, без фейков), multidisorder (обратный порядок, без фейков), fakedsplit (с фейками, прямой порядок), hostfakesplit (по hostname), tcpseg (диапазон), oob (urgent byte).
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в жизненный цикл desync-функции; здесь описано только то, чем fakeddisorder от этого скелета отличается.
Зачем нужен fakeddisorder
DPI анализирует TCP-поток, пытаясь собрать полный payload и найти в нём сигнатуры (hostname в HTTP, SNI в TLS). fakeddisorder атакует DPI двумя способами одновременно:
-
Запутывание по содержимому (fake). Между реальными сегментами посылаются фейковые, содержащие мусорные данные. DPI должен отличить настоящие сегменты от поддельных. Фейки отбрасываются сервером благодаря fooling (badseq, badsum, TTL и т.д.), но DPI может их проглотить.
-
Запутывание по порядку (disorder). Сегменты отправляются в обратном порядке: сначала вторая часть payload, затем первая. DPI, ожидающий последовательного потока, может быть сбит с толку.
Проще говоря
Сигнатура — заранее известная строка, по которой DPI узнаёт сайт. Фейк — пакет-обманка с мусором вместо данных, у которого тот же размер и тот же seq (порядковый номер байта в потоке), что у настоящего куска: сервер отбрасывает его благодаря порче заголовка, а DPI может принять за настоящие данные. Disorder — отправка кусков не по порядку: сервер ждёт недостающие и склеивает всё по seq, а DPI, которому нужен последовательный поток, может запутаться. Фейки подробнее разобраны в разделах Зачем нужен fake и Зачем нужен fakedsplit, обратный порядок — в разделе Обратный порядок отправки — суть disorder.
Двойное запутывание (содержимое + порядок) эффективнее, чем каждый приём по отдельности:
multisplit: [часть 1] → [часть 2] — только разрез
multidisorder: [часть 2] → [часть 1] — разрез + обратный порядок
fakedsplit: [F1][часть 1][F2] → [F3][часть 2][F4] — разрез + фейки
fakeddisorder: [F1][часть 2][F2] → [F3][часть 1][F4] — разрез + фейки + обратный порядок
Как читать схему: четыре строки — четыре функции, стрелка показывает порядок отправки, каждые квадратные скобки — один TCP-сегмент. F1–F4 — фейковые сегменты, «часть 1» и «часть 2» — настоящие куски payload. В нижней строке фейки стоят по обе стороны от каждой настоящей части, а часть 2 уходит раньше части 1. Раскладка на шесть пакетов подробно показана в разделе Порядок отправки сегментов (6 пакетов).
Сервер при этом корректно собирает поток — TCP-стек гарантирует это через sequence numbers, а фейки отбрасываются благодаря fooling.
Проще говоря
Для сервера порядок прибытия не важен: TCP-стек сам расставит куски по seq и отбросит фейки. Для DPI важны и порядок, и то, какие из сегментов настоящие, поэтому обман сразу по двум признакам может сработать там, где одного было бы мало. Какую функцию брать под какой тип DPI, подсказывает таблица в подразделе Когда что использовать.
Быстрый старт
Ниже готовые строки для профиля Zapret 2, от самой короткой к более сложным. Флаг --payload=… работает как фильтр для всех вызовов --lua-desync, которые идут после него (до следующего --payload или до конца профиля): они срабатывают только на данных указанного типа. Поэтому --payload пишут перед --lua-desync, а не после (подробно — в последовательность аргументов).
Минимально (разрез по позиции 2, payload=known, dir=out):
--lua-desync=fakeddisorder:tcp_ack=-66000Здесь payload=known означает «только распознанные протоколы», а dir=out — «только исходящие пакеты»; оба аргумента разобраны в разделах B) Standard direction и C) Standard payload. Фейкам портится ACK (tcp_ack=-66000), а tcp_ts_up в этой минимальной строке нет: статья советует ставить его всегда вместе с tcp_ack (нюанс 5).
Типовой TLS-разрез посередине SNI:
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upФункция срабатывает только на TLS ClientHello и режет его посередине домена второго уровня; уходят шесть пакетов, вторая часть первой.
TLS с seqovl для тройного запутывания:
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:seqovl=5:seqovl_pattern=0x1603030000:tcp_ack=-66000:tcp_ts_upК настоящей второй части слева приклеивается заполнитель 0x1603030000, похожий на начало TLS record — заголовка записи TLS (для seqovl=5 это 4 байта, см. пример 3 в разделе Практические примеры). Как это работает, разобрано в разделе seqovl — скрытый фейк внутри реального сегмента.
HTTP с кастомным паттерном фейков:
--payload=http_req --lua-desync=fakeddisorder:pos=host:pattern=0x474554202F:tcp_ack=-66000Здесь pos=host режет HTTP-запрос в начале имени хоста, а pattern=0x474554202F заполняет фейки не нулями, а образцом, похожим на начало HTTP GET; подробнее — в разделе Фейковые сегменты и pattern.
Откуда берутся данные для нарезки
Внутри fakeddisorder данные (data) выбираются в следующем порядке приоритетов:
1. blob_or_def(desync, desync.arg.blob) — если задан blob= и он существует
2. desync.reasm_data — если есть реассемблированные данные (multi-packet payload)
3. desync.dis.payload — текущий пакет (fallback)
Следствие: все маркеры pos, seqovl и прочие аргументы применяются именно к тем данным, которые реально выбраны. Если вы задали blob=myblob, маркеры вроде midsld будут работать только если myblob содержит валидный TLS/HTTP payload, который zapret может распознать.
Проще говоря
Payload — полезные данные текущего пакета без сетевых заголовков, reasm — те же данные, собранные движком из нескольких пакетов, blob — заранее заготовленные данные, которые подставляются вместо настоящих (как их объявлять — в заметке blob). Функция берёт первый из трёх источников, который есть: сначала blob, если он задан, потом собранный reasm, в последнюю очередь текущий пакет. Тот же выбор описан как стадия 4 общего скелета в заметке жизненный цикл desync-функции. Какие payload распознаёт
nfqws2и почему от этого зависят относительные маркеры вродеmidsld, разобрано в заметке о типах payload.
Маркер позиции (pos)
pos — главный аргумент fakeddisorder. Определяет где внутри payload будет произведён разрез. В отличие от multisplit и multidisorder, pos — это один маркер, а не список. Payload всегда разрезается на ровно 2 части.
Проще говоря
Маркер — способ указать место разреза либо числом байт, либо словом («середина домена второго уровня»), которое
nfqws2сам переводит в число по устройству запроса. Вfakeddisorderмаркер один, поэтому запрос всегда делится ровно на две части. Как задают список маркеров, разобрано в разделах Маркеры позиций (pos) статьи про multisplit и Маркеры позиций (pos) статьи про multidisorder.
Типы маркеров
| Тип | Описание | Пример |
|---|---|---|
| Абсолютный положительный | Смещение от начала payload, считая с 0. pos=N → первые N байт становятся отдельным сегментом | 2, 5, 100 |
| Абсолютный отрицательный | Смещение от конца payload. -1 = последний байт | -1, -10, -50 |
| Относительный | Логическая позиция внутри распознанного payload. Привязана к структуре протокола | midsld, host, sniext |
Абсолютные маркеры не зависят от содержимого. Относительные привязаны к устройству запроса и работают только тогда, когда nfqws2 распознал протокол: в столбце «Для каких payload» таблицы ниже указано, для каких типов данных маркер имеет смысл (про типы payload — Что такое тип payload).
Относительные маркеры
| Маркер | Описание | Для каких payload |
|---|---|---|
method | Начало HTTP-метода (GET, POST, HEAD, PUT и т.д.). Обычно позиция 0, но может стать 1-2 при использовании http_methodeol | http_req |
host | Первый байт имени хоста (Host: в HTTP, SNI в TLS) | http_req, tls_client_hello |
endhost | Байт, следующий за последним байтом имени хоста. Т.е. host..endhost-1 = полный hostname | http_req, tls_client_hello |
sld | Первый байт домена второго уровня (SLD). Для www.example.com — это e в example | http_req, tls_client_hello |
endsld | Байт, следующий за последним байтом SLD. Для example.com — это . после example | http_req, tls_client_hello |
midsld | Середина SLD (самый популярный маркер). Для example (7 символов) — позиция 3-го или 4-го символа | http_req, tls_client_hello |
sniext | Начало поля данных SNI extension в TLS ClientHello. Extension состоит из type (2 байта) + length (2 байта) + данные — sniext указывает на начало данных | tls_client_hello |
extlen | Поле длины всех TLS extensions | tls_client_hello |
Арифметика маркеров
К любому маркеру можно прибавить (+) или вычесть (-) целое число:
midsld+1 — один байт ПОСЛЕ середины SLD
midsld-1 — один байт ДО середины SLD
endhost-2 — два байта до конца hostname
method+2 — два байта после начала метода
sniext+1 — один байт после начала SNI extension data
host+3 — три байта после начала hostname
-1 — последний байт payload (абсолютный, не относительный)
Проще говоря, midsld+1 значит «на один байт правее середины домена второго уровня», а -1 — последний байт всего payload, потому что абсолютное число не привязано к структуре протокола.
Как маркер разрешается в коде
В отличие от multisplit (который использует resolve_multi_pos для списка маркеров), fakeddisorder использует resolve_pos — функцию для одного маркера:
local spos = desync.arg.pos or "2"
local pos = resolve_pos(data, desync.l7payload, spos)resolve_pos возвращает одно число (абсолютную Lua-позицию, 1-based) или nil, если маркер не может быть разрешён.
Проще говоря
resolve_pos— функция, которая переводит слово-маркер в номер байта. Lua нумерует позиции с единицы, а смещения в командной строке считаются с нуля, поэтому маркеру0соответствует Lua-позиция 1; именно её функция отвергает (см. следующий раздел).
Важные нюансы pos
- Только ОДИН маркер.
pos=midsld,endhost— невалидно (в отличие от multisplit/multidisorder, которые принимают списки). Если вам нужно несколько позиций разреза с фейками — используйте несколько инстансовfakeddisorderв цепочке. - Разрез в самом начале данных запрещён. Если маркер разрешается в Lua-позицию 1 — это маркер
0, поскольку смещения в командной строке считаются с нуля, — fakeddisorder логирует"split pos resolved to 0. cannot split."и ничего не делает. Не работает именноpos=0;pos=1(первый байт отдельным сегментом) корректен. Частая жертва правила —pos=methodдля HTTP, где метод лежит на смещении 0. - Неразрешимый маркер = ничего не делает. Если маркер не может быть разрешён (например,
midsldдляunknownpayload), fakeddisorder логирует"cannot resolve pos"и пропускает операцию. - По умолчанию pos=“2”. Если
posне задан, разрез по позиции 2 — payload делится на первые 2 байта и весь остаток (pos=Nдаёт первые N байт отдельным сегментом, см. таблицу типов маркеров выше; описание «1-й байт и остальное» относится кpos=1).
Проще говоря
seqovl — скрытый фейк внутри реального сегмента
seqovl (Sequence Overlap) — техника скрытого замешивания фейковых данных в реальный TCP-сегмент через манипуляцию TCP sequence number. В fakeddisorder seqovl применяется только ко второму реальному сегменту (real part 2 — первый реальный пакет в порядке отправки).
Sequence Overlap — «перекрытие по номерам последовательности». В multisplit и fakedsplit эта техника применяется к первому настоящему сегменту, а в fakeddisorder — ко второму, который уходит первым из настоящих: сравнение — в нюансе 8. У multidisorder seqovl тоже маркер, и его механика разобрана в отдельном разделе той статьи.
Принцип работы seqovl в fakeddisorder
Без seqovl:
Пакет 2 (real part 2):
TCP seq: pos-1
Данные: [РЕАЛЬНАЯ_ЧАСТЬ_2]
Сервер: принимает [РЕАЛЬНАЯ_ЧАСТЬ_2] целиком
С seqovl=10:
Пакет 2 (real part 2):
TCP seq: pos-1-10 (уменьшен на 10)
Данные: [PATTERN_10_БАЙТ][РЕАЛЬНАЯ_ЧАСТЬ_2]
Что видит DPI:
Единый TCP-сегмент начиная с seq pos-1-10.
DPI анализирует весь блок, включая PATTERN.
PATTERN содержит мусор → DPI может не распознать SNI.
Что видит сервер (TCP-стек):
Байты до pos-1 выходят за левую границу window → отбрасываются.
Байты с pos-1 (РЕАЛЬНАЯ_ЧАСТЬ_2) → принимаются.
Визуализация:
TCP window boundary (= pos-1)
↓
| ОТБРОСИТЬ | ПРИНЯТЬ |
| PATTERN(10) | РЕАЛЬНАЯ_ЧАСТЬ_2 |
^seq=pos-11 ^seq=pos-1
Проще говоря
Перед настоящей второй частью приклеено 10 байт заполнителя (
PATTERN), а seq сдвинут назад на 10. Границей приёма служитpos-1: всё левее сервер выбрасывает, всё от границы принимает. DPI видит цельный сегмент, начинающийся сpos-11, и может не распознать в нём SNI. Общее объяснение окна приёма и разбор того, что видят DPI и сервер, есть в разделе Принцип работы seqovl статьи про multisplit.
seqovl — маркер, а не число
Критическое отличие от multisplit и fakedsplit: в fakeddisorder (как и в multidisorder) seqovl — это маркер, а не просто число. Он разрешается через resolve_pos:
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
seqovl = seqovl - 1 -- Lua→0-basedЭто значит, что можно использовать:
seqovl=5— абсолютное число (разрешится в 5, станет 4 после-1)seqovl=host— относительный маркер (разрешится в позицию начала hostname,-1)seqovl=midsld-2— маркер с арифметикой
Проще говоря
seqovlзадают так же, как место разреза: числом или словом-маркером. Внутри функции позиция сначала переводится в номер байта, а потом от неё отнимается 1 (Lua→0-based, то есть переход от счёта с единицы к счёту с нуля). Как то же устроено у другой функции — в разделе seqovl как маркер статьи про multidisorder.
Ограничение: результат seqovl (после -1) должен быть строго меньше pos-1. Иначе seqovl отменяется с логом:
fakeddisorder: seqovl cancelled because seqovl N is not less than the split pos M
Если маркер не разрешается, seqovl тоже отменяется:
fakeddisorder: seqovl cancelled because could not resolve marker 'xxx'
Проще говоря
Отмена
seqovlне отменяет всю функцию: по псевдокоду ниже в этом случаеseqovlобнуляется, а разрез и отправка шести пакетов идут дальше.
Зачем seqovl лучше обычного fooling
Обычный способ спрятать фейк от сервера — fooling: пакету-обманке портят заголовки (TTL, номера seq или ack, опцию md5sig), и сервер отбрасывает такой пакет. seqovl добивается похожего результата другим путём, и таблица ниже сравнивает оба подхода.
| Критерий | Обычный fooling (TTL, badseq, md5sig) | seqovl |
|---|---|---|
| Заголовки | Модифицируются (TTL, seq, ack, md5) | Не модифицируются — пакет выглядит полностью легитимным |
| Обнаружение | DPI может детектировать подозрительные заголовки | DPI видит “честный” сегмент с правильными заголовками |
| Механизм отбрасывания | Сервер отбрасывает весь пакет из-за невалидных заголовков | Сервер отбрасывает только часть, выходящую за TCP window |
| Надёжность | Зависит от поведения конкретного стека | Основан на фундаментальном свойстве TCP |
Вывод: seqovl добавляет третий уровень запутывания (к фейкам и disorder), причём реальный сегмент с seqovl выглядит абсолютно легитимно для DPI.
seqovl_pattern
Паттерн, которым заполняется seqovl-область. По умолчанию — 0x00 (нули).
В fakeddisorder seqovl_pattern — это имя blob. Паттерн повторяется до нужной длины seqovl.
Blob можно записать прямо в команде шестнадцатеричной строкой (inline hex, 0x…) или заранее объявить флагом --blob и сослаться на него по имени; подробно — в заметке blob.
# Inline hex blob (маскировка под начало TLS record)
--lua-desync=fakeddisorder:pos=midsld:seqovl=5:seqovl_pattern=0x1603030000:tcp_ack=-66000
# Предзагруженный blob
--blob=tlspat:0x1603030100 \
--lua-desync=fakeddisorder:pos=midsld:seqovl=8:seqovl_pattern=tlspat:tcp_ack=-66000Если optional задан и blob seqovl_pattern отсутствует — используется нулевой паттерн (seqovl не отменяется).
Фейковые сегменты и pattern
Фейковые сегменты генерируются функцией pattern():
fakepat = desync.arg.pattern and blob(desync, desync.arg.pattern) or "\x00"
fake_part2 = pattern(fakepat, pos, #data - pos + 1) -- фейк для части 2
fake_part1 = pattern(fakepat, 1, pos - 1) -- фейк для части 1Ключевые свойства:
- Фейк совпадает по размеру с реальной частью, которую он имитирует
pattern()принимает смещение (posили1), что означает, что фейк заполняется так, как будто он начинается с той же позиции в payload. Это делает его TCP sequence корректным для DPI- По умолчанию
pattern=\x00(нули). Можно задать свой blob черезpattern=<blob> - Blob повторяется до нужной длины
Важно: фейки отправляются с полным fooling (badseq, TTL и т.д.), чтобы сервер их отбросил. Без fooling сервер примет фейки как реальные данные и поток будет повреждён.
Проще говоря
pattern()— функция, которая повторяет образец (по умолчанию один нулевой байт) до нужной длины. Фейк для части 2 по размеру равен части 2, фейк для части 1 — части 1, а seq у них такие же, как у настоящих частей. Образец задаёт аргументpattern, его значение — имя blob (см. blob). Как именно фейкам портят заголовки, разобрано в разделе Применение fooling и reconstruct.
Полный список аргументов
Аргументы делятся на собственные аргументы fakeddisorder (раздел A) и стандартные (разделы B–G): их понимают и другие функции дурения, а общий обзор есть в desync.
Формат вызова:
--lua-desync=fakeddisorder[:arg1[=val1][:arg2[=val2]]...]
Все val приходят в Lua как строки. Если =val не указан, значение = пустая строка "" (в Lua это truthy), поэтому флаги пишутся просто как :optional, :nodrop, :tcp_ts_up.
A) Собственные аргументы fakeddisorder
pos
- Формат:
pos=<marker> - Тип: строка с одним маркером (НЕ список!)
- По умолчанию:
"2" - Описание: Точка разреза. Payload делится на 2 части:
data[1..pos-1]иdata[pos..#data] - Примеры:
pos=2— разрез после 2-го байта (дефолт)pos=1— первый байт уходит отдельным сегментомpos=midsld— разрез посередине SLDpos=host— разрез в начале hostnamepos=sniext+1— один байт после начала SNI extension datapos=endhost-2— два байта до конца hostname
seqovl
- Формат:
seqovl=<marker> - Тип: маркер (не только число — в отличие от multisplit/fakedsplit!)
- По умолчанию: не задан (нет seqovl)
- Описание: Применяется ко второму реальному сегменту (real part 2). К данным слева добавляется
seqovlбайтseqovl_pattern, а TCPth_seqуменьшается наseqovl. Маркер разрешается черезresolve_pos, затем результат уменьшается на 1 (Lua→0-based). Результат должен быть строго меньшеpos-1 - Примеры:
seqovl=5— 4 байта фейка слева (5→resolve→5, 5-1=4)seqovl=host— seqovl равен позиции начала hostname (минус 1)seqovl=sld-2— маркер с арифметикой
seqovl_pattern
- Формат:
seqovl_pattern=<blobName> - Тип: имя blob-переменной
- По умолчанию: один байт
0x00, повторяемый до длиныseqovl - Описание: Данные для заполнения seqovl-области. Blob повторяется функцией
pattern()до нужного размера - Поведение с
optional: еслиoptionalзадан и blob отсутствует — используется нулевой паттерн, seqovl не отменяется - Примеры:
seqovl_pattern=0x1603030000— inline hex (маскировка под TLS)seqovl_pattern=my_pattern_blob— предзагруженный blob
pattern
- Формат:
pattern=<blobName> - Тип: имя blob-переменной
- По умолчанию: один байт
0x00 - Описание: Данные для заполнения фейковых сегментов. Blob повторяется до размера соответствующей реальной части
- Примеры:
pattern=0x474554202F— inline hex (маскировка под начало HTTP GET)pattern=fake_tls_pat— предзагруженный blob
blob
- Формат:
blob=<blobName> - Тип: имя blob-переменной
- По умолчанию: не задан
- Описание: Заменить текущий payload/reasm на указанный blob и резать/слать его. Используется для отправки произвольных данных (фейковых payload, модифицированных ClientHello и т.д.)
- Примеры:
blob=fake_default_tls— стандартный TLS-фейкblob=0xDEADBEEF— inline hexblob=my_custom_ch— предзагруженный blob
nofake1
- Формат:
nofake1(флаг, без значения) - Описание: Не отправлять первый фейковый пакет (fake part 2, первый по порядку отправки)
nofake2
- Формат:
nofake2(флаг, без значения) - Описание: Не отправлять третий пакет (fake part 2 повторно, после реального part 2)
nofake3
- Формат:
nofake3(флаг, без значения) - Описание: Не отправлять четвёртый пакет (fake part 1, перед реальным part 1)
nofake4
- Формат:
nofake4(флаг, без значения) - Описание: Не отправлять шестой пакет (fake part 1 повторно, после реального part 1)
Проще говоря
Четыре флага соответствуют четырём фейкам по порядку отправки:
nofake1— пакет 1,nofake2— пакет 3,nofake3— пакет 4,nofake4— пакет 6. Флага для отключения настоящих пакетов 2 и 5 среди аргументов нет. Примеры — в разделе Пример с nofake-флагами.
optional
- Формат:
optional(флаг, без значения) - Описание: Мягкий режим:
- Если задан
blob=...и blob отсутствует — fakeddisorder ничего не делает (тихий skip, без ошибок) - Если задан
seqovl_pattern=...и blob отсутствует — используется нулевой паттерн (seqovl не отменяется)
- Если задан
Проще говоря
optionalсмягчает две ситуации: если не найден blob для данных, функция тихо ничего не делает, а если не найден blob-образец дляseqovl, заполнитель будет нулевым.
nodrop
- Формат:
nodrop(флаг, без значения) - Описание: После успешной отправки не выносить
VERDICT_DROP(вместо этого вернутьVERDICT_PASS). Оригинальный пакет тоже будет отправлен (наряду с 6 нарезанными пакетами) - Предупреждение: в боевых профилях
nodropобычно нежелателен — оригинал ещё раз уйдёт, что создаст дублирование
B) Standard direction
| Параметр | Значения | По умолчанию |
|---|---|---|
dir | in, out, any | out |
Фильтр по направлению пакета. fakeddisorder по умолчанию работает только с исходящими (out).
dir=out— только исходящие (от клиента к серверу)dir=in— только входящие (от сервера к клиенту)dir=any— оба направления
При первом вызове с указанным dir функция делает direction_cutoff_opposite — отсекает себя от противоположного направления.
Проще говоря
Cutoff (отсечение) — команда «эта функция больше не вызывается для такого направления или потока». Что происходит на стадии отсечения направления, описано в заметке стадия 2 жизненного цикла.
C) Standard payload
| Параметр | Значения | По умолчанию |
|---|---|---|
payload | список типов через запятую | known |
Фильтр по типу payload на уровне Lua. Это дополнительный фильтр к --payload=... на уровне профиля.
payload=known— только распознанные протоколы (http_req,tls_client_hello,quic_initialи т.д.)payload=all— любой payload, включаяunknownpayload=tls_client_hello,http_req— конкретные типыpayload=~unknown— инверсия: всё кроме unknown
Важно: лучше ставить --payload=... на уровне профиля (C-код, быстрее), а не полагаться только на Lua-фильтр.
Проще говоря
Тип payload — то, что движок распознал в данных пакета (
tls_client_hello,http_reqи другие). Подробнее — в заметке Что такое тип payload; порядок--payloadи--lua-desyncв профиле разобран в последовательность аргументов.
D) Standard fooling
Модификации L3/L4 заголовков. В fakeddisorder fooling применяется только к фейковым сегментам. К реальным применяется только tcp_ts_up.
Каждая строка таблицы — один способ порчи заголовков IP или TCP у фейка, чтобы сервер его отбросил. Разбор способов по слоям и того, где какой ломается, есть в разделе Как заставить fake умереть: каталог способов.
| Параметр | Описание | Пример |
|---|---|---|
ip_ttl=N | Установить IPv4 TTL | ip_ttl=6 |
ip6_ttl=N | Установить IPv6 Hop Limit | ip6_ttl=6 |
ip_autottl=delta,min-max | Автоматический TTL (delta от серверного TTL) | ip_autottl=-2,40-64 |
ip6_autottl=delta,min-max | Аналогично для IPv6 | ip6_autottl=-2,40-64 |
ip6_hopbyhop[=HEX] | Вставить extension header hop-by-hop (по умолчанию 6 нулей) | ip6_hopbyhop |
ip6_hopbyhop2[=HEX] | Второй hop-by-hop header | ip6_hopbyhop2 |
ip6_destopt[=HEX] | Destination options header | ip6_destopt |
ip6_destopt2[=HEX] | Второй destination options | ip6_destopt2 |
ip6_routing[=HEX] | Routing header | ip6_routing |
ip6_ah[=HEX] | Authentication header | ip6_ah |
tcp_seq=N | Сместить TCP sequence (+ или -) | tcp_seq=-10000 |
tcp_ack=N | Сместить TCP ack (+ или -) | tcp_ack=-66000 |
tcp_ts=N | Сместить TCP timestamp | tcp_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-функция fooling | fool=my_fooler |
Ключевое: tcp_ts_up — единственный параметр fooling, который применяется и к реальным, и к фейковым сегментам. Все остальные fooling-параметры применяются только к фейкам.
Заметка про tcp_ts_up: На Linux-серверах пакеты с инвалидным ACK стабильно отбрасываются только если TCP timestamp option идёт первой в заголовке. tcp_ts_up перемещает её в начало, обеспечивая корректную работу badseq-fooling. Поэтому tcp_ts_up рекомендуется включать всегда вместе с tcp_ack.
Проще говоря
TCP timestamp — необязательная опция в конце TCP-заголовка (устройство опций разобрано в разделе Что такое TCP options).
tcp_ts_upтолько переставляет её в начало и ничего не портит, поэтому его безопасно применять и к настоящим сегментам.
E) Standard ipid
| Параметр | Описание | По умолчанию |
|---|---|---|
ip_id=seq | Последовательные IP ID | seq |
ip_id=rnd | Случайные IP ID | — |
ip_id=zero | Нулевые IP ID | — |
ip_id=none | Не менять IP ID | — |
ip_id_conn | Сквозная нумерация IP ID в рамках соединения (требует tracking) | — |
ip_id применяется и к фейкам, и к оригиналам.
Проще говоря
ip_idзадаёт, как нумеруются IP ID пакетов:seq— по порядку,rnd— случайно,zero— нулями,none— не менять. К фейкам и к настоящим сегментам он применяется одинаково (таблица в разделе Применение fooling и reconstruct).
F) Standard reconstruct
| Параметр | Описание |
|---|---|
badsum | Испортить L4 (TCP) checksum при реконструкции raw-пакета |
reconstruct применяется только к фейкам. К оригиналам reconstruct не применяется.
Проще говоря
Reconstruct — сборка готового сырого пакета из полей и данных;
badsumпри такой сборке нарочно портит контрольную сумму TCP. Настоящие сегменты собираются без этого.
G) Standard rawsend
| Параметр | Описание |
|---|---|
repeats=N | Отправить каждый пакет N раз (применяется только к фейкам) |
ifout=<iface> | Интерфейс для отправки (по умолчанию определяется автоматически) |
fwmark=N | Firewall mark (только Linux, nftables/iptables) |
Важно: repeats в fakeddisorder применяется только к фейковым сегментам (через rawsend_opts), а не ко всем. Реальные сегменты отправляются через rawsend_opts_base, где repeats не задействуется.
Важно: ipfrag не задействуется в fakeddisorder — ни для фейков, ни для оригиналов. Объект ipfrag установлен в пустую таблицу {} для обоих типов пакетов.
Проще говоря
Rawsend — отправка собранных пакетов в сеть; его параметры задают число повторов, интерфейс и метку firewall. IP-фрагментация — деление пакета на части на уровне IP: multisplit её поддерживает, а
fakeddisorderнет.
Порядок отправки сегментов (6 пакетов)
fakeddisorder всегда отправляет 6 пакетов (если все nofake-флаги не установлены). Порядок обратный по отношению к fakedsplit: сначала идёт вторая часть (с фейками), затем первая (с фейками).
Проще говоря
Пакеты 1–3 — вторая часть запроса в окружении двух фейков, пакеты 4–6 — первая часть в таком же окружении. Смещение (seq offset) считается от начала payload и у фейка равно смещению настоящей части, которую он копирует. Столбец
nofakeпоказывает, каким флагом отключается пакет.
ASCII-диаграмма полной последовательности
Payload (100 байт), pos=midsld (разрешился в позицию 40):
Часть 1: data[1..39] (39 байт, seq offset = 0)
Часть 2: data[40..100] (61 байт, seq offset = 39)
Порядок отправки (хронологический):
№ Пакет Тип seq offset Размер Opts nofake
─────────────────────────────────────────────────────────────────────────
1 fake part 2 ФЕЙК 39 61 opts_fake nofake1
2 real part 2 РЕАЛ 39 (-seqovl) 61(+ovl) opts_orig —
3 fake part 2 ФЕЙК 39 61 opts_fake nofake2
4 fake part 1 ФЕЙК 0 39 opts_fake nofake3
5 real part 1 РЕАЛ 0 39 opts_orig —
6 fake part 1 ФЕЙК 0 39 opts_fake nofake4
─────────────────────────────────────────────────────────────────────────
opts_orig: только tcp_ts_up из fooling, пустой reconstruct, пустой ipfrag
opts_fake: полный fooling + полный reconstruct, пустой ipfrag
Ниже те же шесть пакетов вдоль оси времени: слева самый ранний, справа самый поздний; под каждым подписано, что с ним делает сервер.
Визуализация на временной оси:
Время ──────────────────────────────────────────────────────────────────→
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ FAKE P2 │ │ REAL P2 │ │ FAKE P2 │ │ FAKE P1 │ │ REAL P1 │ │ FAKE P1 │
│ nofake1 │ │ (+seqovl)│ │ nofake2 │ │ nofake3 │ │ │ │ nofake4 │
│ seq=39 │ │ seq=39-N │ │ seq=39 │ │ seq=0 │ │ seq=0 │ │ seq=0 │
│ [МУСОР] │ │[PAT][DAT]│ │ [МУСОР] │ │ [МУСОР] │ │ [ДАННЫЕ] │ │ [МУСОР] │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
^^^^ФЕЙК^^^^ ^^РЕАЛЬНЫЙ^^ ^^^^ФЕЙК^^^^ ^^^^ФЕЙК^^^^ ^^РЕАЛЬНЫЙ^^ ^^^^ФЕЙК^^^^
↓ ↓ ↓ ↓ ↓ ↓
сервер сервер сервер сервер сервер сервер
ОТБРОСИТ ПРИМЕТ ОТБРОСИТ ОТБРОСИТ ПРИМЕТ ОТБРОСИТ
(fooling) (fooling) (fooling) (fooling)
Сравнение с fakedsplit (прямой порядок):
fakedsplit: [F1_P1] [REAL_P1] [F2_P1] → [F3_P2] [REAL_P2] [F4_P2]
fakeddisorder: [F1_P2] [REAL_P2] [F2_P2] → [F3_P1] [REAL_P1] [F4_P1]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Сначала ВТОРАЯ часть Потом ПЕРВАЯ часть
Проще говоря
Это та же схема, что у fakedsplit, только две половины поменялись местами: у fakedsplit сначала уходит первая часть, у
fakeddisorderвторая. Схема fakedsplit для сравнения — в разделе Последовательность отправки (6 пакетов).
Пример с seqovl
Payload (100 байт), pos=midsld (40), seqovl=10:
№ Пакет seq offset Данные
──────────────────────────────────────────────────────────────────────────
1 fake part 2 39 [PATTERN 61 байт]
2 real part 2 29 [SEQOVL_PAT 10 байт][data[40..100] 61 байт]
3 fake part 2 39 [PATTERN 61 байт]
4 fake part 1 0 [PATTERN 39 байт]
5 real part 1 0 [data[1..39] 39 байт]
6 fake part 1 0 [PATTERN 39 байт]
Сервер для пакета 2:
TCP window начинается с seq=39
Байты 29-38 (SEQOVL_PAT) → отбрасываются
Байты 39-99 (data[40..100]) → принимаются
Здесь у пакета 2 смещение 29, то есть 39 минус 10 байт заполнителя. По схеме выше окно приёма сервера для этого пакета начинается с seq=39, поэтому байты 29–38 он отбрасывает, а с 39-го принимает настоящую часть 2.
Пример с nofake-флагами
fakeddisorder:pos=midsld:tcp_ack=-66000:nofake2:nofake4
Отправляется 4 пакета вместо 6:
№ Пакет Тип
────────────────────────────
1 fake part 2 ФЕЙК
2 real part 2 РЕАЛ
— (пропущен nofake2)
3 fake part 1 ФЕЙК
4 real part 1 РЕАЛ
— (пропущен nofake4)
Проще говоря
nofake2иnofake4убирают повторные фейки, которые идут после настоящих частей; остаётся по одному фейку перед каждой настоящей частью, всего четыре пакета.
Можно отключить все фейки кроме одного, или вообще все:
# Только 2 реальных пакета (все фейки отключены) = multidisorder с одной позицией
fakeddisorder:pos=midsld:nofake1:nofake2:nofake3:nofake4
Применение fooling и reconstruct
Это ключевое отличие от multisplit и multidisorder, где fooling применяется ко всем сегментам.
Проще говоря
У пакетов два набора опций:
opts_origдля настоящих сегментов иopts_fakeдля фейков. Общий разбор наборов опций — в разделе Наборы опций: кто их применяет и к чему заметки про жизненный цикл, а у fakedsplit — в разделе Два набора опций: opts_orig vs opts_fake.
opts_orig opts_fake
(реальные сегменты) (фейковые сегменты)
───────────────────────────────────────────────────────────────────
fooling ТОЛЬКО tcp_ts_up ВСЁ (tcp_ack, tcp_seq,
ip_ttl, tcp_md5, ...)
reconstruct пусто ({}) ВСЁ (badsum и т.д.)
ipfrag пусто ({}) пусто ({})
ipid desync.arg (полный) desync.arg (полный)
rawsend rawsend_opts_base rawsend_opts (с repeats)
───────────────────────────────────────────────────────────────────
Почему так: реальные сегменты должны дойти до сервера в неповреждённом виде. Fooling вроде tcp_ack=-66000 сделал бы их невалидными для сервера. Поэтому fooling идёт только на фейки — чтобы сервер их отбросил, а DPI — запутался.
Исключение — tcp_ts_up. Он не портит пакет, а лишь перемещает TCP timestamp в начало заголовка. Это полезно для обеспечения корректной работы badseq/badack на принимающей стороне и применяется ко всем сегментам.
Поведение при replay / reasm
Многопакетный запрос nfqws2 придерживает, собирает в буфер reasm_data и перепроигрывает через desync-функции. fakeddisorder использует штатную развилку из общего скелета desync-функций (жизненный цикл desync-функции, стадия 6) без изменений: на первой части отправляет всю шестёрку пакетов (2 реальных + 4 фейка) по собранному буферу, остальные части дропает. Если отправка сорвалась, флаг «реасм уже отправлен» не ставится и оставшиеся части проходят как есть.
Проще говоря
Если ClientHello не влез в один пакет, движок придерживает пакеты, собирает запрос целиком в буфер
reasm_data(reasm — от reassembly, «пересборка») и «проигрывает» (replay) его по кускам через функции дурения. Функция должна сработать один раз, на первом куске, а остальные куски выбросить. Подробно — в разделе Стадия 6. Развилка перепроигрывания (replay).
Специфика функции: раз работа идёт по всему реасму, обратный порядок и размеры фейков определяются целым запросом, а не отдельным пакетом.
Автосегментация по MSS
О размерах TCP-сегментов думать не нужно. Функция rawsend_payload_segmented из zapret-lib.lua автоматически:
- Отслеживает MSS для каждого TCP-соединения
- Если часть payload превышает MSS — дополнительно режет по MSS
- Каждый под-сегмент отправляется с корректным TCP sequence
Проще говоря
MSS (Maximum Segment Size) — максимальный размер TCP-сегмента: если часть запроса больше, она уходит несколькими сегментами, и у каждого seq правильный. Указывать это в аргументах не нужно.
Место в общем скелете
Тело fakeddisorder построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в жизненный цикл desync-функции — здесь только отклонения:
| Стадия общего скелета | Что делает fakeddisorder |
|---|---|
| 1. Отсев транспорта | только TCP; на не-TCP делает instance_cutoff (кроме связанного ICMP) |
| 2. Направление | dir=out по умолчанию, отключается от входящего |
| 3. Аргументы | обязательных нет; optional + отсутствующий blob → тихий выход |
| 4. Данные | штатная цепочка blob → reasm → payload |
| 5. Гварды | штатные #data>0, direction_check, payload_check (по умолчанию known) |
| 6. replay | штатная развилка: работа на первой части, дроп остальных |
| 7. Своя техника | одна позиция разреза, два реальных сегмента и до четырёх фейков, отправка в обратном порядке, seqovl как маркер — этому посвящена вся заметка |
| 8. Вердикт | VERDICT_DROP после успешной отправки, VERDICT_PASS при nodrop или сбое rawsend |
Как и fakedsplit, функция собирает два разных набора опций: фейкам достаются fooling, reconstruct и repeats в полном объёме, реальным частям — только tcp_ts_up и политика ip_id. Смысл в том, что фейк сервер обязан отвергнуть, а реальные данные — принять. Общий разбор наборов опций — в жизненный цикл desync-функции.
Проще говоря
Cutoff (отсечение) — «эта функция больше не вызывается»; вердикт — решение, что делать с перехваченным пакетом:
VERDICT_DROPвыбросить его,VERDICT_PASSпропустить. Как вердикты складываются между функциями, описано в заметке Стадия 8. Вердикт и его агрегация.
Псевдокод алгоритма
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в жизненный цикл desync-функции:
Читать код удобнее вместе с таблицей из раздела Место в общем скелете и схемой из раздела Порядок отправки сегментов (6 пакетов): комментарии в коде отмечают, где кончается общая часть и начинаются шесть отправок.
function fakeddisorder(ctx, desync)
-- 1. Проверка: только TCP
if not desync.dis.tcp then
if not desync.dis.icmp then instance_cutoff_shim() end
return
end
-- 2. Cutoff противоположного направления
direction_cutoff_opposite(ctx, desync)
-- 3. Проверка optional blob
if optional and blob specified and blob not exists then
DLOG("blob not found. skipped")
return
end
-- 4. Выбор данных
data = blob_or_def(blob) or reasm_data or dis.payload
-- 5. Проверки: данные не пусты, направление OK, payload OK
if #data > 0 and direction_check() and payload_check() then
-- 6. Только первый replay
if replay_first() then
-- 7. Разрешение маркера (ОДИН, не список!)
spos = desync.arg.pos or "2"
pos = resolve_pos(data, l7payload, spos)
if pos == nil then
DLOG("cannot resolve pos")
elseif pos == 1 then
DLOG("split pos resolved to 0. cannot split.")
else
-- 8. Подготовка opts
opts_orig = {
fooling = {tcp_ts_up = desync.arg.tcp_ts_up}, -- ТОЛЬКО tcp_ts_up
reconstruct = {}, -- пусто
ipfrag = {}, -- пусто
ipid = desync.arg,
rawsend = rawsend_opts_base(desync)
}
opts_fake = {
fooling = desync.arg, -- ПОЛНЫЙ fooling
reconstruct = reconstruct_opts(desync), -- ПОЛНЫЙ reconstruct
ipfrag = {}, -- пусто
ipid = desync.arg,
rawsend = rawsend_opts(desync) -- с repeats
}
fakepat = pattern_blob or "\x00"
-- ============ ЧАСТЬ 2 (ОБРАТНЫЙ ПОРЯДОК!) ============
-- 9. Fake part 2 (пакет 1)
fake2 = pattern(fakepat, pos, #data - pos + 1)
if not nofake1 then
rawsend_payload_segmented(fake2, pos-1, opts_fake)
end
-- 10. Real part 2 + seqovl (пакет 2)
part2 = data:sub(pos)
seqovl = 0
if desync.arg.seqovl then
seqovl = resolve_pos(data, l7payload, desync.arg.seqovl)
if seqovl then
seqovl = seqovl - 1 -- Lua→0-based
if seqovl >= (pos-1) then
DLOG("seqovl cancelled: not less than split pos")
seqovl = 0
else
pat = seqovl_pattern_blob or "\x00"
part2 = pattern(pat, 1, seqovl) .. part2
end
else
DLOG("seqovl cancelled: cannot resolve marker")
seqovl = 0
end
end
rawsend_payload_segmented(part2, pos-1-seqovl, opts_orig)
-- 11. Fake part 2 again (пакет 3)
if not nofake2 then
rawsend_payload_segmented(fake2, pos-1, opts_fake)
end
-- ============ ЧАСТЬ 1 ============
-- 12. Fake part 1 (пакет 4)
fake1 = pattern(fakepat, 1, pos-1)
if not nofake3 then
rawsend_payload_segmented(fake1, 0, opts_fake)
end
-- 13. Real part 1 (пакет 5)
part1 = data:sub(1, pos-1)
rawsend_payload_segmented(part1, 0, opts_orig)
-- 14. Fake part 1 again (пакет 6)
if not nofake4 then
rawsend_payload_segmented(fake1, 0, opts_fake)
end
-- 15. Пометить как отправленное
replay_drop_set()
return nodrop and VERDICT_PASS or VERDICT_DROP
end
else
-- 16. Не первый replay
DLOG("not acting on further replay pieces")
end
-- 17. Дропнуть если ранее успешно отправлено
if replay_drop() then
return nodrop and VERDICT_PASS or VERDICT_DROP
end
end
endНюансы и подводные камни
1. Работает только с TCP
Если текущий пакет не TCP (UDP, ICMP и т.д.), fakeddisorder делает instance_cutoff_shim — отключает себя для этого потока навсегда. Исключение — ICMP-пакеты (related), для которых cutoff не выполняется (просто return).
Проще говоря
Instance cutoff — отключение функции для этого потока до его конца. Почему техника работает только с TCP, разобрано в нюансе «Работает только с TCP — и почему» статьи про multisplit; о самой стадии отсева — в разделе Стадия 1.
2. Только ОДИН маркер в pos
В отличие от multisplit и multidisorder, fakeddisorder принимает один маркер, а не список. pos=midsld,endhost не будет работать как два разреза. Если нужно несколько разрезов с фейками, используйте цепочку инстансов (хотя это нетривиально из-за VERDICT_DROP).
Проще говоря
Цепочка инстансов нетривиальна из-за
VERDICT_DROP: по нюансу 11 инстанс, стоящий послеfakeddisorder, увидитVERDICT_DROPи не получит оригинальный payload (нюанс 11). Про списки маркеров у соседей — Маркеры позиций (pos).
3. Позиция 1 не работает
Если маркер разрешается в Lua-позицию 1 (то есть задан маркер 0), fakeddisorder логирует "split pos resolved to 0. cannot split." и ничего не делает. Минимальный работающий разрез — pos=1, дефолт — pos=2.
4. Fooling ОБЯЗАТЕЛЕН для фейков
Без fooling-параметров (tcp_ack, tcp_seq, ip_ttl, tcp_md5, badsum и т.д.) сервер примет фейковые сегменты как реальные данные. Это повредит TCP-поток. Всегда указывайте хотя бы один fooling-параметр:
# ПЛОХО: фейки без fooling — сервер их примет!
--lua-desync=fakeddisorder:pos=midsld
# ХОРОШО: фейки с badseq
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upПроще говоря
Фейк без порчи заголовка сервер не отличит от настоящих данных и примет. Как фейкам портят заголовки, разобрано в разделе Что такое fooling.
5. tcp_ts_up рекомендуется с tcp_ack
На Linux-серверах пакеты с инвалидным ACK стабильно отбрасываются только если TCP timestamp option идёт первой в заголовке. Без tcp_ts_up есть риск, что сервер примет фейк с tcp_ack=-66000.
Проще говоря
Инвалидный ACK — номер подтверждения, который не подходит серверу; именно его портит
tcp_ack=-66000. Опция timestamp и её порядок в заголовке описаны в разделе Что такое TCP options.
6. ipfrag НЕ задействуется
В отличие от multisplit и multidisorder, fakeddisorder не поддерживает IP-фрагментацию. Объект ipfrag установлен в {} и для фейков, и для оригиналов. Если нужна IP-фрагментация — используйте multidisorder (без фейков, но с ipfrag).
Проще говоря
IP-фрагментация — деление пакета на части на уровне IP, ниже TCP. Здесь её нет ни для фейков, ни для настоящих сегментов; функцию, которая её поддерживает, описывает раздел F) Standard ipfrag статьи про multisplit.
7. seqovl — маркер, а не число
В multisplit и fakedsplit seqovl — просто tonumber(desync.arg.seqovl). В fakeddisorder (как и в multidisorder) seqovl разрешается через resolve_pos. Это значит:
seqovl=5работает (разрешится в абсолютную позицию 5, затем -1 = 4)seqovl=hostтоже работает- Но результат должен быть < pos-1
8. seqovl применяется только к real part 2
В отличие от fakedsplit (где seqovl к real part 1 — первому реальному), в fakeddisorder seqovl применяется к real part 2 — первому реальному по порядку отправки (но второму по порядку в payload). Это логично: в disorder-порядке сначала отправляется вторая часть, и именно она получает seqovl.
Проще говоря
Real part 2 — вторая часть запроса, которая при обратном порядке уходит первой из настоящих; поэтому
seqovlдостаётся именно ей.
9. nodrop создаёт дублирование
С nodrop fakeddisorder отправляет 6 нарезанных пакетов И пропускает оригинальный. Сервер получит данные дважды. Используйте nodrop только для отладки.
Проще говоря
Без
nodropоригинальный пакет выбрасывается, и сервер получает данные один раз; сnodropуходят и оригинал, и все шесть пакетов, поэтому флаг годится только для отладки.
10. Ошибка rawsend прерывает цепочку
Если любой из 6 вызовов rawsend_payload_segmented возвращает false, fakeddisorder немедленно возвращает VERDICT_PASS (оригинальный пакет пропускается). Остальные пакеты из 6 не отправляются, replay_drop_set не вызывается.
Проще говоря
Если хотя бы одна отправка не удалась, остальные не выполняются, а оригинальный пакет пропускается как есть.
11. Порядок инстансов важен
Если перед fakeddisorder стоит fake — он отправит свой фейк до того, как fakeddisorder начнёт свою последовательность. Если после fakeddisorder стоит ещё один инстанс — он увидит VERDICT_DROP и не получит оригинальный payload.
Проще говоря
Как показывает пример с
fakeпередfakeddisorder, инстансы отрабатывают по очереди, в порядке записи в профиле. Про такой же нюанс у самогоfake— 8. Порядок инстансов: fake всегда первый.
Отличия от fakedsplit, multisplit и multidisorder
Таблица сравнивает четыре функции по строкам; жирным выделено то, что относится к fakeddisorder. Отличия от других функций сегментации со стороны соседей разобраны в разделах multisplit, multidisorder и fakedsplit.
| Аспект | multisplit | multidisorder | fakedsplit | fakeddisorder |
|---|---|---|---|---|
| Количество позиций | Список (любое кол-во) | Список (любое кол-во) | Одна | Одна |
| Порядок отправки | Прямой (1-2-3) | Обратный (3-2-1) | Прямой (1-2) | Обратный (2-1) |
| Фейковые сегменты | Нет | Нет | Да (до 4 шт.) | Да (до 4 шт.) |
| Всего пакетов (max) | N+1 (по кол-ву позиций) | N+1 | 6 | 6 |
| seqovl тип | Только число | Маркер | Только число | Маркер |
| seqovl к какому сегменту | 1-й (прямой) | 2-й (предпоследний) | 1-й реальный (part 1) | 2-й реальный (part 2) |
| Fooling к | Всем сегментам | Всем сегментам | Только к фейкам | Только к фейкам |
| reconstruct к | Всем | Всем | Только к фейкам | Только к фейкам |
| repeats к | Всем | Всем | Только к фейкам | Только к фейкам |
| ipfrag | Да | Да | Нет | Нет |
| Уровень запутывания | Один (разрез) | Два (разрез + порядок) | Два (разрез + фейки) | Три (разрез + фейки + порядок) |
Когда что использовать
| Сценарий | Рекомендация |
|---|---|
| DPI не реассемблирует TCP, достаточно разрезать | multisplit |
| DPI реассемблирует, но не проверяет порядок | multidisorder |
| DPI проверяет порядок, но не отличает фейки | fakedsplit |
| DPI проверяет и порядок, и содержимое | fakeddisorder |
| Нужна IP-фрагментация поверх TCP | multisplit или multidisorder |
| Нужно несколько позиций разреза с фейками | Цепочка fakedsplit / fakeddisorder |
Таблица выше отвечает на вопрос, какую функцию брать под какой тип DPI; для конкретного провайдера выбор всё равно проверяют на деле по чек-листу честной проверки. Для нескольких позиций разреза с фейками остаётся цепочка инстансов (нюанс 2).
Миграция с nfqws1
nfqws1 — движок первой версии zapret: там техники включаются флагами --dpi-desync-*, а во второй версии --lua-desync. Таблица ниже переводит ключи первой версии в аргументы fakeddisorder, общая таблица соответствий флагов есть в заметке desync.
Соответствие параметров
| nfqws1 | nfqws2 |
|---|---|
--dpi-desync=fakeddisorder | --lua-desync=fakeddisorder |
--dpi-desync-split-pos=midsld | :pos=midsld |
--dpi-desync-fooling=badseq | :tcp_ack=-66000:tcp_ts_up |
--dpi-desync-fooling=badack | :tcp_ack=-66000:tcp_ts_up |
--dpi-desync-fooling=md5sig | :tcp_md5 |
--dpi-desync-fooling=datanoack | :tcp_flags_unset=ACK |
--dpi-desync-fooling=hopbyhop | :ip6_hopbyhop |
--dpi-desync-fooling=hopbyhop2 | :ip6_hopbyhop2 |
--dpi-desync-split-seqovl=N | :seqovl=N |
--dpi-desync-split-seqovl-pattern=HEX | :seqovl_pattern=HEX |
--dpi-desync-fake-tls=FILE | :pattern=<blobname> (предзагрузить через --blob=) |
--dpi-desync-any-protocol | Не нужно; или payload=all в инстансе |
Пример полной миграции
# nfqws1:
nfqws --dpi-desync=fakeddisorder \
--dpi-desync-fooling=badseq \
--dpi-desync-split-pos=midsld
# nfqws2 (эквивалент):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up# nfqws1 (комплексный):
nfqws --dpi-desync=fakeddisorder \
--dpi-desync-fooling=md5sig \
--dpi-desync-split-pos=midsld \
--dpi-desync-split-seqovl=5 \
--dpi-desync-split-seqovl-pattern=0x1603030000
# nfqws2 (эквивалент):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fakeddisorder:pos=midsld:tcp_md5:seqovl=5:seqovl_pattern=0x1603030000# nfqws1 (фулл):
nfqws --dpi-desync=fake,fakeddisorder \
--dpi-desync-fooling=badseq \
--dpi-desync-split-pos=midsld \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
# nfqws2 (эквивалент — fake отдельным инстансом):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_ack=-66000:tcp_ts_up:tls_mod=rnd,rndsni,dupsid \
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upПрактические примеры
Ниже 18 примеров: от минимального до боевых. Каждая команда — строка для профиля; подробности про аргументы pos, seqovl, pattern, nofake1–nofake4 — в разделах Маркер позиции (pos), seqovl и Полный список аргументов.
1. Минимальный (с fooling)
--lua-desync=fakeddisorder:tcp_ack=-66000:tcp_ts_upРазрезает payload после 2-го байта (pos=2 по умолчанию) с badseq fooling для фейков. 6 пакетов.
2. TLS: разрез посередине SNI
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upSNI разрезан пополам. DPI видит 6 пакетов с перемешанными фейковыми и реальными сегментами в обратном порядке — двойное запутывание.
3. TLS: разрез + seqovl (тройное запутывание)
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:seqovl=5:seqovl_pattern=0x1603030000:tcp_ack=-66000:tcp_ts_upТри уровня: фейки + disorder + seqovl. Real part 2 имеет 4-байтовый TLS-фейк слева (5→resolve→5, 5-1=4), который DPI может принять за начало TLS record.
4. TLS: seqovl как маркер
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:seqovl=sld:tcp_ack=-66000:tcp_ts_upseqovl разрешается в позицию начала SLD. Фейковая часть слева от real part 2 — от начала SLD до позиции разреза.
5. HTTP: разрез в начале hostname
--payload=http_req --lua-desync=fakeddisorder:pos=host:tcp_ack=-66000:tcp_ts_upДля GET / HTTP/1.1\r\nHost: example.com\r\n... разрежет так, что часть 1 содержит заголовки до hostname, а часть 2 — hostname и далее.
6. HTTP: кастомный паттерн для фейков
--payload=http_req --lua-desync=fakeddisorder:pos=host:pattern=0x474554202F20485454502F312E310D0A:tcp_ack=-66000:tcp_ts_upФейки заполняются паттерном, похожим на начало HTTP-запроса (GET / HTTP/1.1\r\n). DPI может принять фейк за реальный запрос к другому хосту.
7. С tcp_md5 fooling
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_md5:tcp_ts_upВместо badseq используется TCP MD5 signature option. Сервер отбрасывает пакеты с неверной MD5 подписью.
8. С badsum reconstruct
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:badsumФейки отправляются с испорченным TCP checksum. Двойная защита: и badseq, и badsum.
9. Без первого и последнего фейка (4 пакета)
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:nofake1:nofake4Отключены фейки “снаружи” (первый и последний). Остаётся: real part 2, fake part 2 (после), fake part 1 (перед), real part 1. 4 пакета.
10. Только внутренние фейки (4 пакета)
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:nofake1:nofake2Вся часть 2 — только реальная (без обрамления фейками). Часть 1 — с фейками. Полезно, когда DPI анализирует только начало потока.
11. Без всех фейков (эквивалент disorder)
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:nofake1:nofake2:nofake3:nofake4Отключены все 4 фейка. Остаются только 2 реальных пакета в обратном порядке. Фактически — multidisorder с одной позицией. Fooling не нужен (применяется только к фейкам, которых нет).
12. Цепочка: fake + fakeddisorder
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:repeats=5:tcp_md5 \
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upСначала 5 фейковых TLS ClientHello (с md5sig fooling), затем fakeddisorder с 6 пакетами. Итого 11 пакетов. Массированная атака на DPI.
13. С произвольным blob вместо payload
--blob=mydata:@custom_payload.bin \
--lua-desync=fakeddisorder:blob=mydata:pos=50:tcp_ack=-66000:tcp_ts_upРежет и отправляет произвольные данные из файла вместо реального payload.
14. С optional для необязательного blob
--lua-desync=fakeddisorder:blob=maybe_missing:optional:pos=midsld:tcp_ack=-66000:tcp_ts_upЕсли blob maybe_missing не существует — тихий пропуск, без ошибок.
15. Боевой пример для YouTube
--filter-tcp=443 --hostlist=youtube.txt \
--payload=tls_client_hello \
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_upРазрезает TLS ClientHello посередине SNI домена YouTube с фейками в обратном порядке.
16. Комбинация fooling + repeats для фейков
--payload=tls_client_hello \
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:repeats=3Каждый из 4 фейковых пакетов отправляется 3 раза. Реальные — по 1 разу. Итого 14 пакетов (4*3 + 2).
17. IPv6: hop-by-hop fooling
--payload=tls_client_hello \
--lua-desync=fakeddisorder:pos=midsld:ip6_hopbyhop:tcp_ts_upДля IPv6: фейки отправляются с extension header hop-by-hop, который сбрасывается на первом маршрутизаторе. Реальные сегменты — без hop-by-hop.
18. Отладка: не блокировать оригинал
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:nodropОтправляет 6 нарезанных пакетов И пропускает оригинальный (для экспериментов/отладки).
📚 См. также
- desync — обзор всех функций
--lua-desyncи общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты - жизненный цикл desync-функции — восемь стадий, общих для всех техник дурения: отсев транспорта, выбор данных, replay, вердикты
- multisplit — тот же разрез, но без фейков и без обратного порядка; там же подробно разобран принцип
seqovl - multidisorder — тот же обратный порядок, но без фейков; там
seqovlтоже маркер, разобранный в разделе seqovl как маркер - fakedsplit — те же фейки вокруг каждой части, но части уходят в прямом порядке
- hostfakesplit — разрез по границам имени хоста с подмешиванием фейкового имени
- tcpseg — отправка произвольного диапазона данных, ограниченного двумя маркерами
- oob — сегментация с urgent-байтом вместо разреза
- fake — отдельный фейковый пакет, с которым
fakeddisorderкомбинируют в цепочке - blob — как объявлять и передавать данные для аргументов
blob,patternиseqovl_pattern - payload — распознавание типов протоколов, от которого зависит работа относительных маркеров
- ts-and-fooling — что такое fooling, как фейк заставляют «умереть» до сервера и зачем нужен
tcp_ts_up - verify-strategy — как честно проверить, работает ли подобранная комбинация
- последовательность аргументов — как выстраивается цепочка инстансов в профиле
- profile — где
--lua-desyncживёт среди остальных настроек - DPI и ТСПУ — как устроена инспекция трафика, против которой работает
fakeddisorder
Источники:
lua/zapret-antidpi.lua:908-1021,docs/manual.md:4165-4206,docs/manual.en.md:3984-4025из репозитория zapret2.
🤖 Эти статьи открыты — можно обучать на них ИИ
При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: исходник этой заметки · скачать весь репозиторий одним zip-архивом.