Some checks failed
Published content check / validate (push) Failing after 17s
git.zapret.moe в России заблокирован по имени, как и wiki.zapret.moe. Ссылки на Forgejo во всех заметках, футерах, wizard.html и главной заменены на git2.zapret.moe (включая закодированные адреса значков shields.io). Какое имя рабочее, теперь решают реестры хоста /etc/zapret-*-domains.conf через zapret-site-names: pre-commit хук vault отклоняет добавленные строки с заблокированным именем, сборка Quartz сама переводит ссылки и baseUrl на рабочие имена. Правило в AGENTS.md переписано под этот механизм. scripts/wiki-stats.py: переходы с любых имён вики и git (wikiN, gitN) больше не считаются внешними сайтами. content-check: проверка зеркал сторонних проектов узнаёт любое имя git. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1224 lines
108 KiB
Markdown
1224 lines
108 KiB
Markdown
---
|
||
date: 2026-02-17
|
||
tags:
|
||
- zapret
|
||
- zapret2
|
||
- nfqws2
|
||
- lua
|
||
- lua-desync
|
||
- antidpi
|
||
- tcp
|
||
- fakeddisorder
|
||
- fakedsplit
|
||
- disorder
|
||
- seqovl
|
||
aliases:
|
||
- fakeddisorder
|
||
description: "Справочник по fakeddisorder в zapret2/nfqws2: разрез TCP-payload с фейками в обратном порядке, маркеры pos и seqovl, все аргументы, миграция с nfqws1."
|
||
---
|
||
|
||
> [!mirror] Резервное зеркало
|
||
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Zapret2/desync/fakeddisorder](https://wiki2.zapret.moe/Zapret2/desync/fakeddisorder)
|
||
|
||
# `fakeddisorder` — TCP-сегментация с фейками в обратном порядке (zapret2 / nfqws2)
|
||
|
||
## Коротко и по-простому
|
||
|
||
Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке `Host:` запроса. ТСПУ (Технические средства противодействия угрозам — оборудование [[DPI/DPI|DPI]], то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения и по нему решает, что делать с соединением дальше.
|
||
|
||
HTTP-запросы и TLS-соединения передаются по протоколу TCP, а TCP отправляет данные кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер строго по этим номерам. Поэтому ему безразлично, в каком порядке куски доехали: если конец запроса пришёл раньше начала, сервер подержит его в буфере, дождётся начала и соберёт исходный поток. Почему это гарантирует именно TCP, разобрано в нюансе [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|«Работает только с TCP» из статьи про multisplit]].
|
||
|
||
`fakeddisorder` использует эту особенность TCP дважды, и понять его проще, если сначала разобрать два приёма по отдельности. Первый приём — фейки. Фейк — это пакет-обманка: он несёт мусорные данные (по умолчанию нули), но у него тот же размер и тот же seq, что у настоящего куска, поэтому со стороны он похож на повторную отправку этого куска. Если сервер примет такой пакет, мусор попадёт в поток и соединение сломается ([[#4. Fooling ОБЯЗАТЕЛЕН для фейков|нюанс 4]]). Поэтому фейкам нарочно портят заголовок так, чтобы сервер их отбросил, а DPI всё равно мог прочитать; эту порчу называют fooling ([[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). В большинстве команд этой статьи фейкам сдвигают номер подтверждения (ACK, поле «что подтверждено») на 66000: `tcp_ack=-66000`. Из опций порчи к настоящим кускам применяется только `tcp_ts_up` (плюс политика нумерации `ip_id`): он ничего не портит, а поднимает опцию TCP timestamp в начало заголовка, и без этого Linux-сервер ненадёжно отбрасывает фейки с неверным ACK; подробнее в нюансе [[#5. tcp_ts_up рекомендуется с tcp_ack|5]] и в разделе [[#Применение fooling и reconstruct|Применение fooling и reconstruct]].
|
||
|
||
Второй приём — обратный порядок (по-английски disorder): куски отправляются не от начала к концу, а наоборот. Серверный TCP-стек рассчитан на куски вразнобой, он буферизирует их и ждёт недостающие. DPI, как правило, такой роскоши не имеет: получив конец запроса первым, он видит данные без начала и может не распознать протокол; DPI с пересборкой потока теоретически справится, но у многих ограниченный буфер или таймаут, и они не дожидаются всех частей. Подробнее — в разделе [[Zapret2/desync/multidisorder#Почему disorder ломает DPI|Почему disorder ломает DPI]] статьи про multidisorder.
|
||
|
||
Теперь шаги самого `fakeddisorder`. Движок Zapret 2 (`nfqws2`) перехватывает исходящий пакет с ClientHello или HTTP-запросом, и функция делает четыре вещи. Сначала режет данные пакета в одном месте на две части: место задаёт аргумент `pos`, это может быть число или маркер, привязанный к устройству запроса, например `midsld` — середина домена второго уровня, то есть главной части имени сайта (у `www.youtube.com` это `youtube`); по умолчанию разрез после второго байта, а список позиций, как у [[Zapret2/desync/multisplit|multisplit]], здесь не принимается — только один маркер ([[#Маркер позиции (pos)|Маркер позиции (pos)]]). Потом готовит по два фейка на каждую часть с тем же размером и тем же seq. Потом отправляет шесть пакетов, начиная со второй части: FAKE2, REAL2, FAKE2, FAKE1, REAL1, FAKE1, где REAL — настоящий кусок, FAKE — фейк, а цифра — номер части запроса. В конце выбрасывает перехваченный оригинал, чтобы те же данные не ушли второй раз. Подробная раскладка по пакетам — в разделе [[#Порядок отправки сегментов (6 пакетов)|Порядок отправки сегментов (6 пакетов)]].
|
||
|
||
Сервер получает все шесть пакетов, четыре фейка отбрасывает из-за испорченного ACK, принимает REAL2, держит его в буфере до прихода REAL1 и по номерам seq собирает ровно тот запрос, который отправил браузер, поэтому отвечает как обычно. DPI видит другое: по три сегмента на каждую позицию (настоящий и два фейка, со стороны они похожи на повторные отправки одного и того же) и конец запроса раньше начала. Какой из трёх сегментов настоящий, DPI не знает, и он может проглотить фейки как реальные данные. Как это выглядит с обеих сторон, показано в разделах [[Zapret2/desync/fakedsplit#Что видит DPI|Что видит DPI]] и [[Zapret2/desync/fakedsplit#Что видит сервер|Что видит сервер]] статьи про fakedsplit, где схема та же, но порядок прямой. Сработает ли это на конкретном провайдере, заранее сказать нельзя: результат зависит от конкретного DPI, способ порчи фейков выбирают по месту запуска и подтверждают тестами ([[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталог способов]]), а итог проверяют на своём провайдере по [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листу честной проверки]].
|
||
|
||
Команда из быстрого старта `fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up` режет ClientHello посередине домена второго уровня, но первой отправляет вторую часть, и каждая часть окружена фейками того же размера и с тем же seq: FAKE2, REAL2, FAKE2, FAKE1, REAL1, FAKE1. ТСПУ получает по три сегмента на каждую позицию, да ещё конец запроса раньше начала; сервер отбрасывает фейки по испорченному номеру подтверждения (ACK сдвинут на 66000) и держит REAL2 в буфере до прихода REAL1. Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.
|
||
|
||
![[desync-anim-fakeddisorder.svg|Анимация fakeddisorder: части ClientHello в обратном порядке, каждая окружена фейками с тем же seq]]
|
||
|
||
Самая короткая команда включает `fakeddisorder` со значениями по умолчанию: один разрез после второго байта (`pos=2`), обработка только распознанных протоколов (`payload=known`) и только исходящих пакетов (`dir=out`), а фейкам портится ACK. Строку `--lua-desync=…` пишут в [[Zapret2/profile#Анатомия профиля|профиль]] — набор правил Zapret 2 для определённого трафика:
|
||
|
||
```bash
|
||
--lua-desync=fakeddisorder:tcp_ack=-66000
|
||
```
|
||
|
||
Статья советует добавлять к `tcp_ack` ещё и `tcp_ts_up` (см. [[#5. tcp_ts_up рекомендуется с tcp_ack|нюанс 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 видит цельный сегмент вместе с заполнителем и может не распознать в нём имя сайта; общий принцип с раскладкой по байтам разобран в разделе [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]] статьи про multisplit. У `fakeddisorder` два отличия от `multisplit` и `fakedsplit`. Во-первых, `seqovl` здесь применяется ко второй части, а не к первой. Во-вторых, его значение не число, а маркер того же вида, что `pos` (`seqovl=host`, `seqovl=midsld-2`), как у [[Zapret2/desync/multidisorder#seqovl как маркер|multidisorder]]; результат (после вычитания единицы) должен быть строго меньше `pos-1`, иначе `seqovl` отменяется. Подробно — в разделах [[#seqovl — скрытый фейк внутри реального сегмента|seqovl — скрытый фейк внутри реального сегмента]] и [[#seqovl — маркер, а не число|seqovl — маркер, а не число]].
|
||
|
||
Ближайшие соседи `fakeddisorder` — три техники с одним из его приёмов. [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]] только режет и шлёт по порядку, без фейков. [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|multidisorder]] режет и шлёт в обратном порядке, тоже без фейков. [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] окружает части такими же фейками, но шлёт в прямом порядке. Сводная таблица по ним — в разделе [[#Отличия от fakedsplit, multisplit и multidisorder|Отличия от fakedsplit, multisplit и multidisorder]], а подсказка «когда что брать» — в подразделе [[#Когда что использовать|Когда что использовать]]: `fakeddisorder` рассчитан на DPI, который проверяет и порядок, и содержимое. Ограничения такие: `pos` — один маркер, а не список ([[#2. Только ОДИН маркер в pos|нюанс 2]]); IP-фрагментации функция не поддерживает, для неё нужен `multisplit` или `multidisorder` ([[#6. ipfrag НЕ задействуется|нюанс 6]]); работает только с TCP ([[#1. Работает только с TCP|нюанс 1]]). Флаги `nofake1`–`nofake4` отключают отдельные фейки, а если отключить все четыре, останется [[multidisorder]] с одной позицией ([[#11. Без всех фейков (эквивалент disorder)|пример 11]]).
|
||
|
||
---
|
||
|
||
## Оглавление
|
||
|
||
- [Коротко и по-простому](#коротко-и-по-простому)
|
||
- [Справка: где функция в коде и родственники](#справка-где-функция-в-коде-и-родственники)
|
||
- [Зачем нужен fakeddisorder](#зачем-нужен-fakeddisorder)
|
||
- [Быстрый старт](#быстрый-старт)
|
||
- [Откуда берутся данные для нарезки](#откуда-берутся-данные-для-нарезки)
|
||
- [Маркер позиции (pos)](#маркер-позиции-pos)
|
||
- [Типы маркеров](#типы-маркеров)
|
||
- [Относительные маркеры](#относительные-маркеры)
|
||
- [Арифметика маркеров](#арифметика-маркеров)
|
||
- [Как маркер разрешается в коде](#как-маркер-разрешается-в-коде)
|
||
- [Важные нюансы pos](#важные-нюансы-pos)
|
||
- [seqovl — скрытый фейк внутри реального сегмента](#seqovl--скрытый-фейк-внутри-реального-сегмента)
|
||
- [Принцип работы seqovl в fakeddisorder](#принцип-работы-seqovl-в-fakeddisorder)
|
||
- [seqovl — маркер, а не число](#seqovl--маркер-а-не-число)
|
||
- [Зачем seqovl лучше обычного fooling](#зачем-seqovl-лучше-обычного-fooling)
|
||
- [seqovl_pattern](#seqovl_pattern)
|
||
- [Фейковые сегменты и pattern](#фейковые-сегменты-и-pattern)
|
||
- [Полный список аргументов](#полный-список-аргументов)
|
||
- [A) Собственные аргументы fakeddisorder](#a-собственные-аргументы-fakeddisorder)
|
||
- [B) Standard direction](#b-standard-direction)
|
||
- [C) Standard payload](#c-standard-payload)
|
||
- [D) Standard fooling](#d-standard-fooling)
|
||
- [E) Standard ipid](#e-standard-ipid)
|
||
- [F) Standard reconstruct](#f-standard-reconstruct)
|
||
- [G) Standard rawsend](#g-standard-rawsend)
|
||
- [Порядок отправки сегментов (6 пакетов)](#порядок-отправки-сегментов-6-пакетов)
|
||
- [ASCII-диаграмма полной последовательности](#ascii-диаграмма-полной-последовательности)
|
||
- [Пример с seqovl](#пример-с-seqovl)
|
||
- [Пример с nofake-флагами](#пример-с-nofake-флагами)
|
||
- [Применение fooling и reconstruct](#применение-fooling-и-reconstruct)
|
||
- [Поведение при replay / reasm](#поведение-при-replay--reasm)
|
||
- [Автосегментация по MSS](#автосегментация-по-mss)
|
||
- [Место в общем скелете](#место-в-общем-скелете)
|
||
- [Псевдокод алгоритма](#псевдокод-алгоритма)
|
||
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
|
||
- [Отличия от fakedsplit, multisplit и multidisorder](#отличия-от-fakedsplit-multisplit-и-multidisorder)
|
||
- [Миграция с nfqws1](#миграция-с-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`, чтобы оригинальный пакет не ушёл.
|
||
|
||
> [!note] Проще говоря
|
||
> Payload — полезные данные пакета без сетевых заголовков: сам HTTP-запрос или сам ClientHello. Reasm — те же данные, собранные движком из нескольких пакетов, если запрос в один пакет не влез. Blob — заранее заготовленные данные, которые можно подсунуть вместо настоящих. Какой из трёх источников выбирается, разобрано в разделе [[#Откуда берутся данные для нарезки|Откуда берутся данные для нарезки]]. `VERDICT_DROP` — решение «перехваченный пакет не отправлять»; как движок складывает такие решения от разных функций, описано в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]]. «Двойное запутывание» — это два приёма сразу: фейки (запутывание по содержимому) и обратный порядок отправки (запутывание по порядку); оба разобраны в разделе [[#Зачем нужен fakeddisorder|Зачем нужен fakeddisorder]].
|
||
|
||
Родственные функции: [[multisplit]] (прямой порядок, без фейков), [[multidisorder]] (обратный порядок, без фейков), [[fakedsplit]] (с фейками, прямой порядок), [[hostfakesplit]] (по hostname), [[tcpseg]] (диапазон), [[oob]] (urgent byte).
|
||
|
||
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `fakeddisorder` от этого скелета отличается.
|
||
|
||
---
|
||
|
||
## Зачем нужен fakeddisorder
|
||
|
||
DPI анализирует TCP-поток, пытаясь собрать полный payload и найти в нём сигнатуры (hostname в HTTP, SNI в TLS). `fakeddisorder` атакует DPI двумя способами одновременно:
|
||
|
||
1. **Запутывание по содержимому (fake).** Между реальными сегментами посылаются фейковые, содержащие мусорные данные. DPI должен отличить настоящие сегменты от поддельных. Фейки отбрасываются сервером благодаря [[Zapret2/ts-and-fooling|fooling]] (badseq, badsum, TTL и т.д.), но DPI может их проглотить.
|
||
|
||
2. **Запутывание по порядку (disorder).** Сегменты отправляются в **обратном порядке**: сначала вторая часть payload, затем первая. DPI, ожидающий последовательного потока, может быть сбит с толку.
|
||
|
||
> [!note] Проще говоря
|
||
> Сигнатура — заранее известная строка, по которой DPI узнаёт сайт. Фейк — пакет-обманка с мусором вместо данных, у которого тот же размер и тот же seq (порядковый номер байта в потоке), что у настоящего куска: сервер отбрасывает его благодаря порче заголовка, а DPI может принять за настоящие данные. Disorder — отправка кусков не по порядку: сервер ждёт недостающие и склеивает всё по seq, а DPI, которому нужен последовательный поток, может запутаться. Фейки подробнее разобраны в разделах [[Zapret2/desync/fake#Зачем нужен fake|Зачем нужен fake]] и [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|Зачем нужен fakedsplit]], обратный порядок — в разделе [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|Обратный порядок отправки — суть disorder]].
|
||
|
||
**Двойное запутывание** (содержимое + порядок) эффективнее, чем каждый приём по отдельности:
|
||
|
||
![[wiki-anim-z2-orders.svg|Анимация: порядок отправки у multisplit, multidisorder, fakedsplit и fakeddisorder]]
|
||
|
||
Как читать схему: четыре строки — четыре функции, стрелка показывает порядок отправки, каждые квадратные скобки — один TCP-сегмент. `F1`–`F4` — фейковые сегменты, «часть 1» и «часть 2» — настоящие куски payload. В нижней строке фейки стоят по обе стороны от каждой настоящей части, а часть 2 уходит раньше части 1. Раскладка на шесть пакетов подробно показана в разделе [[#Порядок отправки сегментов (6 пакетов)|Порядок отправки сегментов (6 пакетов)]].
|
||
|
||
Сервер при этом корректно собирает поток — TCP-стек гарантирует это через sequence numbers, а фейки отбрасываются благодаря fooling.
|
||
|
||
> [!note] Проще говоря
|
||
> Для сервера порядок прибытия не важен: TCP-стек сам расставит куски по seq и отбросит фейки. Для DPI важны и порядок, и то, какие из сегментов настоящие, поэтому обман сразу по двум признакам может сработать там, где одного было бы мало. Какую функцию брать под какой тип DPI, подсказывает таблица в подразделе [[#Когда что использовать|Когда что использовать]].
|
||
|
||
---
|
||
|
||
## Быстрый старт
|
||
|
||
Ниже готовые строки для [[Zapret2/profile#Анатомия профиля|профиля]] Zapret 2, от самой короткой к более сложным. Флаг `--payload=…` работает как фильтр для всех вызовов `--lua-desync`, которые идут после него (до следующего `--payload` или до конца профиля): они срабатывают только на данных указанного типа. Поэтому `--payload` пишут перед `--lua-desync`, а не после (подробно — в [[последовательность аргументов]]).
|
||
|
||
Минимально (разрез по позиции 2, payload=known, dir=out):
|
||
|
||
```bash
|
||
--lua-desync=fakeddisorder:tcp_ack=-66000
|
||
```
|
||
|
||
Здесь `payload=known` означает «только распознанные протоколы», а `dir=out` — «только исходящие пакеты»; оба аргумента разобраны в разделах [[#B) Standard direction|B) Standard direction]] и [[#C) Standard payload|C) Standard payload]]. Фейкам портится ACK (`tcp_ack=-66000`), а `tcp_ts_up` в этой минимальной строке нет: статья советует ставить его всегда вместе с `tcp_ack` ([[#5. tcp_ts_up рекомендуется с tcp_ack|нюанс 5]]).
|
||
|
||
Типовой TLS-разрез посередине SNI:
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
Функция срабатывает только на TLS ClientHello и режет его посередине домена второго уровня; уходят шесть пакетов, вторая часть первой.
|
||
|
||
TLS с seqovl для тройного запутывания:
|
||
|
||
```bash
|
||
--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 — скрытый фейк внутри реального сегмента|seqovl — скрытый фейк внутри реального сегмента]].
|
||
|
||
HTTP с кастомным паттерном фейков:
|
||
|
||
```bash
|
||
--payload=http_req --lua-desync=fakeddisorder:pos=host:pattern=0x474554202F:tcp_ack=-66000
|
||
```
|
||
|
||
Здесь `pos=host` режет HTTP-запрос в начале имени хоста, а `pattern=0x474554202F` заполняет фейки не нулями, а образцом, похожим на начало HTTP GET; подробнее — в разделе [[#Фейковые сегменты и pattern|Фейковые сегменты и 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 может распознать.
|
||
|
||
> [!note] Проще говоря
|
||
> Payload — полезные данные текущего пакета без сетевых заголовков, reasm — те же данные, собранные движком из нескольких пакетов, blob — заранее заготовленные данные, которые подставляются вместо настоящих (как их объявлять — в заметке [[blob]]). Функция берёт первый из трёх источников, который есть: сначала blob, если он задан, потом собранный reasm, в последнюю очередь текущий пакет. Тот же выбор описан как стадия 4 общего скелета в заметке [[жизненный цикл desync-функции]]. Какие payload распознаёт `nfqws2` и почему от этого зависят относительные маркеры вроде `midsld`, разобрано в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|о типах payload]].
|
||
|
||
---
|
||
|
||
## Маркер позиции (pos)
|
||
|
||
![[desync-anim-hub-markers.svg|Анимация: маркеры позиций pos на HTTP-запросе и на TLS ClientHello, разрез по pos=host,midsld,endhost]]
|
||
|
||
`pos` — главный аргумент `fakeddisorder`. Определяет **где** внутри payload будет произведён разрез. В отличие от [[multisplit]] и [[multidisorder]], `pos` — это **один маркер**, а не список. Payload всегда разрезается на **ровно 2 части**.
|
||
|
||
> [!note] Проще говоря
|
||
> Маркер — способ указать место разреза либо числом байт, либо словом («середина домена второго уровня»), которое `nfqws2` сам переводит в число по устройству запроса. В `fakeddisorder` маркер один, поэтому запрос всегда делится ровно на две части. Как задают список маркеров, разобрано в разделах [[Zapret2/desync/multisplit#Маркеры позиций (pos)|Маркеры позиций (pos)]] статьи про multisplit и [[Zapret2/desync/multidisorder#Маркеры позиций (pos)|Маркеры позиций (pos)]] статьи про multidisorder.
|
||
|
||
### Типы маркеров
|
||
|
||
| Тип | Описание | Пример |
|
||
|:----|:---------|:-------|
|
||
| **Абсолютный положительный** | Смещение от начала payload, считая с 0. `pos=N` → первые N байт становятся отдельным сегментом | `2`, `5`, `100` |
|
||
| **Абсолютный отрицательный** | Смещение от конца payload. `-1` = последний байт | `-1`, `-10`, `-50` |
|
||
| **Относительный** | Логическая позиция внутри распознанного payload. Привязана к структуре протокола | `midsld`, `host`, `sniext` |
|
||
|
||
Абсолютные маркеры не зависят от содержимого. Относительные привязаны к устройству запроса и работают только тогда, когда `nfqws2` распознал протокол: в столбце «Для каких payload» таблицы ниже указано, для каких типов данных маркер имеет смысл (про типы payload — [[Zapret2/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` — функцию для **одного** маркера:
|
||
|
||
```lua
|
||
local spos = desync.arg.pos or "2"
|
||
local pos = resolve_pos(data, desync.l7payload, spos)
|
||
```
|
||
|
||
`resolve_pos` возвращает одно число (абсолютную Lua-позицию, 1-based) или `nil`, если маркер не может быть разрешён.
|
||
|
||
> [!note] Проще говоря
|
||
> `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` для `unknown` payload), fakeddisorder логирует `"cannot resolve pos"` и пропускает операцию.
|
||
- **По умолчанию pos="2".** Если `pos` не задан, разрез по позиции 2 — payload делится на первые 2 байта и весь остаток (`pos=N` даёт первые N байт отдельным сегментом, см. таблицу типов маркеров выше; описание «1-й байт и остальное» относится к `pos=1`).
|
||
|
||
> [!note] Проще говоря
|
||
> Если разрез не получился (позиция 0 или неразрешимый маркер), функция ничего не отправляет и только записывает причину в лог. Такие случаи разобраны и в нюансах [[#2. Только ОДИН маркер в pos|2]] и [[#3. Позиция 1 не работает|3]] ниже.
|
||
|
||
---
|
||
|
||
## seqovl — скрытый фейк внутри реального сегмента
|
||
|
||
**seqovl** (Sequence Overlap) — техника скрытого замешивания фейковых данных в реальный TCP-сегмент через манипуляцию TCP sequence number. В `fakeddisorder` seqovl применяется **только ко второму реальному сегменту** (real part 2 — первый реальный пакет в порядке отправки).
|
||
|
||
Sequence Overlap — «перекрытие по номерам последовательности». В [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|multisplit]] и [[Zapret2/desync/fakedsplit#seqovl — скрытый фейк внутри сегмента|fakedsplit]] эта техника применяется к первому настоящему сегменту, а в `fakeddisorder` — ко второму, который уходит первым из настоящих: сравнение — в [[#8. seqovl применяется только к real part 2|нюансе 8]]. У [[Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие|multidisorder]] `seqovl` тоже маркер, и его механика разобрана в отдельном разделе той статьи.
|
||
|
||
### Принцип работы seqovl в fakeddisorder
|
||
|
||
![[desync-anim-hub-seqovl.svg|Анимация: seqovl дописывает слева паттерн и сдвигает seq на 990; DPI читает сегмент целиком, сервер выбрасывает байты левее 1000]]
|
||
|
||
|
||
|
||
|
||
|
||
> [!note] Проще говоря
|
||
> Перед настоящей второй частью приклеено 10 байт заполнителя (`PATTERN`), а seq сдвинут назад на 10. Границей приёма служит `pos-1`: всё левее сервер выбрасывает, всё от границы принимает. DPI видит цельный сегмент, начинающийся с `pos-11`, и может не распознать в нём SNI. Общее объяснение окна приёма и разбор того, что видят DPI и сервер, есть в разделе [[Zapret2/desync/multisplit#Принцип работы seqovl|Принцип работы seqovl]] статьи про multisplit.
|
||
|
||
### seqovl — маркер, а не число
|
||
|
||
**Критическое отличие от [[multisplit]] и [[fakedsplit]]:** в `fakeddisorder` (как и в [[multidisorder]]) `seqovl` — это **маркер**, а не просто число. Он разрешается через `resolve_pos`:
|
||
|
||
```lua
|
||
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` — маркер с арифметикой
|
||
|
||
> [!note] Проще говоря
|
||
> `seqovl` задают так же, как место разреза: числом или словом-маркером. Внутри функции позиция сначала переводится в номер байта, а потом от неё отнимается 1 (Lua→0-based, то есть переход от счёта с единицы к счёту с нуля). Как то же устроено у другой функции — в разделе [[Zapret2/desync/multidisorder#seqovl как маркер|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'
|
||
```
|
||
|
||
> [!note] Проще говоря
|
||
> Отмена `seqovl` не отменяет всю функцию: по псевдокоду ниже в этом случае `seqovl` обнуляется, а разрез и отправка шести пакетов идут дальше.
|
||
|
||
### Зачем seqovl лучше обычного fooling
|
||
|
||
Обычный способ спрятать фейк от сервера — [[Zapret2/ts-and-fooling#Что такое 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]].
|
||
|
||
```bash
|
||
# 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()`:
|
||
|
||
```lua
|
||
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 сервер примет фейки как реальные данные и поток будет повреждён.
|
||
|
||
> [!note] Проще говоря
|
||
> `pattern()` — функция, которая повторяет образец (по умолчанию один нулевой байт) до нужной длины. Фейк для части 2 по размеру равен части 2, фейк для части 1 — части 1, а seq у них такие же, как у настоящих частей. Образец задаёт аргумент `pattern`, его значение — имя blob (см. [[blob]]). Как именно фейкам портят заголовки, разобрано в разделе [[#Применение fooling и reconstruct|Применение 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` — разрез посередине SLD
|
||
- `pos=host` — разрез в начале hostname
|
||
- `pos=sniext+1` — один байт после начала SNI extension data
|
||
- `pos=endhost-2` — два байта до конца hostname
|
||
|
||
#### `seqovl`
|
||
|
||
- **Формат:** `seqovl=<marker>`
|
||
- **Тип:** **маркер** (не только число — в отличие от multisplit/fakedsplit!)
|
||
- **По умолчанию:** не задан (нет seqovl)
|
||
- **Описание:** Применяется ко **второму реальному сегменту** (real part 2). К данным слева добавляется `seqovl` байт `seqovl_pattern`, а TCP `th_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 hex
|
||
- `blob=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)
|
||
|
||
> [!note] Проще говоря
|
||
> Четыре флага соответствуют четырём фейкам по порядку отправки: `nofake1` — пакет 1, `nofake2` — пакет 3, `nofake3` — пакет 4, `nofake4` — пакет 6. Флага для отключения настоящих пакетов 2 и 5 среди аргументов нет. Примеры — в разделе [[#Пример с nofake-флагами|Пример с nofake-флагами]].
|
||
|
||
#### `optional`
|
||
|
||
- **Формат:** `optional` (флаг, без значения)
|
||
- **Описание:** Мягкий режим:
|
||
- Если задан `blob=...` и blob отсутствует — fakeddisorder **ничего не делает** (тихий skip, без ошибок)
|
||
- Если задан `seqovl_pattern=...` и blob отсутствует — используется нулевой паттерн (seqovl не отменяется)
|
||
|
||
> [!note] Проще говоря
|
||
> `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` — отсекает себя от противоположного направления.
|
||
|
||
> [!note] Проще говоря
|
||
> Cutoff (отсечение) — команда «эта функция больше не вызывается для такого направления или потока». Что происходит на стадии отсечения направления, описано в заметке [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадия 2 жизненного цикла]].
|
||
|
||
---
|
||
|
||
### C) Standard payload
|
||
|
||
| Параметр | Значения | По умолчанию |
|
||
|:---------|:---------|:-------------|
|
||
| `payload` | список типов через запятую | `known` |
|
||
|
||
Фильтр по типу payload на уровне Lua. Это **дополнительный** фильтр к `--payload=...` на уровне профиля.
|
||
|
||
- `payload=known` — только распознанные протоколы (`http_req`, `tls_client_hello`, `quic_initial` и т.д.)
|
||
- `payload=all` — любой payload, включая `unknown`
|
||
- `payload=tls_client_hello,http_req` — конкретные типы
|
||
- `payload=~unknown` — инверсия: всё кроме unknown
|
||
|
||
**Важно:** лучше ставить `--payload=...` на уровне профиля (C-код, быстрее), а не полагаться только на Lua-фильтр.
|
||
|
||
> [!note] Проще говоря
|
||
> Тип payload — то, что движок распознал в данных пакета (`tls_client_hello`, `http_req` и другие). Подробнее — в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|Что такое тип payload]]; порядок `--payload` и `--lua-desync` в профиле разобран в [[последовательность аргументов]].
|
||
|
||
---
|
||
|
||
### D) Standard fooling
|
||
|
||
Модификации L3/L4 заголовков. В `fakeddisorder` fooling применяется **только к фейковым сегментам**. К реальным применяется **только** `tcp_ts_up`.
|
||
|
||
Каждая строка таблицы — один способ порчи заголовков IP или TCP у фейка, чтобы сервер его отбросил. Разбор способов по слоям и того, где какой ломается, есть в разделе [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|Как заставить 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`.
|
||
|
||
> [!note] Проще говоря
|
||
> TCP timestamp — необязательная опция в конце TCP-заголовка (устройство опций разобрано в разделе [[Zapret2/ts-and-fooling#Что такое TCP options (и зачем fooling с ними играет)|Что такое 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` применяется **и к фейкам, и к оригиналам**.
|
||
|
||
> [!note] Проще говоря
|
||
> `ip_id` задаёт, как нумеруются IP ID пакетов: `seq` — по порядку, `rnd` — случайно, `zero` — нулями, `none` — не менять. К фейкам и к настоящим сегментам он применяется одинаково (таблица в разделе [[#Применение fooling и reconstruct|Применение fooling и reconstruct]]).
|
||
|
||
---
|
||
|
||
### F) Standard reconstruct
|
||
|
||
| Параметр | Описание |
|
||
|:---------|:---------|
|
||
| `badsum` | Испортить L4 (TCP) checksum при реконструкции raw-пакета |
|
||
|
||
`reconstruct` применяется **только к фейкам**. К оригиналам reconstruct не применяется.
|
||
|
||
> [!note] Проще говоря
|
||
> 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` установлен в пустую таблицу `{}` для обоих типов пакетов.
|
||
|
||
> [!note] Проще говоря
|
||
> Rawsend — отправка собранных пакетов в сеть; его параметры задают число повторов, интерфейс и метку firewall. IP-фрагментация — деление пакета на части на уровне IP: [[Zapret2/desync/multisplit#F) Standard ipfrag|multisplit её поддерживает]], а `fakeddisorder` нет.
|
||
|
||
---
|
||
|
||
## Порядок отправки сегментов (6 пакетов)
|
||
|
||
`fakeddisorder` всегда отправляет **6 пакетов** (если все nofake-флаги не установлены). Порядок **обратный** по отношению к [[fakedsplit]]: сначала идёт вторая часть (с фейками), затем первая (с фейками).
|
||
|
||
> [!note] Проще говоря
|
||
> Пакеты 1–3 — вторая часть запроса в окружении двух фейков, пакеты 4–6 — первая часть в таком же окружении. Смещение (seq offset) считается от начала payload и у фейка равно смещению настоящей части, которую он копирует. Столбец `nofake` показывает, каким флагом отключается пакет.
|
||
|
||
### ASCII-диаграмма полной последовательности
|
||
|
||
![[wiki-anim-z2-fakeddisorder6.svg|Анимация: шесть пакетов fakeddisorder, seqovl=10 и пропуск фейков флагами nofake]]
|
||
|
||
На схеме выше те же шесть пакетов в порядке отправки: справа подписано, что с каждым делает сервер.
|
||
|
||
|
||
|
||
**Сравнение с fakedsplit (прямой порядок):**
|
||
|
||
![[wiki-anim-z2-orders.svg|Анимация: порядок отправки у multisplit, multidisorder, fakedsplit и fakeddisorder]]
|
||
|
||
> [!note] Проще говоря
|
||
> Это та же схема, что у fakedsplit, только две половины поменялись местами: у fakedsplit сначала уходит первая часть, у `fakeddisorder` вторая. Схема fakedsplit для сравнения — в разделе [[Zapret2/desync/fakedsplit#Последовательность отправки (6 пакетов)|Последовательность отправки (6 пакетов)]].
|
||
|
||
### Пример с seqovl
|
||
|
||
|
||
|
||
Здесь у пакета 2 смещение 29, то есть 39 минус 10 байт заполнителя. По схеме выше окно приёма сервера для этого пакета начинается с seq=39, поэтому байты 29–38 он отбрасывает, а с 39-го принимает настоящую часть 2.
|
||
|
||
### Пример с nofake-флагами
|
||
|
||
|
||
|
||
> [!note] Проще говоря
|
||
> `nofake2` и `nofake4` убирают повторные фейки, которые идут после настоящих частей; остаётся по одному фейку перед каждой настоящей частью, всего четыре пакета.
|
||
|
||
Можно отключить все фейки кроме одного, или вообще все:
|
||
|
||
```
|
||
# Только 2 реальных пакета (все фейки отключены) = multidisorder с одной позицией
|
||
fakeddisorder:pos=midsld:nofake1:nofake2:nofake3:nofake4
|
||
```
|
||
|
||
---
|
||
|
||
## Применение fooling и reconstruct
|
||
|
||
Это ключевое отличие от [[multisplit]] и [[multidisorder]], где fooling применяется ко всем сегментам.
|
||
|
||
> [!note] Проще говоря
|
||
> У пакетов два набора опций: `opts_orig` для настоящих сегментов и `opts_fake` для фейков. Общий разбор наборов опций — в разделе [[Zapret2/жизненный цикл desync-функции#Наборы опций: кто их применяет и к чему|Наборы опций: кто их применяет и к чему]] заметки про жизненный цикл, а у fakedsplit — в разделе [[Zapret2/desync/fakedsplit#Два набора опций: opts_orig vs opts_fake|Два набора опций: 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
|
||
|
||
![[desync-anim-hub-reasm.svg|Анимация: большой ClientHello приходит тремя пакетами, ядро собирает reasm_data, функция режет весь буфер на первой части, остальные части дропаются]]
|
||
|
||
Многопакетный запрос `nfqws2` придерживает, собирает в буфер `reasm_data` и перепроигрывает через desync-функции. `fakeddisorder` использует штатную развилку из общего скелета desync-функций ([[жизненный цикл desync-функции]], стадия 6) без изменений: на первой части отправляет всю шестёрку пакетов (2 реальных + 4 фейка) по собранному буферу, остальные части дропает. Если отправка сорвалась, флаг «реасм уже отправлен» не ставится и оставшиеся части проходят как есть.
|
||
|
||
> [!note] Проще говоря
|
||
> Если ClientHello не влез в один пакет, движок придерживает пакеты, собирает запрос целиком в буфер `reasm_data` (reasm — от reassembly, «пересборка») и «проигрывает» (replay) его по кускам через функции дурения. Функция должна сработать один раз, на первом куске, а остальные куски выбросить. Подробно — в разделе [[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)|Стадия 6. Развилка перепроигрывания (replay)]].
|
||
|
||
Специфика функции: раз работа идёт по всему реасму, обратный порядок и размеры фейков определяются целым запросом, а не отдельным пакетом.
|
||
|
||
---
|
||
|
||
## Автосегментация по MSS
|
||
|
||
![[desync-anim-hub-mss.svg|Анимация: кусок данных больше MSS автоматически режется на под-сегменты с растущим seq]]
|
||
|
||
О размерах TCP-сегментов думать **не нужно**. Функция `rawsend_payload_segmented` из `zapret-lib.lua` автоматически:
|
||
|
||
1. Отслеживает MSS для каждого TCP-соединения
|
||
2. Если часть payload превышает MSS — дополнительно режет по MSS
|
||
3. Каждый под-сегмент отправляется с корректным TCP sequence
|
||
|
||
> [!note] Проще говоря
|
||
> 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-функции]].
|
||
|
||
> [!note] Проще говоря
|
||
> Cutoff (отсечение) — «эта функция больше не вызывается»; вердикт — решение, что делать с перехваченным пакетом: `VERDICT_DROP` выбросить его, `VERDICT_PASS` пропустить. Как вердикты складываются между функциями, описано в заметке [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|Стадия 8. Вердикт и его агрегация]].
|
||
|
||
---
|
||
|
||
## Псевдокод алгоритма
|
||
|
||
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл desync-функции]]:
|
||
|
||
Читать код удобнее вместе с таблицей из раздела [[#Место в общем скелете|Место в общем скелете]] и схемой из раздела [[#Порядок отправки сегментов (6 пакетов)|Порядок отправки сегментов (6 пакетов)]]: комментарии в коде отмечают, где кончается общая часть и начинаются шесть отправок.
|
||
|
||
```lua
|
||
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).
|
||
|
||
> [!note] Проще говоря
|
||
> Instance cutoff — отключение функции для этого потока до его конца. Почему техника работает только с TCP, разобрано в нюансе [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|«Работает только с TCP — и почему»]] статьи про multisplit; о самой стадии отсева — в разделе [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|Стадия 1]].
|
||
|
||
### 2. Только ОДИН маркер в pos
|
||
|
||
В отличие от [[multisplit]] и [[multidisorder]], `fakeddisorder` принимает **один** маркер, а не список. `pos=midsld,endhost` не будет работать как два разреза. Если нужно несколько разрезов с фейками, используйте цепочку инстансов (хотя это нетривиально из-за VERDICT_DROP).
|
||
|
||
> [!note] Проще говоря
|
||
> Цепочка инстансов нетривиальна из-за `VERDICT_DROP`: по нюансу 11 инстанс, стоящий после `fakeddisorder`, увидит `VERDICT_DROP` и не получит оригинальный payload ([[#11. Порядок инстансов важен|нюанс 11]]). Про списки маркеров у соседей — [[Zapret2/desync/multisplit#Маркеры позиций (pos)|Маркеры позиций (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-параметр:
|
||
|
||
```bash
|
||
# ПЛОХО: фейки без fooling — сервер их примет!
|
||
--lua-desync=fakeddisorder:pos=midsld
|
||
|
||
# ХОРОШО: фейки с badseq
|
||
--lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
> [!note] Проще говоря
|
||
> Фейк без порчи заголовка сервер не отличит от настоящих данных и примет. Как фейкам портят заголовки, разобрано в разделе [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]].
|
||
|
||
### 5. tcp_ts_up рекомендуется с tcp_ack
|
||
|
||
На Linux-серверах пакеты с инвалидным ACK стабильно отбрасываются **только если** TCP timestamp option идёт первой в заголовке. Без `tcp_ts_up` есть риск, что сервер примет фейк с `tcp_ack=-66000`.
|
||
|
||
> [!note] Проще говоря
|
||
> Инвалидный ACK — номер подтверждения, который не подходит серверу; именно его портит `tcp_ack=-66000`. Опция timestamp и её порядок в заголовке описаны в разделе [[Zapret2/ts-and-fooling#Что такое TCP options (и зачем fooling с ними играет)|Что такое TCP options]].
|
||
|
||
### 6. ipfrag НЕ задействуется
|
||
|
||
В отличие от [[multisplit]] и [[multidisorder]], `fakeddisorder` не поддерживает IP-фрагментацию. Объект `ipfrag` установлен в `{}` и для фейков, и для оригиналов. Если нужна IP-фрагментация — используйте `multidisorder` (без фейков, но с ipfrag).
|
||
|
||
> [!note] Проще говоря
|
||
> IP-фрагментация — деление пакета на части на уровне IP, ниже TCP. Здесь её нет ни для фейков, ни для настоящих сегментов; функцию, которая её поддерживает, описывает раздел [[Zapret2/desync/multisplit#F) Standard ipfrag|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.
|
||
|
||
> [!note] Проще говоря
|
||
> Real part 2 — вторая часть запроса, которая при обратном порядке уходит первой из настоящих; поэтому `seqovl` достаётся именно ей.
|
||
|
||
### 9. nodrop создаёт дублирование
|
||
|
||
С `nodrop` fakeddisorder отправляет 6 нарезанных пакетов И пропускает оригинальный. Сервер получит данные дважды. Используйте `nodrop` только для отладки.
|
||
|
||
> [!note] Проще говоря
|
||
> Без `nodrop` оригинальный пакет выбрасывается, и сервер получает данные один раз; с `nodrop` уходят и оригинал, и все шесть пакетов, поэтому флаг годится только для отладки.
|
||
|
||
### 10. Ошибка rawsend прерывает цепочку
|
||
|
||
Если любой из 6 вызовов `rawsend_payload_segmented` возвращает `false`, fakeddisorder немедленно возвращает `VERDICT_PASS` (оригинальный пакет пропускается). Остальные пакеты из 6 не отправляются, `replay_drop_set` не вызывается.
|
||
|
||
> [!note] Проще говоря
|
||
> Если хотя бы одна отправка не удалась, остальные не выполняются, а оригинальный пакет пропускается как есть.
|
||
|
||
### 11. Порядок инстансов важен
|
||
|
||
Если перед `fakeddisorder` стоит `fake` — он отправит свой фейк до того, как fakeddisorder начнёт свою последовательность. Если после fakeddisorder стоит ещё один инстанс — он увидит VERDICT_DROP и не получит оригинальный payload.
|
||
|
||
> [!note] Проще говоря
|
||
> Как показывает пример с `fake` перед `fakeddisorder`, инстансы отрабатывают по очереди, в порядке записи в профиле. Про такой же нюанс у самого `fake` — [[Zapret2/desync/fake#8. Порядок инстансов: fake всегда первый|8. Порядок инстансов: fake всегда первый]].
|
||
|
||
---
|
||
|
||
## Отличия от fakedsplit, multisplit и multidisorder
|
||
|
||
Таблица сравнивает четыре функции по строкам; жирным выделено то, что относится к `fakeddisorder`. Отличия от других функций сегментации со стороны соседей разобраны в разделах [[Zapret2/desync/multisplit#Отличия от других функций сегментации|multisplit]], [[Zapret2/desync/multidisorder#Отличия от других функций сегментации|multidisorder]] и [[Zapret2/desync/fakedsplit#Отличия от других функций сегментации|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; для конкретного провайдера выбор всё равно проверяют на деле по [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листу честной проверки]]. Для нескольких позиций разреза с фейками остаётся цепочка инстансов ([[#2. Только ОДИН маркер в pos|нюанс 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` в инстансе |
|
||
|
||
### Пример полной миграции
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
```bash
|
||
# 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
|
||
```
|
||
|
||
```bash
|
||
# 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 примеров: от минимального до боевых. Каждая команда — строка для [[Zapret2/profile#Анатомия профиля|профиля]]; подробности про аргументы `pos`, `seqovl`, `pattern`, `nofake1`–`nofake4` — в разделах [[#Маркер позиции (pos)|Маркер позиции (pos)]], [[#seqovl — скрытый фейк внутри реального сегмента|seqovl]] и [[#Полный список аргументов|Полный список аргументов]].
|
||
|
||
### 1. Минимальный (с fooling)
|
||
|
||
```bash
|
||
--lua-desync=fakeddisorder:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
Разрезает payload после 2-го байта (`pos=2` по умолчанию) с badseq fooling для фейков. 6 пакетов.
|
||
|
||
### 2. TLS: разрез посередине SNI
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
SNI разрезан пополам. DPI видит 6 пакетов с перемешанными фейковыми и реальными сегментами в обратном порядке — двойное запутывание.
|
||
|
||
### 3. TLS: разрез + seqovl (тройное запутывание)
|
||
|
||
```bash
|
||
--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 как маркер
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:seqovl=sld:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
seqovl разрешается в позицию начала SLD. Фейковая часть слева от real part 2 — от начала SLD до позиции разреза.
|
||
|
||
### 5. HTTP: разрез в начале hostname
|
||
|
||
```bash
|
||
--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: кастомный паттерн для фейков
|
||
|
||
```bash
|
||
--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
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_md5:tcp_ts_up
|
||
```
|
||
|
||
Вместо badseq используется TCP MD5 signature option. Сервер отбрасывает пакеты с неверной MD5 подписью.
|
||
|
||
### 8. С badsum reconstruct
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:badsum
|
||
```
|
||
|
||
Фейки отправляются с испорченным TCP checksum. Двойная защита: и badseq, и badsum.
|
||
|
||
### 9. Без первого и последнего фейка (4 пакета)
|
||
|
||
```bash
|
||
--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 пакета)
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:tcp_ack=-66000:tcp_ts_up:nofake1:nofake2
|
||
```
|
||
|
||
Вся часть 2 — только реальная (без обрамления фейками). Часть 1 — с фейками. Полезно, когда DPI анализирует только начало потока.
|
||
|
||
### 11. Без всех фейков (эквивалент disorder)
|
||
|
||
```bash
|
||
--payload=tls_client_hello --lua-desync=fakeddisorder:pos=midsld:nofake1:nofake2:nofake3:nofake4
|
||
```
|
||
|
||
Отключены все 4 фейка. Остаются только 2 реальных пакета в обратном порядке. Фактически — [[multidisorder]] с одной позицией. Fooling не нужен (применяется только к фейкам, которых нет).
|
||
|
||
### 12. Цепочка: fake + fakeddisorder
|
||
|
||
```bash
|
||
--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
|
||
|
||
```bash
|
||
--blob=mydata:@custom_payload.bin \
|
||
--lua-desync=fakeddisorder:blob=mydata:pos=50:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
Режет и отправляет произвольные данные из файла вместо реального payload.
|
||
|
||
### 14. С optional для необязательного blob
|
||
|
||
```bash
|
||
--lua-desync=fakeddisorder:blob=maybe_missing:optional:pos=midsld:tcp_ack=-66000:tcp_ts_up
|
||
```
|
||
|
||
Если blob `maybe_missing` не существует — тихий пропуск, без ошибок.
|
||
|
||
### 15. Боевой пример для YouTube
|
||
|
||
```bash
|
||
--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 для фейков
|
||
|
||
```bash
|
||
--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
|
||
|
||
```bash
|
||
--payload=tls_client_hello \
|
||
--lua-desync=fakeddisorder:pos=midsld:ip6_hopbyhop:tcp_ts_up
|
||
```
|
||
|
||
Для IPv6: фейки отправляются с extension header hop-by-hop, который сбрасывается на первом маршрутизаторе. Реальные сегменты — без hop-by-hop.
|
||
|
||
### 18. Отладка: не блокировать оригинал
|
||
|
||
```bash
|
||
--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` тоже маркер, разобранный в разделе [[Zapret2/desync/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/DPI|DPI и ТСПУ]] — как устроена инспекция трафика, против которой работает `fakeddisorder`
|
||
|
||
---
|
||
|
||
> **Источники:** `lua/zapret-antidpi.lua:908-1021`, `docs/manual.md:4165-4206`, `docs/manual.en.md:3984-4025` из репозитория zapret2.
|
||
|
||
---
|
||
|
||
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
|
||
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/desync/fakeddisorder.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).
|