todo/Zapret2/desync/multidisorder.md

1082 lines
65 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
date: 2026-02-17
tags:
- zapret
- zapret2
- nfqws2
- lua
- lua-desync
- antidpi
- tcp
- multidisorder
- seqovl
- disorder
aliases:
- multidisorder
---
# `multidisorder` — TCP-сегментация в обратном порядке (zapret2 / nfqws2)
**Файл:** `lua/zapret-antidpi.lua:597`
**nfqws1 эквивалент:** `--dpi-desync=multidisorder`
**Сигнатура:** `function multidisorder(ctx, desync)`
`multidisorder` — функция TCP-сегментации в zapret2, которая берёт текущий payload (или reasm, или blob), разрезает его на несколько TCP-сегментов по заданным позициям и отправляет их в **обратном порядке** (от последнего к первому). Обратный порядок (disorder) эксплуатирует поведение TCP-стека: при получении внеочередных сегментов стек буферизирует их и собирает поток, а DPI зачастую не справляется с реассемблированием разупорядоченных данных. После успешной отправки выносит `VERDICT_DROP`, чтобы оригинальный пакет не ушёл.
Родственные функции: [[multisplit]] (прямой порядок), [[fakedsplit]] (с фейками), [[fakeddisorder]] (фейки + обратный порядок), [[hostfakesplit]] (по hostname), [[tcpseg]] (диапазон), [[oob]] (urgent byte), [[multidisorder_legacy]] (совместимый с nfqws1 алгоритм).
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `multidisorder` от этого скелета отличается.
---
## Оглавление
- [Зачем нужен multidisorder](#зачем-нужен-multidisorder)
- [Быстрый старт](#быстрый-старт)
- [Откуда берутся данные для нарезки](#откуда-берутся-данные-для-нарезки)
- [Маркеры позиций (pos)](#маркеры-позиций-pos)
- [Типы маркеров](#типы-маркеров)
- [Относительные маркеры](#относительные-маркеры)
- [Арифметика маркеров](#арифметика-маркеров)
- [Как маркеры разрешаются в коде](#как-маркеры-разрешаются-в-коде)
- [Важные нюансы pos](#важные-нюансы-pos)
- [Обратный порядок отправки — суть disorder](#обратный-порядок-отправки--суть-disorder)
- [Почему disorder ломает DPI](#почему-disorder-ломает-dpi)
- [Ограничение: Windows-серверы](#ограничение-windows-серверы)
- [seqovl — перезапись буфера сокета через перекрытие](#seqovl--перезапись-буфера-сокета-через-перекрытие)
- [Принцип работы seqovl в disorder](#принцип-работы-seqovl-в-disorder)
- [Отличие seqovl от multisplit](#отличие-seqovl-от-multisplit)
- [seqovl как маркер](#seqovl-как-маркер)
- [Валидация seqovl: должен быть меньше pos 1](#валидация-seqovl-должен-быть-меньше-pos-1)
- [Нюанс реализации: ovl = seqovl - 1](#нюанс-реализации-ovl--seqovl---1)
- [seqovl_pattern](#seqovl_pattern)
- [Полный список аргументов](#полный-список-аргументов)
- [A) Собственные аргументы multidisorder](#a-собственные-аргументы-multidisorder)
- [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 ipfrag](#f-standard-ipfrag)
- [G) Standard reconstruct](#g-standard-reconstruct)
- [H) Standard rawsend](#h-standard-rawsend)
- [Порядок отправки сегментов](#порядок-отправки-сегментов)
- [Поведение при replay / reasm](#поведение-при-replay--reasm)
- [Автосегментация по MSS](#автосегментация-по-mss)
- [Место в общем скелете](#место-в-общем-скелете)
- [Псевдокод алгоритма](#псевдокод-алгоритма)
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
- [Отличия от других функций сегментации](#отличия-от-других-функций-сегментации)
- [Миграция с nfqws1](#миграция-с-nfqws1)
- [Практические примеры](#практические-примеры)
---
## Зачем нужен multidisorder
DPI анализирует TCP-поток, пытаясь собрать полный payload и найти в нём сигнатуры (hostname в HTTP, SNI в TLS). Разрезание payload на сегменты уже усложняет работу DPI ([[multisplit]]), но **обратный порядок** отправки создаёт дополнительную проблему:
1. **DPI не реассемблирует out-of-order сегменты:** многие DPI работают потоково (stream-based) и ожидают сегменты в порядке sequence number. Получив сначала хвост потока, DPI может потерять контекст или не дождаться головы
2. **DPI не может сопоставить hostname:** если разрез проходит через SNI/Host, а сегменты приходят задом наперёд, ни попакетный, ни потоковый DPI не найдёт сигнатуру
3. **seqovl в режиме disorder перезаписывает буфер:** в отличие от [[multisplit]], где seqovl работает через выход за TCP window, в disorder фейковые данные сначала попадают в буфер сокета, а затем **перезаписываются** реальными данными из последнего сегмента
Сервер при этом корректно собирает поток — TCP-стек буферизирует внеочередные сегменты и отдаёт данные приложению только после сборки непрерывной последовательности.
**multidisorder** vs **multisplit:** disorder работает за счёт разупорядочивания, а не за счёт линейного дробления. Для замешивания фейковых сегментов (отдельные пакеты с fooling) есть [[fakeddisorder]].
---
## Быстрый старт
Минимально (разрез по позиции 2, payload=known, dir=out):
```bash
--lua-desync=multidisorder
```
Типовой TLS-разрез:
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=midsld
```
TLS с seqovl (маркер):
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=midsld:seqovl=midsld-1
```
HTTP с разрезом по hostname:
```bash
--payload=http_req --lua-desync=multidisorder:pos=host,midsld,endhost
```
---
## Откуда берутся данные для нарезки
Внутри `multidisorder` данные (`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 может распознать.
---
## Маркеры позиций (pos)
`pos` — главный аргумент `multidisorder`. Определяет **где** внутри payload будет произведён разрез. Задаётся как строка со списком маркеров через запятую.
### Типы маркеров
| Тип | Описание | Пример |
|:----|:---------|:-------|
| **Абсолютный положительный** | Смещение от начала payload, считая с 0. `pos=N` → первые N байт становятся отдельным сегментом | `1`, `5`, `100` |
| **Абсолютный отрицательный** | Смещение от конца payload. `-1` = последний байт | `-1`, `-10`, `-50` |
| **Относительный** | Логическая позиция внутри распознанного payload. Привязана к структуре протокола | `midsld`, `host`, `sniext` |
### Относительные маркеры
| Маркер | Описание | Для каких 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 — два байта после начала метода (разрежет "GET " после "GE")
sniext+1 — один байт после начала SNI extension data
host+3 — три байта после начала hostname
-1 — последний байт payload (абсолютный, не относительный)
```
Арифметика работает и с абсолютными маркерами, хотя это избыточно (`5+3` = `8`).
### Пример списка маркеров
```
pos=100,midsld,sniext+1,endhost-2,-10
```
Здесь 5 маркеров -> payload разрежется максимум на 6 частей (если все маркеры успешно разрешатся и дадут различные позиции).
### Как маркеры разрешаются в коде
Внутри `multidisorder` вызывается:
```lua
local pos = resolve_multi_pos(data, desync.l7payload, spos)
```
Функция `resolve_multi_pos`:
1. Разбивает строку `spos` по запятым
2. Для каждого маркера вызывает `resolve_pos(blob, l7payload_type, marker)`
3. Если маркер не может быть разрешён (например, `midsld` для `unknown` payload) — он **молча пропускается**
4. Результаты дедуплицируются и сортируются
5. Возвращается массив **уникальных** позиций, уже переведённых в 1-based индексы Lua (маркер `1` превращается в позицию `2`, маркер `0` — в позицию `1`)
Затем вызывается:
```lua
delete_pos_1(pos) -- удалить Lua-позицию 1, то есть маркер 0
```
### Важные нюансы pos
- **Разрез в самом начале данных отбрасывается.** `delete_pos_1` удаляет Lua-позицию 1, которая соответствует маркеру `0`: первый сегмент вышел бы пустым. Считать маркеры нужно с нуля, поэтому под удаление попадает `pos=0`, а `pos=1` (первый байт отдельным сегментом) работает и применяется часто. Частая жертва этого правила — `pos=method` для HTTP: метод обычно лежит на смещении 0, разреза не произойдёт; нужен сдвиг вроде `pos=method+2`
- **Дублирующиеся позиции объединяются.** `pos=5,5,5` = `pos=5`
- **Неразрешимые маркеры пропускаются.** Если `midsld` не разрешается (payload = unknown), он просто исчезает из списка. Если все маркеры не разрешились — multidisorder ничего не делает (логирует "no valid split positions")
- **Позиции сортируются.** Независимо от порядка записи, `pos=100,5,50` будет обработано как `5,50,100`
- **По умолчанию `pos=2`.** Если `pos` не задан, payload делится на 2 части: первые 2 байта отдельным сегментом, весь остаток — вторым
---
## Обратный порядок отправки — суть disorder
Ключевое отличие `multidisorder` от [[multisplit]]: сегменты отправляются **от последнего к первому** (в порядке убывания sequence number). Цикл в `multidisorder_send`:
```lua
for i=#pos,0,-1 do
```
```
multisplit: отправка 1 -> 2 -> 3 -> 4 (прямой порядок)
multidisorder: отправка 4 -> 3 -> 2 -> 1 (обратный порядок)
```
### Почему disorder ломает DPI
TCP-стек сервера спроектирован для работы с внеочередными сегментами — он буферизирует их и ждёт недостающие части. DPI, как правило, не имеет такой роскоши:
1. **Потоковые DPI** обрабатывают данные по мере поступления. Получив последний сегмент первым, DPI видит данные без начала (без заголовков HTTP/TLS) и не может распознать протокол
2. **Попакетные DPI** анализируют каждый пакет отдельно. В каждом отдельном сегменте нет полной сигнатуры
3. **DPI с реассемблированием** теоретически могут собрать поток, но на практике многие имеют ограниченный буфер или timeout и не дожидаются всех частей
### Ограничение: Windows-серверы
Техника seqovl в режиме disorder **не работает с Windows-серверами**. При перекрытии sequence numbers:
- **Linux/BSD/macOS:** более поздний сегмент **перезаписывает** ранее буферизированные данные
- **Windows:** **сохраняет** первые полученные данные, игнорируя перекрывающиеся
Это означает, что seqovl_pattern, записанный в буфер предпоследним сегментом, на Windows **не будет перезаписан** реальными данными из последнего сегмента — сервер получит мусор.
**Без seqovl** multidisorder работает нормально на всех системах — простое разупорядочивание не зависит от поведения при перекрытии.
---
## seqovl — перезапись буфера сокета через перекрытие
В `multidisorder` техника seqovl работает **принципиально иначе**, чем в [[multisplit]]. Здесь seqovl эксплуатирует переписывание данных в буфере сокета при получении перекрывающихся сегментов.
### Принцип работы seqovl в disorder
Рассмотрим payload из 2 частей: `pos=100` (одна точка разреза).
```
Payload (300 байт): [ЧАСТЬ_1: 100 байт][ЧАСТЬ_2: 200 байт]
^pos=100
seqovl=midsld (разрешился в 50), ovl = 50 - 1 = 49
Порядок отправки (обратный, i от #pos до 0):
Шаг 1 (i=1): ЧАСТЬ_2 с seqovl!
К ЧАСТЬ_2 слева приписывается 49 байт PATTERN
part = [PATTERN(49)][ЧАСТЬ_2(200)]
seq = pos_start - 1 - ovl = 100 - 1 - 49 = 50
len = 249
Отправляется первым
Шаг 2 (i=0): ЧАСТЬ_1 без seqovl
part = [ЧАСТЬ_1(100)]
seq = 0
len = 100
Отправляется последним
```
**Что происходит на сервере (Linux/BSD):**
```
t1: приходит [PATTERN(49)][ЧАСТЬ_2(200)] seq=50 len=249
Буфер сокета:
позиции: 0 50 100 300
[ пусто ][PATTERN(49)][ЧАСТЬ_2(200)........]
^--- PATTERN занимает позиции 50..98
^--- ЧАСТЬ_2 начинается с позиции 99
Данные НЕ выдаются приложению — нет непрерывной последовательности от 0
t2: приходит [ЧАСТЬ_1(100)] seq=0 len=100
Буфер сокета (Linux/BSD — позднее ПЕРЕЗАПИСЫВАЕТ):
позиции: 0 100 300
АСТЬ_1(100)....................][ЧАСТЬ_2(200)........]
^--- ЧАСТЬ_1 занимает позиции 0..99
PATTERN ПЕРЕЗАПИСАН реальными данными!
Непрерывная последовательность 0..299 -> выдаётся приложению корректно
```
**На Windows-сервере:**
```
t2: приходит [ЧАСТЬ_1(100)] seq=0 len=100
Буфер сокета (Windows — СОХРАНЯЕТ старое):
позиции: 0 50 100 300
АСТЬ_1(50)][PATTERN(49)][ЧАСТЬ_2(200)........]
^--- только байты 0..49 из ЧАСТЬ_1 записались
^--- байты 50..98 остались от PATTERN (мусор!)
Приложение получает повреждённые данные!
```
### Отличие seqovl от multisplit
| Аспект | seqovl в multisplit | seqovl в multidisorder |
|:-------|:--------------------|:-----------------------|
| Механизм | Выход за левую границу TCP window | **Перезапись буфера** перекрывающимся сегментом |
| К какому сегменту | 1-й отправляемый (1-й в оригинале, `i==0`) | **Предпоследний** отправляемый (2-й в оригинале, `i==1`) |
| Тип значения | Только число | **Маркер** (число, имя маркера, маркер+арифметика) |
| Кто отбрасывает фейк | TCP-стек (данные за пределами window) | **Перезапись** следующим сегментом (последним отправляемым) |
| Работает на Windows | Да (TCP window — универсальный механизм) | **Нет** (Windows не перезаписывает буфер) |
### seqovl как маркер
В отличие от [[multisplit]], где `seqovl` принимает только число, в `multidisorder` seqovl разрешается через `resolve_pos()` — полноценный маркер:
```lua
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
```
Это означает, что поддерживаются:
```
seqovl=5 — число (абсолютная позиция, как в multisplit)
seqovl=midsld-1 — маркер с арифметикой (типичное использование)
seqovl=host+2 — маркер с арифметикой
seqovl=sld — маркер без арифметики
seqovl=-10 — отрицательная позиция (от конца данных)
```
**Типичный паттерн:** `pos=midsld` + `seqovl=midsld-1`. Разрез по середине SLD, а seqovl перекрывает почти всю первую часть.
**Если маркер не резолвится** (например, `seqovl=midsld-1` для unknown payload) — seqovl отменяется, но сегментация в обратном порядке всё равно происходит. В лог записывается "seqovl cancelled because could not resolve marker".
### Валидация seqovl: должен быть меньше pos[1]
Из `multidisorder_send`:
```lua
if seqovl>=pos[1] then
DLOG("multidisorder: seqovl cancelled because seqovl "..
(seqovl-1).." is not less than the first split pos "..(pos[1]-1))
```
seqovl (разрешённое значение маркера) **обязательно должен быть строго меньше** `pos[1]` (первой позиции разреза). Если это условие не выполнено — seqovl отменяется (но сегментация всё равно происходит, просто без перекрытия).
**Почему:** seqovl приписывается к сегменту `i==1`АСТЬ_2, от `pos[1]` до конца или до `pos[2]-1`). Перекрытие заползает назад на `ovl` байт. Если seqovl >= pos[1], перекрытие вылезло бы за начало данных первого сегмента, что не имеет смысла.
Пример:
```
pos=10,50 -> pos[1]=10
seqovl=midsld -> разрешился в 8 -> OK (8 < 10)
seqovl=midsld -> разрешился в 10 -> CANCELLED (10 >= 10, не строго меньше)
seqovl=midsld -> разрешился в 15 -> CANCELLED (15 >= 10)
```
### Нюанс реализации: ovl = seqovl - 1
В коде `multidisorder_send` есть важная деталь:
```lua
ovl = seqovl - 1
```
Реальный размер перекрытия на 1 байт **меньше** разрешённого значения маркера. Это связано с тем, что `seqovl` возвращается как позиция (1-based в Lua), а `ovl` используется как размер смещения (0-based). Для пользователя это означает:
```
seqovl=midsld (разрешился, скажем, в 50)
-> ovl = 49
-> К сегменту приписывается 49 байт pattern слева
-> TCP seq уменьшается на 49
```
**Следствие:** если seqovl разрешится в 1, то `ovl = 0` — seqovl фактически не применяется. Минимальный эффективный seqovl — значение 2 (ovl = 1, 1 байт перекрытия).
### seqovl_pattern
Паттерн, которым заполняется seqovl-область (ovl байт слева от реальных данных). По умолчанию — `0x00` (нули).
В `multidisorder` `seqovl_pattern` — это **имя blob**. Паттерн повторяется функцией `pattern()` до нужной длины `ovl` (= seqovl - 1).
```bash
# Inline hex blob (маскировка под начало TLS record)
--lua-desync=multidisorder:pos=midsld:seqovl=midsld-1:seqovl_pattern=0x1603030000
# Предзагруженный blob
--blob=tlspat:0x1603030100 \
--lua-desync=multidisorder:pos=midsld:seqovl=midsld-1:seqovl_pattern=tlspat
```
Если `optional` задан и blob `seqovl_pattern` отсутствует — используется нулевой паттерн (операция не отменяется).
---
## Полный список аргументов
Формат вызова:
```
--lua-desync=multidisorder[:arg1[=val1][:arg2[=val2]]...]
```
Все `val` приходят в Lua как строки. Если `=val` не указан, значение = пустая строка `""` (в Lua это truthy), поэтому флаги пишутся просто как `:optional`, `:nodrop`, `:tcp_ts_up`.
### A) Собственные аргументы multidisorder
#### `pos`
- **Формат:** `pos=<marker[,marker2,...]>`
- **Тип:** строка со списком маркеров через запятую
- **По умолчанию:** `"2"`
- **Описание:** Точки разреза. Каждый маркер определяет позицию, по которой payload будет разрезан. N маркеров -> до N+1 сегментов. Сегменты отправляются в **обратном** порядке
- **Примеры:**
- `pos=2` — разрез после 2-го байта (дефолт)
- `pos=1` — первый байт уходит отдельным сегментом
- `pos=midsld` — разрез посередине SLD
- `pos=1,midsld` — два разреза: после 1-го байта и посередине SLD -> 3 сегмента
- `pos=host,midsld,endhost-2,-10` — четыре разреза -> до 5 сегментов
- `pos=method+2` — после первых 2 символов HTTP-метода
#### `seqovl`
- **Формат:** `seqovl=<marker>` (маркер, число, или маркер+арифметика)
- **Тип:** **маркер** (в отличие от multisplit, где только число). Разрешается через `resolve_pos()`
- **По умолчанию:** не задан (нет seqovl)
- **Описание:** Применяется к сегменту `i==1` (2-й в оригинальном порядке, предпоследний отправляемый). К данным этого сегмента слева добавляется `seqovl-1` байт `seqovl_pattern`, а TCP `th_seq` уменьшается на `seqovl-1`. Последний отправляемый сегмент (1-й в оригинале) перезаписывает фейковые данные в буфере сервера
- **Ограничение:** разрешённое значение seqovl **должно быть строго меньше** pos[1], иначе seqovl отменяется
- **Примеры:**
- `seqovl=5` — 4 байта фейка слева (ovl = 5-1)
- `seqovl=midsld-1` — типичное использование с `pos=midsld`
- `seqovl=host+2` — маркер с арифметикой
- `seqovl=sld` — маркер без арифметики
#### `seqovl_pattern`
- **Формат:** `seqovl_pattern=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** один байт `0x00`, повторяемый до длины `ovl` (= seqovl - 1)
- **Описание:** Данные для заполнения seqovl-области. Blob повторяется функцией `pattern()` до нужного размера
- **Поведение с `optional`:** если `optional` задан и blob отсутствует — используется нулевой паттерн, seqovl не отменяется
- **Примеры:**
- `seqovl_pattern=0x1603030000` — inline hex (маскировка под TLS)
- `seqovl_pattern=my_pattern_blob` — предзагруженный blob
#### `blob`
- **Формат:** `blob=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** не задан
- **Описание:** Заменить текущий payload/reasm на указанный blob и резать/слать его. Используется для отправки произвольных данных (фейковых payload, модифицированных ClientHello и т.д.)
- **Примеры:**
- `blob=fake_default_tls` — стандартный TLS-фейк
- `blob=0xDEADBEEF` — inline hex
- `blob=my_custom_ch` — предзагруженный blob
#### `optional`
- **Формат:** `optional` (флаг, без значения)
- **Описание:** Мягкий режим:
- Если задан `blob=...` и blob отсутствует -> multidisorder **ничего не делает** (тихий skip, без ошибок)
- Если задан `seqovl_pattern=...` и blob отсутствует -> используется нулевой паттерн (seqovl не отменяется)
- **Использование:** защита от ошибок при использовании blob, которые могут отсутствовать (например, если blob генерируется другой функцией)
#### `nodrop`
- **Формат:** `nodrop` (флаг, без значения)
- **Описание:** После успешной отправки сегментов **не выносить** `VERDICT_DROP` (вместо этого вернуть `VERDICT_PASS`). Это означает, что оригинальный пакет тоже будет отправлен (наряду с нарезанными сегментами)
- **Использование:** для отладки, для отправки произвольных данных без блокировки оригинала
- **Предупреждение:** в боевых профилях `nodrop` обычно нежелателен — оригинал ещё раз уйдёт, что создаст дублирование и может ухудшить обход
---
### B) Standard direction
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |
Фильтр по направлению пакета. `multidisorder` по умолчанию работает только с исходящими (`out`).
- `dir=out` — только исходящие (от клиента к серверу)
- `dir=in` — только входящие (от сервера к клиенту)
- `dir=any`оба направления
При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` — отсекает себя от противоположного направления.
---
### 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-фильтр.
---
### D) Standard fooling
Модификации L3/L4 заголовков. В `multidisorder` применяются **ко всем** отправляемым сегментам (в отличие от [[fakeddisorder]], где fooling идёт только на фейки).
| Параметр | Описание | Пример |
|:---------|:---------|:-------|
| `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:** На Linux-серверах пакеты с инвалидным ACK стабильно отбрасываются **только если** TCP timestamp option идёт первой в заголовке. `tcp_ts_up` перемещает её в начало, обеспечивая корректную работу badseq-fooling.
---
### 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` применяется к **каждому** отправляемому сегменту (включая под-сегменты при MSS-сегментации).
---
### F) Standard ipfrag
IP-фрагментация **поверх** TCP-сегментации. Каждый TCP-сегмент дополнительно фрагментируется на уровне IP.
| Параметр | Описание | По умолчанию |
|:---------|:---------|:-------------|
| `ipfrag[=func]` | Включить IP-фрагментацию. Если без значения -> `ipfrag2` | — |
| `ipfrag_disorder` | Отправить IP-фрагменты в обратном порядке | — |
| `ipfrag_pos_tcp=N` | Позиция фрагментации TCP (кратно 8) | `32` |
| `ipfrag_pos_udp=N` | Позиция фрагментации UDP (кратно 8). Для multidisorder бесполезно — он только TCP | `8` |
| `ipfrag_next=N` | IPv6: next protocol во 2-м фрагменте (penetration атака на фаерволы) | — |
---
### G) Standard reconstruct
| Параметр | Описание |
|:---------|:---------|
| `badsum` | Испортить L4 (TCP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет |
---
### H) Standard rawsend
| Параметр | Описание |
|:---------|:---------|
| `repeats=N` | Отправить каждый сегмент N раз (идентичные повторы) |
| `ifout=<iface>` | Интерфейс для отправки (по умолчанию определяется автоматически) |
| `fwmark=N` | Firewall mark (только Linux, nftables/iptables) |
---
## Порядок отправки сегментов
`multidisorder` всегда отправляет сегменты в **обратном порядке** — от последнего к первому (в порядке убывания TCP sequence). Цикл в `multidisorder_send`: `for i=#pos,0,-1`.
### Пример с 3 позициями разреза (без seqovl)
```
Payload (600 байт):
[AAA...100 байт...AAA][BBB...200 байт...BBB][CCC...150 байт...CCC][DDD...150 байт...DDD]
^pos=100 ^pos=300 ^pos=450
Оригинальный порядок Порядок отправки
------------------- ----------------
Сегмент 0 (i=0): [AAA...100] seq=0 4-й (последний)
Сегмент 1 (i=1): [BBB...200] seq=100 3-й
Сегмент 2 (i=2): [CCC...150] seq=300 2-й
Сегмент 3 (i=3): [DDD...150] seq=450 1-й (первым отправляется)
Реальная последовательность отправки:
1. [DDD...150] seq=450 len=150 (i=3, последний сегмент данных)
2. [CCC...150] seq=300 len=150 (i=2)
3. [BBB...200] seq=100 len=200 (i=1)
4. [AAA...100] seq=0 len=100 (i=0, первый сегмент данных)
```
### Пример с seqovl (2 сегмента)
```
Payload (300 байт), pos=100, seqovl=midsld (разрешился в 50):
ovl = 50 - 1 = 49
Оригинальный порядок Порядок отправки
------------------- ----------------
Сегмент 0 (i=0): [ЧАСТЬ_1: 100 байт] seq=0 2-й (последний)
Сегмент 1 (i=1): [ЧАСТЬ_2: 200 байт] seq=100 1-й (первым)
Реальная последовательность отправки:
1. i=1: [PATTERN(49)][ЧАСТЬ_2(200)] seq=100-1-49=50 len=249 (seqovl!)
2. i=0: [ЧАСТЬ_1(100)] seq=0 len=100 (перезаписывает PATTERN)
```
### ASCII-диаграмма: хронология буфера сокета на сервере
```
Время -->
t1: приходит [PATTERN(49)][ЧАСТЬ_2(200)] seq=50 len=249
Буфер сокета:
позиция: 0 50 99 100 299
[ пусто ][PATRN(49)][?? ][ЧАСТЬ_2(200)..............]
^^^^^^^^^^^^--- PATTERN записан в позиции 50-98
^^^--- ЧАСТЬ_2 в позициях 99-298
Данные НЕ выдаются приложению (нет непрерывности от 0)
t2: приходит [ЧАСТЬ_1(100)] seq=0 len=100
Linux/BSD (поздний ПЕРЕЗАПИСЫВАЕТ):
позиция: 0 100 299
АСТЬ_1(100)............][ЧАСТЬ_2(200)..............]
^--- ЧАСТЬ_1 перезаписала позиции 0-99
PATTERN уничтожен, данные корректны!
Windows (СОХРАНЯЕТ раннее):
позиция: 0 50 99 100 299
АСТЬ_1(50)][PATRN(49)][?? ][ЧАСТЬ_2(200)..............]
^--- только позиции 0-49 обновились
позиции 50-98 остались от PATTERN = МУСОР
```
### Пример с 3 позициями + seqovl
```
Payload (600 байт), pos=100,300,450, seqovl=host (разрешился в 80):
ovl = 80 - 1 = 79
Порядок отправки:
1. i=3: [DDD...150] seq=450 len=150 (без seqovl)
2. i=2: [CCC...150] seq=300 len=150 (без seqovl)
3. i=1: [PATTERN(79)][BBB...200] seq=100-1-79=20 len=279 (seqovl! только i==1)
4. i=0: [AAA...100] seq=0 len=100 (перезаписывает PATTERN)
```
**Обратите внимание:** seqovl применяется **только** к `i==1` (2-й сегмент в оригинальном порядке, предпоследний в порядке отправки), независимо от количества позиций разреза.
---
## Поведение при replay / reasm
Многопакетный запрос (большой TLS ClientHello с post-quantum ключами и т. п.) `nfqws2` придерживает, собирает в буфер `reasm_data` и перепроигрывает через desync-функции. `multidisorder` использует штатную развилку перепроигрывания из общего скелета desync-функций ([[жизненный цикл desync-функции]], стадия 6) без изменений: нарезает и отправляет **весь** собранный буфер на первой части, а остальные части дропает, потому что их содержимое уже ушло в составе нарезки. Если отправка сорвалась, флаг «реасм уже отправлен» не ставится и оставшиеся части проходят как есть.
Специфика `multidisorder` — в следствии этого поведения. Раз режется весь реасм целиком, обратный порядок распространяется на весь запрос, а не на отдельные пакеты: последний сегмент всего ClientHello уйдёт первым, даже если исходно он приехал третьим пакетом.
**Отличие от [[multidisorder_legacy]]:** legacy-версия развилку не использует вовсе — она обрабатывает каждую часть replay отдельно, нормализуя позиции разреза под границы конкретного пакета, как это делал nfqws1. Из-за этого порядок сегментов при многопакетных запросах у двух функций разный. Для полной совместимости с nfqws1 нужен именно legacy-вариант.
---
## Автосегментация по MSS
О размерах TCP-сегментов думать **не нужно**. Отправкой занимается `rawsend_payload_segmented` из `zapret-lib.lua`, и она сама доводит куски до допустимого размера:
1. MSS берётся из `desync.tcp_mss` — движок отслеживает его для каждого TCP-соединения
2. Если кусок вместе с заголовками превышает MSS — он дополнительно режется по MSS
3. Каждый под-сегмент отправляется с корректным TCP sequence
**Пример:** если seqovl разрешился в большое значение (скажем, 5000), это не вызовет ошибку. `rawsend_payload_segmented` отправит несколько TCP-сегментов общим размером, равным seqovl_pattern + данные сегмента.
---
## Место в общем скелете
Тело `multidisorder` построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в [[жизненный цикл desync-функции]] — здесь только отклонения:
| Стадия общего скелета | Что делает `multidisorder` |
|:----------------------|:---------------------------|
| 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` |
Отличие от [[multisplit]] сосредоточено ровно в одной стадии — седьмой. Всё остальное у двух функций совпадает буквально построчно, вплоть до общего хелпера `multidisorder_send`, который и разворачивает порядок отправки.
---
## Псевдокод алгоритма
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл desync-функции]]:
```lua
function multidisorder(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. Разрешение маркеров позиций
pos = resolve_multi_pos(data, l7payload, pos_arg or "2")
delete_pos_1(pos) -- удалить Lua-позицию 1 (маркер 0)
if #pos > 0 then
-- 8. Разрешение seqovl (МАРКЕР, не число!)
seqovl = nil
if arg.seqovl then
seqovl = resolve_pos(data, l7payload, arg.seqovl)
if not seqovl then
DLOG("seqovl cancelled: marker not resolved")
end
end
-- 9. multidisorder_send: цикл ОБРАТНЫЙ
for i = #pos, 0, -1 do -- <--- от последнего к первому!
pos_start = pos[i] or 1
pos_end = (i < #pos) and pos[i+1]-1 or #data
part = data:sub(pos_start, pos_end)
-- 10. seqovl только для i==1 (предпоследний отправляемый)
ovl = 0
if i == 1 and seqovl and seqovl > 0 then
if seqovl >= pos[1] then
DLOG("seqovl cancelled: not less than first split pos")
else
ovl = seqovl - 1 -- ВАЖНО: минус 1!
pat = seqovl_pattern_blob or "\x00"
part = pattern(pat, 1, ovl) .. part
end
end
-- 11. Отправка с автосегментацией
rawsend_payload_segmented(part, pos_start - 1 - ovl)
end
-- 12. Пометить как отправленное
replay_drop_set()
return nodrop and VERDICT_PASS or VERDICT_DROP
else
DLOG("no valid split positions")
end
else
-- 13. Не первый replay
DLOG("not acting on further replay pieces")
end
-- 14. Дропнуть если ранее успешно отправлено
if replay_drop() then
return nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
```
---
## Нюансы и подводные камни
### 1. Работает только с TCP
Если текущий пакет не TCP (UDP, ICMP и т.д.), `multidisorder` делает `instance_cutoff_shim` — отключает себя для этого потока навсегда (кроме ICMP-ответов, для которых cutoff не вызывается).
### 2. Разрез в самом начале данных отбрасывается
`delete_pos_1(pos)` убирает из списка Lua-позицию 1 — она соответствует маркеру `0`, то есть разрезу перед первым байтом, от которого первый сегмент вышел бы пустым. Если после этой чистки не осталось ни одной позиции, `multidisorder` не делает ничего.
Путаться тут легко из-за разной нумерации: в командной строке маркеры считаются от нуля, внутри Lua — от единицы. Поэтому под удаление попадает не `pos=1`, а `pos=0`. Самая частая реальная жертва — `pos=method` для HTTP: метод обычно лежит на смещении 0, маркер разрешается в удаляемую позицию, и разреза не происходит. Рабочий вариант — `pos=method+2`.
### 3. Все маркеры могут не разрешиться
Если вы указали `pos=midsld,sniext` для HTTP-payload, оба маркера (специфичные для TLS) не разрешатся. Multidisorder напишет в лог "no valid split positions" и ничего не сделает.
### 4. seqovl как маркер тоже может не разрешиться
В отличие от [[multisplit]] (где seqovl — число и всегда валидно), в multidisorder `seqovl=midsld-1` может не разрешиться (например, для unknown payload). В этом случае seqovl отменяется (логируется), но сегментация в обратном порядке всё равно происходит.
### 5. seqovl НЕ работает на Windows-серверах
Если целевой сервер работает под Windows, seqovl бесполезен: Windows сохраняет первые полученные данные и не перезаписывает буфер при получении перекрывающихся сегментов. Результат — сервер получит мусор (seqovl_pattern) вместо реальных данных. **Без seqovl** multidisorder работает на всех ОС нормально.
### 6. ovl = seqovl - 1: размер перекрытия на 1 меньше
Из-за строки `ovl = seqovl - 1` в `multidisorder_send`, если seqovl разрешился в 1, то ovl будет 0 — фактически seqovl не применяется. Минимальный эффективный seqovl — значение 2 (ovl = 1, 1 байт перекрытия).
### 7. seqovl >= pos[1] -> отмена seqovl
Если разрешённое значение seqovl больше или равно первой позиции разреза, seqovl отменяется. Это частая ошибка при использовании маркеров, которые разрешаются в большие значения. Логируется как "seqovl cancelled because seqovl N is not less than the first split pos M".
### 8. nodrop создаёт дублирование
С `nodrop` multidisorder отправляет нарезанные сегменты И пропускает оригинальный пакет. Сервер получит данные дважды. Используйте `nodrop` только для отладки или когда это осознанно нужно.
### 9. Fooling применяется ко ВСЕМ сегментам
В отличие от [[fakeddisorder]], где fooling идёт только на фейки, в `multidisorder` все сегменты получают fooling. Если задать `tcp_ack=-66000`, **все** сегменты получат инвалидный ack — сервер их отбросит, и ничего не заработает. Fooling в multidisorder имеет смысл только для специфических вещей (например, `tcp_ts_up`, `ip_id`, IPv6 extension headers).
### 10. Алгоритм отличается от nfqws1
Из комментария в коде: "algorithm is not 100% the same as in nfqws1. multi-segment queries can produce different segment ordering." nfqws2 работает с целым reasm, а nfqws1 — по отдельным частям replay. Для полной совместимости с nfqws1 используйте [[multidisorder_legacy]].
### 11. Порядок инстансов важен
Если перед `multidisorder` стоит `fake`, он отправит фейк первым. Если после multidisorder стоит ещё один инстанс — он увидит VERDICT_DROP и не получит оригинальный payload.
---
## Отличия от других функций сегментации
| Аспект | `multisplit` | `multidisorder` | `fakedsplit` | `fakeddisorder` |
|:-------|:-------------|:----------------|:-------------|:----------------|
| Количество позиций | Список (любое кол-во) | Список (любое кол-во) | **Одна** | **Одна** |
| Порядок отправки | Прямой (1->2->3) | **Обратный (3->2->1)** | Прямой | Обратный |
| Фейковые сегменты | **Нет** | **Нет** | Да (до 4 шт.) | Да (до 4 шт.) |
| seqovl тип | Только число | **Маркер** | Только число | **Маркер** |
| seqovl к какому сегменту | 1-й (первый отправляемый, `i==0`) | **2-й в оригинале (предпоследний отправляемый, `i==1`)** | 1-й реальный | 2-й реальный |
| seqovl механизм | Выход за TCP window | **Перезапись буфера** | Выход за TCP window | **Перезапись буфера** |
| Windows-серверы (seqovl) | Работает | **Не работает** | Работает | **Не работает** |
| Fooling к | Всем сегментам | Всем сегментам | Только к фейкам | Только к фейкам |
| ipfrag | Да | Да | **Нет** | **Нет** |
| blob аргумент | Да | Да | Нет | Нет |
| reasm | Целый reasm на replay_first | Целый reasm на replay_first | По частям | По частям |
---
## Миграция с nfqws1
### Соответствие параметров
| nfqws1 | nfqws2 |
|:-------|:-------|
| `--dpi-desync=multidisorder` | `--lua-desync=multidisorder` |
| `--dpi-desync-split-pos=midsld` | `:pos=midsld` |
| `--dpi-desync-split-pos=1,midsld` | `:pos=1,midsld` |
| `--dpi-desync-split-seqovl=5` | `:seqovl=5` |
| `--dpi-desync-split-seqovl-pattern=0x1603030000` | `:seqovl_pattern=0x1603030000` |
| `--dpi-desync-any-protocol` | Не нужно; или `payload=all` в инстансе |
### Простая миграция
```bash
# nfqws1:
nfqws --dpi-desync=multidisorder --dpi-desync-split-pos=midsld
# nfqws2:
nfqws2 --lua-desync=multidisorder:pos=midsld
```
### Миграция с fake + multidisorder
```bash
# nfqws1:
nfqws --dpi-desync=fake,multidisorder \
--dpi-desync-split-pos=1,midsld \
--dpi-desync-repeats=11 \
--dpi-desync-fooling=md5sig
# nfqws2:
nfqws2 \
--lua-desync=fake:blob=fake_default_tls:repeats=11:tcp_md5 \
--lua-desync=multidisorder:pos=1,midsld
```
### Миграция с seqovl
```bash
# nfqws1:
nfqws --dpi-desync=multidisorder \
--dpi-desync-split-pos=midsld \
--dpi-desync-split-seqovl=5 \
--dpi-desync-split-seqovl-pattern=0x1603030000
# nfqws2 (seqovl теперь может быть маркером!):
nfqws2 --lua-desync=multidisorder:pos=midsld:seqovl=5:seqovl_pattern=0x1603030000
# или с маркером (новая возможность nfqws2):
nfqws2 --lua-desync=multidisorder:pos=midsld:seqovl=midsld-1:seqovl_pattern=0x1603030000
```
### Полная миграция: fake + disorder + seqovl + fooling
```bash
# nfqws1:
nfqws --dpi-desync=fake,multidisorder \
--dpi-desync-fooling=md5sig \
--dpi-desync-split-pos=1,midsld \
--dpi-desync-split-seqovl=5 \
--dpi-desync-split-seqovl-pattern=0x1603030000 \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
# nfqws2 (эквивалент):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--payload=http_req \
--lua-desync=fake:blob=fake_default_http:tcp_md5 \
--payload=tls_client_hello,http_req \
--lua-desync=multidisorder:pos=1,midsld:seqovl=5:seqovl_pattern=0x1603030000
```
**Замечание о совместимости:** алгоритм multidisorder в nfqws2 **не на 100% совпадает** с nfqws1 при многопакетных запросах (multi-segment queries). nfqws2 работает с целым reasm, а nfqws1 — по частям. Для полной совместимости используйте [[multidisorder_legacy]].
---
## Практические примеры
### 1. Минимальный (дефолт: pos=2, dir=out, payload=known)
```bash
--lua-desync=multidisorder
```
Разрезает payload после 2-го байта -> 2 сегмента (первые 2 байта и остаток), отправляемых в обратном порядке.
### 2. TLS: разрез посередине SNI
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=midsld
```
SNI разрезан пополам, хвост отправлен первым — DPI не может собрать домен.
### 3. TLS: разрез + seqovl маркером (классическая комбинация)
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=midsld:seqovl=midsld-1
```
Разрез по midsld, seqovl перекрывает почти всю первую часть фейковыми данными. Типичный паттерн для disorder.
### 4. TLS: seqovl + кастомный паттерн
```bash
--payload=tls_client_hello \
--lua-desync=multidisorder:pos=midsld:seqovl=midsld-1:seqovl_pattern=0x1603030000
```
seqovl-область заполнена данными, похожими на начало TLS record — DPI может принять за легитимный TLS-трафик.
### 5. HTTP: разрез после метода (disorder)
```bash
--payload=http_req --lua-desync=multidisorder:pos=method+2
```
Для `GET /path...` разрежет после `GE` -> хвост уйдёт первым, голова — вторым.
### 6. HTTP: несколько разрезов вокруг hostname
```bash
--payload=http_req --lua-desync=multidisorder:pos=host,midsld,endhost
```
Разрезает: до hostname | первая половина | вторая половина | после hostname -> 4 сегмента в обратном порядке.
### 7. Два разреза с seqovl
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=2,midsld:seqovl=sld
```
3 сегмента, seqovl применяется к сегменту i=1 (второй в оригинале). Маркер `sld` определяет размер перекрытия.
### 8. Произвольный blob вместо payload
```bash
--blob=mydata:@custom_payload.bin \
--lua-desync=multidisorder:blob=mydata:pos=10,100,-20
```
Режет и отправляет произвольные данные из файла вместо реального payload, в обратном порядке.
### 9. Защита от отсутствующего blob
```bash
--lua-desync=multidisorder:blob=maybe_missing:optional:pos=midsld
```
Если blob не существует — тихий пропуск, без ошибок и без VERDICT_DROP.
### 10. Отладка: не блокировать оригинал
```bash
--payload=http_req --lua-desync=multidisorder:pos=host,midsld:nodrop
```
Отправляет нарезанные сегменты в обратном порядке И пропускает оригинальный пакет (для экспериментов).
### 11. IP-фрагментация поверх TCP-сегментации (двойной disorder)
```bash
--payload=tls_client_hello \
--lua-desync=multidisorder:pos=1,midsld:ipfrag:ipfrag_disorder:ipfrag_pos_tcp=32
```
Каждый TCP-сегмент дополнительно фрагментируется на IP-уровне в обратном порядке. Двойной disorder: TCP-сегменты в обратном порядке + IP-фрагменты каждого сегмента тоже в обратном порядке.
### 12. Комбинация: fake -> multidisorder (типичный боевой профиль)
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--lua-desync=multidisorder:pos=1,midsld:seqovl=midsld-1:seqovl_pattern=0x1603030000
```
Сначала отправляется фейковый TLS ClientHello (с fooling), затем реальный — нарезанный на 3 сегмента в обратном порядке с seqovl.
### 13. Боевой пример для YouTube
```bash
--filter-tcp=443 --hostlist=youtube.txt \
--lua-desync=fake:blob=fake_default_tls:repeats=11:tcp_md5 \
--lua-desync=multidisorder:pos=1,midsld
```
11 фейков подряд + реальный payload разрезан на 3 части в обратном порядке.
### 14. С TCP timestamp + IP ID
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=1:tcp_ts_up:ip_id=seq:ip_id_conn
```
### 15. Повторы отправки
```bash
--payload=tls_client_hello --lua-desync=multidisorder:pos=midsld:repeats=2
```
Каждый сегмент отправляется 2 раза (бинарные повторы).
---
> **Источники:** `lua/zapret-antidpi.lua:546-629`, `lua/zapret-lib.lua`, `docs/manual.md:4067-4097`, `docs/readme.md` из репозитория zapret2.