---
date: 2026-02-17
tags:
  - zapret
  - zapret2
  - nfqws2
  - lua
  - lua-desync
  - antidpi
  - tcp
  - multidisorder
  - multidisorder_legacy
  - seqovl
aliases:
  - multidisorder_legacy
description: "Справочник multidisorder_legacy в Zapret2: попакетный обратный порядок TCP-сегментов, полная совместимость с nfqws1, маркеры pos, seqovl, примеры."
---

> [!mirror] Резервное зеркало
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Zapret2/desync/multidisorder_legacy](https://wiki2.zapret.moe/Zapret2/desync/multidisorder_legacy)

# `multidisorder_legacy` --- попакетный обратный порядок 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]].

На этой особенности TCP построено целое семейство техник, и `multidisorder_legacy` — одна из них. Движок Zapret 2 (`nfqws2`) перехватывает исходящий пакет с ClientHello или HTTP-запросом, режет его данные в заданных местах и отправляет получившиеся куски отдельными TCP-сегментами в обратном порядке, от последнего к первому, каждому ставя правильный seq. Перехваченный оригинал при этом выбрасывается, чтобы те же данные не ушли второй раз. Места разреза задаёт аргумент `pos`: число («первые N байт уходят отдельным сегментом») или маркер, привязанный к устройству запроса, например `midsld` — середина домена второго уровня, главной части имени сайта (у `www.example.com` это `example`). Маркеров можно перечислить несколько через запятую; все они разобраны в разделе [[#Маркеры позиций (pos)|Маркеры позиций (pos)]]. Общий принцип обратного порядка описан в разделе [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|«Обратный порядок отправки — суть disorder»]] статьи про [[Zapret2/desync/multidisorder|multidisorder]].

Теперь о том, чем legacy-вариант отличается от нового `multidisorder`. Иногда ClientHello не помещается в один TCP-сегмент (например, если в нём есть ключи для post-quantum шифрования) и уходит из системы двумя и более пакетами; такой запрос называют многопакетным. Тогда `nfqws2` придерживает пакеты, склеивает их данные в общий буфер (`reasm_data`, reasm — от reassembly, «повторная сборка») и затем перепроигрывает (replay): вызывает desync-функцию на каждой придержанной части, но уже с собранным целым на руках. Подробно это описано в разделе [[Zapret2/desync/multidisorder#Поведение при replay / reasm|«Поведение при replay / reasm» статьи про multidisorder]]. Новый `multidisorder` режет весь склеенный буфер целиком и разворачивает порядок для всего запроса: последний кусок ClientHello уйдёт первым, даже если исходно он приехал третьим пакетом. `multidisorder_legacy` повторяет поведение nfqws1 — первой версии zapret: он обрабатывает каждый исходный пакет отдельно, разворачивает порядок кусков только внутри этого пакета, а сами пакеты идут друг за другом в прямом порядке. Исходные размеры пакетов при этом сохраняются. Если запрос уместился в один пакет, разницы между двумя функциями нет: подробнее — в нюансе [[#7. Для однопакетных payload разницы нет|«Для однопакетных payload разницы нет»]].

Отличие от [[multidisorder]] заметно, только когда ClientHello не помещается в один пакет. На схеме ClientHello длиной 800 байт ядро отправило двумя пакетами: A (байты 0–499) и B (байты 500–799); разрезы стоят на позициях 200 и 600. `multidisorder_legacy` режет каждый пакет отдельно и разворачивает части только внутри него: A2, A1, затем B2, B1. Новый `multidisorder` развернул бы весь поток целиком: 600–800, 200–600, 0–200. Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.

![[desync-anim-multidisorder_legacy.svg|Анимация multidisorder_legacy: части разворачиваются только внутри каждого пакета, пакет A идёт раньше пакета B]]

Сервер получает все куски, раскладывает их по seq и видит ровно тот запрос, который отправил браузер, поэтому отвечает как обычно. DPI видит другое: имя сайта разорвано границами кусков, и куски идут в обратном порядке. Если DPI разбирает пакеты по одному или не умеет пересобирать поток, пришедший не по порядку, полного имени он не найдёт. DPI, который пересобирает поток, теоретически может собрать запрос, но многие на практике ограничены буфером или таймаутом и не дожидаются всех частей (так это описано в разделе [[Zapret2/desync/multidisorder#Почему disorder ломает DPI|«Почему disorder ломает DPI»]]). Отдельный довод в пользу legacy: некоторые DPI анализируют размеры TCP-сегментов, а legacy сохраняет размеры исходных пакетов и меняет только порядок внутри каждого.

К разрезу можно добавить приём `seqovl` — скрытый фейк. Функция дописывает слева ко второму по порядку куску (в исходной нарезке) несколько байт-заглушки, по умолчанию нулей, и сдвигает его seq назад на ту же длину; этот кусок уходит предпоследним. Последним уходит самый первый кусок и переписывает заглушку настоящими данными в буфере сервера, а DPI может принять заглушку, например, за начало TLS record (заголовка записи TLS). В legacy-варианте `seqovl` применяется только к тому пакету, в который попало его место; механика — в разделе [[#seqovl --- попакетный скрытый фейк|seqovl --- попакетный скрытый фейк]]. Про Windows-серверы, на которых этот приём в режиме disorder не работает, сказано в разделе [[Zapret2/desync/multidisorder#Ограничение: Windows-серверы|«Ограничение: Windows-серверы»]].

Самая короткая команда включает `multidisorder_legacy` со значениями по умолчанию: один разрез после второго байта (`pos=2`), обработка только распознанных протоколов (`payload=known`) и только исходящих пакетов (`dir=out`). Строку `--lua-desync=…` пишут в [[Zapret2/profile#Анатомия профиля|профиль]] — набор правил Zapret 2 для определённого трафика:

```bash
--lua-desync=multidisorder_legacy
```

Для HTTPS берут типовой вариант `--payload=tls_client_hello --lua-desync=multidisorder_legacy:pos=1,midsld`; здесь `--payload=tls_client_hello` значит «срабатывать только на TLS ClientHello». Остальные готовые команды собраны в разделах [[#Быстрый старт|Быстрый старт]] и [[#Практические примеры|Практические примеры]].

Выбирать legacy стоит, когда вы переносите рабочий профиль из nfqws1 с ключом `--dpi-desync=multidisorder` и нужна полная совместимость, либо когда DPI чувствителен к исходной сегментации. Если нужны аргументы `blob` или `nodrop`, legacy не подойдёт: он их не поддерживает, и тогда берут новый [[Zapret2/desync/multidisorder#Зачем нужен multidisorder|multidisorder]]. Работает legacy, как и вся семья, только с TCP. Порядок выбора между двумя вариантами сведён в таблицу раздела [[#Когда использовать multidisorder_legacy|Когда использовать multidisorder_legacy]], а полное сравнение — в разделе [[#Сравнительная таблица: multidisorder vs multidisorder_legacy|Сравнительная таблица]]. Ближайшие соседи: [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]] режет так же, но отправляет куски по порядку; [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] и [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] делают один разрез и подмешивают фейковые сегменты — отдельные пакеты-обманки, которые нарочно портят (такая порча называется fooling, см. [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]), чтобы сервер их отбросил, а DPI прочитал. Отдельный пакет-обманку с ложными данными отправляет функция [[Zapret2/desync/fake#Зачем нужен fake|fake]], и с ней `multidisorder_legacy` можно комбинировать (см. [[#6. Комбинация: fake + multidisorder_legacy|пример 6]]).

---

## Оглавление

- [Коротко и по-простому](#коротко-и-по-простому)
- [Справка: где функция в коде и родственники](#справка-где-функция-в-коде-и-родственники)
- [Зачем нужен multidisorder_legacy](#зачем-нужен-multidisorder_legacy)
- [Быстрый старт](#быстрый-старт)
- [Ключевое отличие: попакетная обработка vs целый reasm](#ключевое-отличие-попакетная-обработка-vs-целый-reasm)
  - [Диаграмма: новый multidisorder (весь reasm)](#диаграмма-новый-multidisorder-весь-reasm)
  - [Диаграмма: multidisorder_legacy (попакетно)](#диаграмма-multidisorder_legacy-попакетно)
- [Откуда берутся данные](#откуда-берутся-данные)
- [Маркеры позиций (pos)](#маркеры-позиций-pos)
  - [Типы маркеров](#типы-маркеров)
  - [Относительные маркеры](#относительные-маркеры)
  - [Арифметика маркеров](#арифметика-маркеров)
  - [Нормализация позиций по пакету](#нормализация-позиций-по-пакету)
  - [Важные нюансы pos](#важные-нюансы-pos)
- [seqovl --- попакетный скрытый фейк](#seqovl-----попакетный-скрытый-фейк)
  - [seqovl --- маркер, а не число](#seqovl-----маркер-а-не-число)
  - [Нормализация seqovl по пакету](#нормализация-seqovl-по-пакету)
  - [seqovl_pattern](#seqovl_pattern)
- [Полный список аргументов](#полный-список-аргументов)
  - [A) Собственные аргументы multidisorder_legacy](#a-собственные-аргументы-multidisorder_legacy)
  - [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)
- [Порядок отправки сегментов](#порядок-отправки-сегментов)
- [Место в общем скелете](#место-в-общем-скелете)
- [Псевдокод алгоритма](#псевдокод-алгоритма)
- [Сравнительная таблица: multidisorder vs multidisorder_legacy](#сравнительная-таблица-multidisorder-vs-multidisorder_legacy)
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
- [Миграция с nfqws1](#миграция-с-nfqws1)
- [Практические примеры](#практические-примеры)
- [📚 См. также](#-см-также)

---

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

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

**Файл:** `lua/zapret-antidpi.lua:649`
**nfqws1 эквивалент:** `--dpi-desync=multidisorder` (полная совместимость)
**Сигнатура:** `function multidisorder_legacy(ctx, desync)`

`multidisorder_legacy` --- реализация алгоритма [[multidisorder]], **полностью совместимая с nfqws1**. Ключевое отличие от нового [[multidisorder]]: legacy-вариант работает **попакетно** --- он обрабатывает каждую оригинальную часть replay отдельно, сохраняя исходную TCP-сегментацию. Сегменты отправляются в обратном порядке **только внутри каждого пакета**, а между пакетами --- в прямом порядке (как они пришли).

> [!note] Проще говоря
> Payload — полезные данные пакета без сетевых заголовков: сам HTTP-запрос или сам ClientHello. Reasm — те же данные, собранные движком из нескольких пакетов, если запрос в один пакет не влез. Replay — перепроигрывание: движок по очереди вызывает функцию на каждой придержанной части. Слова «попакетно» и «по всему reasm» описывают, как функция с этим обходится: legacy обрабатывает каждую исходную часть отдельно, новый `multidisorder` берёт собранное целое. Попакетная обработка подробно разобрана в разделе [[#Ключевое отличие: попакетная обработка vs целый reasm|Ключевое отличие: попакетная обработка vs целый reasm]], а место функции среди остальных — в разделе [[#Место в общем скелете|Место в общем скелете]]. `VERDICT_DROP` — решение «перехваченный пакет не отправлять»; как движок складывает такие решения от разных функций, описано в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]].

Родственные функции: [[multisplit]] (прямой порядок), [[multidisorder]] (новый, по всему reasm), [[fakedsplit]] (с фейками), [[fakeddisorder]] (фейки + обратный порядок).

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

---

## Зачем нужен multidisorder_legacy

Сначала о том, откуда вообще берётся многопакетность. Запрос, который не поместился в один TCP-сегмент (например, большой TLS ClientHello), уходит несколькими пакетами; `nfqws2` придерживает их, собирает в буфер `reasm_data` и перепроигрывает через desync-функции. Как это устроено, разобрано в разделе [[Zapret2/desync/multidisorder#Поведение при replay / reasm|«Поведение при replay / reasm» статьи про multidisorder]]. Новый `multidisorder` и его legacy-вариант по-разному обходятся с этим буфером, и весь смысл legacy-варианта в этой разнице.

Новый [[multidisorder]] в zapret2 работает с полным `reasm_data` целиком: он собирает весь многопакетный payload, нарезает его и отправляет все сегменты в обратном порядке за один проход. Это изменяет оригинальную TCP-сегментацию и порядок следования частей по сравнению с nfqws1.

`multidisorder_legacy` нужен для случаев, когда:

1. **Необходима 100%-я совместимость с nfqws1.** Если рабочий профиль из nfqws1 использовал `--dpi-desync=multidisorder`, legacy-вариант воспроизведёт точно такое же поведение
2. **DPI чувствителен к оригинальной сегментации.** Некоторые DPI анализируют размеры TCP-сегментов. Legacy сохраняет оригинальные размеры пакетов, меняя только порядок внутри каждого
3. **Нужен контроль seqovl на уровне пакетов.** В legacy seqovl применяется только к тому пакету, в который попала нормализованная позиция, а не ко всему reasm

> [!note] Проще говоря
> Новый `multidisorder` склеивает многопакетный запрос в одно целое и разворачивает порядок всех кусков разом. `multidisorder_legacy` не склеивает данные для отправки: каждый исходный пакет он режет сам и разворачивает только внутри него, как делал nfqws1. Выбирать его стоит, когда нужна точная совместимость с nfqws1, когда DPI анализирует размеры TCP-сегментов или когда `seqovl` должен действовать на уровне пакета, а не всего запроса. Сам приём `seqovl` объяснён в разделе [[#seqovl --- попакетный скрытый фейк|seqovl --- попакетный скрытый фейк]].

---

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

Ниже три готовые строки для [[Zapret2/profile#Анатомия профиля|профиля]] Zapret 2, от самой короткой к более сложной. Флаг `--payload=…` работает как фильтр для всех вызовов `--lua-desync`, которые идут после него (до следующего `--payload` или до конца профиля): они срабатывают только на данных указанного типа. Поэтому `--payload` пишут перед `--lua-desync` (подробно — в [[последовательность аргументов]]).

Минимально (разрез по позиции 2, payload=known, dir=out):

```bash
--lua-desync=multidisorder_legacy
```

Здесь `payload=known` означает «только распознанные протоколы», а `dir=out` — «только исходящие пакеты»; оба аргумента разобраны в разделах [[#B) Standard direction|B) Standard direction]] и [[#C) Standard payload|C) Standard payload]].

Типовой TLS-разрез посередине SNI:

```bash
--payload=tls_client_hello --lua-desync=multidisorder_legacy:pos=1,midsld
```

Функция срабатывает только на TLS ClientHello и режет каждый пакет по маркерам `1` (после первого байта) и `midsld` (посередине домена второго уровня). Для ClientHello, уместившегося в один пакет, это три сегмента, и они уходят в обратном порядке. Если пакетов несколько, разрезы делятся между ними, и обратный порядок действует внутри каждого пакета (см. [[#A) Собственные аргументы multidisorder_legacy|A) Собственные аргументы multidisorder_legacy]]).

TLS с seqovl (маркер):

```bash
--payload=tls_client_hello --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=midsld-1
```

Здесь к разрезу добавлен `seqovl` — скрытый фейк. Он задан маркером `midsld-1`, а не числом. Как он действует и почему применяется только к одному пакету, разобрано в разделах [[#seqovl --- маркер, а не число|seqovl --- маркер, а не число]] и [[#Нормализация seqovl по пакету|Нормализация seqovl по пакету]].

---

## Ключевое отличие: попакетная обработка vs целый reasm

Это **центральное** различие между [[multidisorder]] и `multidisorder_legacy`. Рассмотрим его на примере большого TLS ClientHello, который не влезает в один TCP-сегмент и разбит на 2 пакета.

Сначала о словах. Многопакетный запрос — запрос, который не поместился в один TCP-сегмент и ушёл из системы двумя и более пакетами; ниже их два, A и B. Reasm (`reasm_data`) — те же данные, склеенные в одну цепочку байт, как если бы пакет был один. Позиции разрезов (в примере 200 и 600) считаются по этой склеенной цепочке. А вот отправляют два варианта функции по-разному: новый режет саму цепочку, а legacy возвращается к исходным пакетам.

На схемах offset — число байт запроса, которые идут перед пакетом (у A — 0, у B — 500, то есть длина A); в коде оно называется `reasm_offset`, и от него отсчитывается `range_low`. Число seq у частей на схемах отсчитывается от начала запроса и показывает, в какое место потока встанет кусок на стороне сервера. Обе схемы построены одинаково, поэтому их удобно сравнивать построчно.

### Диаграмма: новый multidisorder (весь reasm)

Здесь нужно проследить, как исходные пакеты теряют свои границы. `nfqws2` склеивает A и B в буфер на 800 байт, режет его в позициях 200 и 600 на три части и отправляет их в порядке 3, 2, 1.

![[wiki-anim-z2-md-legacy.svg|Анимация: новый multidisorder режет весь reasm, legacy режет каждый пакет отдельно]]

Часть 2 составлена из конца пакета A и начала пакета B, поэтому исходная граница между пакетами (500 байт + 300 байт) исчезает, и порядок 3, 2, 1 распространяется на весь запрос. Это и подчёркивает последняя строка схемы: оригинальная сегментация потеряна.

### Диаграмма: multidisorder_legacy (попакетно)

Здесь функция вызывается для каждого пакета отдельно. Для каждого пакета она держит в руках два набора данных: сам пакет (`data`, его режут и отправляют) и склеенную цепочку целиком (`fulldata`, по ней только вычисляются места разрезов); подробнее — в разделе [[#Откуда берутся данные|Откуда берутся данные]]. Затем позиции нормализуются: переводятся в номера внутри текущего пакета, а те, что в пакет не попали, удаляются. Формула и второй пример — в разделе [[#Нормализация позиций по пакету|Нормализация позиций по пакету]].

| Пакет | Позиции на fulldata | После нормализации | Части | Порядок на проводе |
|:--|:--|:--|:--|:--|
| A (500 байт, offset 0, диапазон [1, 501)) | 200 и 600: 600 вне диапазона, удалён | `pos=[200]` | A1 — 200 байт, A2 — 300 байт | A2 (seq 200), затем A1 (seq 0) |
| B (300 байт, offset 500, диапазон [501, 801)) | 200 вне диапазона, удалён; 600 → 600 - 501 + 1 = 100 | `pos=[100]` | B1 — 100 байт, B2 — 200 байт | B2 (seq 600), затем B1 (seq 500) |

Итог на проводе: A2, A1, B2, B1. Исходная сегментация 500 + 300 сохранена, обратный порядок только внутри каждого пакета, между пакетами прямой.

> [!note] Проще говоря
> Сначала функция обрабатывает пакет A: позиция 200 ему подходит, позиция 600 нет. Части A2 и A1 уходят в обратном порядке. Потом пакет B: подходит 600, а 200 отбрасывается; уходят B2 и B1. Пакеты идут друг за другом как пришли, исходные размеры 500 и 300 сохранены, а обратный порядок остаётся внутри каждого пакета. Итог на проводе: A2, A1, B2, B1.

---

## Откуда берутся данные

Чтобы понять, что режется и по чему считаются места разреза, нужно различать два набора данных. Первый — пакет, который прямо сейчас перехватил движок: его полезное содержимое лежит в `desync.dis.payload` (`dis` — диссект, то есть пакет, заранее разобранный движком на поля; его устройство описано в [[структура desync и диссекта]]). Второй — весь многопакетный запрос, собранный из частей, `desync.reasm_data` (как он собирается, см. [[Zapret2/структура desync и диссекта#Приём многопакетных пейлоадов (reasm / decrypt / replay)|Приём многопакетных пейлоадов]]). `multidisorder_legacy` использует оба одновременно.

В `multidisorder_legacy` используются **два** источника данных одновременно:

```
data     = desync.dis.payload       -- текущий пакет (для нарезки и отправки)
fulldata = desync.reasm_data        -- полный реассемблированный payload (для resolve_pos)
```

**Почему два источника:**
- `fulldata` нужен для разрешения маркеров (`midsld`, `host` и т.д.), потому что они вычисляются по полному payload (SNI может быть виден только в контексте всего ClientHello)
- `data` --- это то, что реально нарезается и отправляется: именно текущий оригинальный пакет

Простыми словами: маркер вроде `midsld` нужно искать во всём ClientHello, потому что имя сайта может оказаться в другом пакете, чем тот, что сейчас перехвачен. А резать и отправлять можно только то, что реально пришло в этом пакете.

**Отличие от нового [[multidisorder]]:**

| | multidisorder (новый) | multidisorder_legacy |
|:--|:--|:--|
| data для нарезки | `blob_or_def() or reasm_data or dis.payload` | `dis.payload` (всегда текущий пакет) |
| data для resolve_pos | то же самое (data) | `reasm_data` (fulldata) |
| blob поддержка | Да | **Нет** |

**Следствие:** `multidisorder_legacy` **не поддерживает** аргумент `blob`. Нельзя заменить payload на произвольные данные.

> [!note] Проще говоря
> Legacy режет только то, что пришло в текущем пакете, а места разрезов ищет по всему запросу. Blob — заготовленные данные, которые можно подсунуть вместо настоящих ([[blob]]); новый `multidisorder` их принимает, legacy нет. Как выбор данных устроен у нового варианта, описано в разделе [[Zapret2/desync/multidisorder#Откуда берутся данные для нарезки|«Откуда берутся данные для нарезки»]], а общая цепочка blob → reasm → payload — в [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload|стадии 4 жизненного цикла]].

---

## Маркеры позиций (pos)

![[desync-anim-hub-markers.svg|Анимация: маркеры позиций pos на HTTP-запросе и на TLS ClientHello, разрез по pos=host,midsld,endhost]]

`pos` --- главный аргумент. Определяет **где** внутри payload будет произведён разрез. Задаётся как строка со списком маркеров через запятую.

Маркер — это место в данных, где надо сделать разрез: число байт от начала или от конца либо «смысловая» позиция вроде `midsld`. Здесь разобраны только те особенности маркеров, которые нужны legacy-варианту. Полный разбор с примерами и тем, как маркеры разрешаются в коде, — в разделах [[Zapret2/desync/multidisorder#Маркеры позиций (pos)|«Маркеры позиций (pos)» у multidisorder]] и [[Zapret2/desync/multisplit#Маркеры позиций (pos)|у multisplit]]. Отсчёт в командной строке идёт от нуля: `pos=N` значит «первые N байт уходят отдельным сегментом».

### Типы маркеров

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

| Тип | Описание | Пример |
|:----|:---------|:-------|
| **Абсолютный положительный** | Смещение от начала payload, считая с 0. `pos=N` → первые N байт становятся отдельным сегментом | `1`, `5`, `100` |
| **Абсолютный отрицательный** | Смещение от конца payload. `-1` = последний байт | `-1`, `-10`, `-50` |
| **Относительный** | Логическая позиция внутри распознанного payload. Привязана к структуре протокола | `midsld`, `host`, `sniext` |

### Относительные маркеры

В правой колонке указано, в каких типах payload маркер имеет смысл: `http_req` — HTTP-запрос, `tls_client_hello` — TLS ClientHello. Как движок определяет тип, описано в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|о типах payload]]. SLD (second-level domain) — домен второго уровня: в `www.example.com` это `example`. SNI extension — расширение ClientHello, в котором браузер передаёт имя сайта.

| Маркер | Описание | Для каких 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 | `tls_client_hello` |
| `extlen` | Поле длины всех TLS extensions | `tls_client_hello` |

### Арифметика маркеров

Маркер можно сдвинуть на несколько байт вперёд или назад, если разрез нужен рядом с ним, а не точно в нём.

К любому маркеру можно прибавить (+) или вычесть (-) целое число:

```
midsld+1      -- один байт ПОСЛЕ середины SLD
midsld-1      -- один байт ДО середины SLD
endhost-2     -- два байта до конца hostname
host+3        -- три байта после начала hostname
sniext+1      -- один байт после начала SNI extension data
-1            -- последний байт payload (абсолютный, не относительный)
```

### Нормализация позиций по пакету

Когда запрос состоит из нескольких пакетов, места разрезов сначала ищутся по всему запросу, а затем каждый пакет забирает себе только те из них, что попали в его границы, и пересчитывает их в номера внутри себя. Так работает нормализация. Границы пакета задаются двумя числами, `range_low` и `range_hi`.

Это **ключевая** особенность `multidisorder_legacy`, отсутствующая в новом [[multidisorder]].

Маркеры разрешаются по **fulldata** (весь reasm), но затем нормализуются по диапазону текущего пакета:

```
range_low = (desync.reasm_offset or 0) + 1
range_hi  = range_low + #data
```

Функция `pos_array_normalize(pos, range_low, range_hi)`:
1. Для каждой позиции проверяет: `pos >= range_low AND pos < range_hi`
2. Если позиция в диапазоне --- нормализует: `pos_normalized = pos - range_low + 1`
3. Если позиция вне диапазона --- **удаляет** из массива

Пример: `fulldata` 800 байт, маркеры разрешены в `pos = [200, 450, 600]`.

| Пакет | Диапазон | Что с каждой позицией | Результат |
|:--|:--|:--|:--|
| A (offset 0, 500 байт) | `range_low=1`, `range_hi=501` | 200 и 450 в диапазоне, остаются; 600 вне диапазона, удалён | `[200, 450]` |
| B (offset 500, 300 байт) | `range_low=501`, `range_hi=801` | 200 и 450 меньше 501, удалены; 600 в диапазоне: 600 - 501 + 1 = 100 | `[100]` |

> [!note] Проще говоря
> Из нескольких пакетов каждый оставляет себе только «свои» места разрезов. В примере пакет A получает 200 и 450, а 600 у него удаляется; пакет B удаляет 200 и 450 и получает единственную позицию 100 (600 − 501 + 1). У пакета A `range_low` равен 1, у пакета B — 501. Такой пересчёт отсутствует в новом [[Zapret2/desync/multidisorder#Маркеры позиций (pos)|multidisorder]], где режется весь запрос сразу.

### Важные нюансы pos

- **Разрез в самом начале данных отбрасывается.** `delete_pos_1` удаляет Lua-позицию 1, соответствующую маркеру `0` (маркеры в командной строке считаются с нуля). Это выполняется **после** нормализации, поэтому удаляется и позиция, нормализовавшаяся в начало текущего куска reasm
- **Дублирующиеся позиции объединяются.** `pos=5,5,5` = `pos=5`
- **Неразрешимые маркеры пропускаются.** Если `midsld` не разрешается (payload = unknown), он исчезает из списка
- **Позиции сортируются.** Независимо от порядка записи, `pos=100,5,50` обрабатывается как `5,50,100`
- **По умолчанию pos="2".** Если `pos` не задан, разрез по позиции 2
- **Если все позиции вышли за пределы пакета** --- пакет отправляется **как есть** (с применёнными опциями fooling, ipid и т.д.) через `rawsend_payload_segmented`, без разреза

> [!note] Проще говоря
> Здесь собраны условия, при которых разрез не происходит или происходит не так, как ждёшь. Разрез в самом начале данных бессмысленен, потому что первый кусок вышел бы пустым, поэтому он выбрасывается. Одинаковые места сливаются в одно, порядок записи не важен, а маркер, который в этом типе данных найти нельзя, просто пропадает из списка. Если в пакете не осталось ни одного места разреза, он уходит как есть, но с включёнными fooling, ipid и прочими опциями. Как это выглядит на примере, см. нюансы [[#4. Позиция 1 удаляется после нормализации|4]] и [[#5. Пакет без позиций отправляется как есть|5]].

---

## seqovl --- попакетный скрытый фейк

`seqovl` (Sequence Overlap, «перекрытие по номерам последовательности») — приём, при котором в настоящий сегмент слева дописываются фальшивые байты, а его seq сдвигается назад на ту же длину. Как приём работает в других функциях, разобрано в разделах [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|«seqovl — скрытый фейк внутри сегмента» у multisplit]] (сервер выбрасывает лишнее, потому что оно левее окна приёма) и [[Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие|«seqovl — перезапись буфера сокета через перекрытие» у multidisorder]] (лишнее лежит в буфере и потом переписывается настоящими данными). Здесь описана специфика legacy: приём применяется на уровне пакета.

### seqovl --- маркер, а не число

Слово «маркер» здесь то же, что в разделе [[#Маркеры позиций (pos)|Маркеры позиций (pos)]]: место в запросе можно задать не только числом, но и смысловой позицией. Подробнее о том же в [[Zapret2/desync/multidisorder#seqovl как маркер|разделе про seqovl как маркер]] у нового варианта.

В отличие от [[multisplit]], где `seqovl` принимает только число, в `multidisorder_legacy` (и в новом [[multidisorder]]) `seqovl` является **маркером**. Это значит, что можно написать:

```bash
:seqovl=midsld-1      -- seqovl = (позиция midsld) - 1
:seqovl=host           -- seqovl = позиция host
:seqovl=5              -- seqovl = 5 (число тоже маркер)
```

> [!note] Проще говоря
> `seqovl` можно записать так же, как места разрезов: `midsld-1`, `host` или просто число. Как и места разрезов, маркер разрешается по всему запросу, а не по одному пакету.

Маркер seqovl разрешается через `resolve_pos(fulldata, l7payload, seqovl)` --- по **полному** reasm, точно так же, как и позиции разреза.

### Нормализация seqovl по пакету

После того как маркер `seqovl` превратился в номер по всему запросу, его, как и места разрезов, нужно пересчитать под текущий пакет:

После разрешения маркера, seqovl нормализуется по диапазону текущего пакета через `pos_normalize(seqovl, range_low, range_hi)`:

```
seqovl_resolved = resolve_pos(fulldata, l7payload, "midsld-1")  -- например, 245
seqovl_normalized = pos_normalize(245, range_low, range_hi)

Для пакета A (range_low=1, range_hi=501):
  245 >= 1 AND 245 < 501 -> OK -> seqovl = 245 - 1 + 1 = 245

Для пакета B (range_low=501, range_hi=801):
  245 >= 501? -> НЕТ -> seqovl = nil (отменён для этого пакета)
```

> [!note] Проще говоря
> Место `seqovl` попадает в один пакет — в примере в пакет A. Для пакета B пересчитать его нельзя, и там `seqovl` просто выключается. Пересчёт числа 245 в пакете A по формуле из примера выше даёт 245 − 1 + 1 = 245, потому что для пакета A `range_low` равен 1.

**Следствие:** seqovl применяется **только к тому пакету**, в диапазон которого попала нормализованная позиция seqovl. Для остальных пакетов seqovl не действует.

### seqovl_pattern

Паттерн — то, чем заполняются лишние байты слева. Blob — заготовленные данные под именем; как их объявлять, описано в заметке [[blob]]. О том же аргументе в других функциях см. [[Zapret2/desync/multidisorder#seqovl_pattern|seqovl_pattern у multidisorder]].

Паттерн заполнения seqovl-области. По умолчанию --- `0x00` (нули). Задаётся как имя blob:

```bash
:seqovl_pattern=0x1603030000         -- inline hex
:seqovl_pattern=my_pattern_blob       -- предзагруженный blob
```

Если `optional` задан и blob `seqovl_pattern` отсутствует --- используется нулевой паттерн (seqovl не отменяется).

---

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

Аргументы делятся на собственные аргументы `multidisorder_legacy` (раздел A) и стандартные (разделы B–H): их понимают и другие функции дурения, а общий обзор есть в [[desync]].

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

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

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

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

#### `pos`

- **Формат:** `pos=<marker[,marker2,...]>`
- **Тип:** строка со списком маркеров через запятую
- **По умолчанию:** `"2"`
- **Описание:** Точки разреза. Маркеры разрешаются по fulldata (reasm_data), затем нормализуются по диапазону текущего пакета. N маркеров -> до N+1 сегментов **на пакет**
- **Примеры:**
  - `pos=2` --- разрез после 2-го байта (дефолт)
  - `pos=1` --- первый байт уходит отдельным сегментом
  - `pos=midsld` --- разрез посередине SLD
  - `pos=1,midsld` --- два разреза
  - `pos=host,midsld,endhost-2,-10` --- четыре разреза

#### `seqovl`

- **Формат:** `seqovl=<marker>` (маркер, не просто число!)
- **Тип:** маркер (строка, разрешаемая через `resolve_pos`)
- **По умолчанию:** не задан (нет seqovl)
- **Описание:** Разрешается по fulldata, нормализуется по текущему пакету. Применяется ко 2-му сегменту в оригинальной очередности (предпоследнему отсылаемому). seqovl обязательно должен быть меньше первой позиции разреза (в нормализованном виде), иначе отменяется
- **Примеры:**
  - `seqovl=5` --- 5 байт фейка
  - `seqovl=midsld-1` --- seqovl привязан к позиции midsld
  - `seqovl=host` --- seqovl привязан к позиции host

#### `seqovl_pattern`

- **Формат:** `seqovl_pattern=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** один байт `0x00`, повторяемый до длины seqovl
- **Описание:** Данные для заполнения seqovl-области. Blob повторяется функцией `pattern()` до нужного размера
- **Поведение с `optional`:** если `optional` задан и blob отсутствует --- используется нулевой паттерн

#### `optional`

- **Формат:** `optional` (флаг, без значения)
- **Описание:** Мягкий режим. Если задан `seqovl_pattern=...` и blob отсутствует --- используется нулевой паттерн (seqovl не отменяется)

**Внимание:** `blob` и `nodrop` **не поддерживаются** в `multidisorder_legacy`. Это ключевое отличие от нового [[multidisorder]].

> [!note] Проще говоря
> Собственных аргументов здесь четыре: `pos` (где резать), `seqovl` (где и сколько фальшивых байт приклеить), `seqovl_pattern` (чем их заполнить) и `optional` (мягкий режим для паттерна). Аргументов `blob` и `nodrop` у legacy нет: указав их, ничего не сломаешь, но и эффекта не получишь. Причина и последствия — в нюансе [[#2. Нет blob и nodrop|2. Нет blob и nodrop]]; про `nodrop` у других функций см. [[Zapret2/desync/multisplit#4. nodrop создаёт дублирование|нюанс 4 в multisplit]].

---

### B) Standard direction

Направление говорит, в какую сторону идёт пакет: от вашего компьютера к серверу (исходящий) или обратно (входящий).

| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |

Фильтр по направлению пакета. `multidisorder_legacy` по умолчанию работает только с исходящими (`out`).

- `dir=out` --- только исходящие (от клиента к серверу)
- `dir=in` --- только входящие (от сервера к клиенту)
- `dir=any` --- оба направления

При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` --- отсекает себя от противоположного направления. Cutoff (отсечение) значит, что этот экземпляр функции отключается от пакетов противоположного направления и движок перестаёт зря вызывать для них Lua; подробно — в [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадии 2 жизненного цикла]].

---

### C) Standard payload

Тип payload — название распознанного движком протокола: `http_req`, `tls_client_hello` и так далее; как движок его определяет, см. [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|заметку о типах 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, а аргумент `payload=` проверяется уже в Lua, то есть медленнее. Поэтому основной отбор лучше делать на уровне профиля (то же самое сказано в разделе [[Zapret2/desync/multisplit#C) Standard payload|C) Standard payload у multisplit]]).

---

### D) Standard fooling

Fooling — намеренная порча заголовков пакета, обычно для того, чтобы сервер отбросил фейк, а DPI его прочитал (подробно — [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). Ниже — модификации L3/L4 заголовков, то есть заголовков IP (сетевой уровень) и TCP (транспортный уровень).

Модификации L3/L4 заголовков. Применяются **ко всем** отправляемым сегментам.

| Параметр | Описание | Пример |
|:---------|:---------|:-------|
| `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` |

**Заметка:** Fooling применяется ко **ВСЕМ** сегментам (и реальным). Если задать `tcp_ack=-66000`, сервер отбросит все сегменты. Fooling в multidisorder_legacy имеет смысл только для специфических вещей (`tcp_ts_up`, `ip_id`, IPv6 extension headers).

Что это значит, сказано в нюансе [[#6. Fooling применяется ко ВСЕМ сегментам|6. Fooling применяется ко ВСЕМ сегментам]]: в отличие от fakedsplit и fakeddisorder, где fooling идёт только на фейки, здесь он достаётся всем сегментам, включая настоящие.

---

### E) Standard ipid

IP ID — идентификатор в заголовке IPv4-пакета. Эти параметры задают, какие значения получат отправляемые сегменты.

| Параметр | Описание | По умолчанию |
|:---------|:---------|:-------------|
| `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 в рамках соединения | --- |

---

### F) Standard ipfrag

IP-фрагментация — деление пакета на части уже на уровне IP; получатель склеивает их обратно на том же уровне.

IP-фрагментация **поверх** TCP-сегментации. Каждый TCP-сегмент дополнительно фрагментируется на уровне IP.

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

---

### G) Standard reconstruct

Реконструкция — сборка готового raw-пакета («сырого», со всеми заголовками) перед отправкой.

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

---

### H) Standard rawsend

Rawsend — отправка готовых сегментов в сеть самой функцией. Эти параметры управляют отправкой.

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

---

## Порядок отправки сегментов

`multidisorder_legacy` использует общую функцию `multidisorder_send`, которая отправляет сегменты **в обратном порядке** (от последнего к первому). Но поскольку legacy обрабатывает каждый пакет отдельно, обратный порядок действует **только внутри одного пакета**.

Общий обратный порядок описан в разделе [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|«Обратный порядок отправки — суть disorder»]]. Ниже три примера: один пакет, два пакета и `seqovl`.

### Пример: однопакетный payload

Если запрос уместился в один пакет, legacy ведёт себя так же, как новый `multidisorder`: части отправляются от последней к первой.

![[wiki-anim-z2-direction.svg|Анимация: multisplit отправляет куски 1, 2, 3, 4, multidisorder — 4, 3, 2, 1, у каждого свой seq]]

Здесь три места разреза дают четыре части. Первой уходит самая правая, последней — самая левая. Метка seq у каждой части равна её смещению в исходных данных.

### Пример: многопакетный payload (2 пакета)

А если пакетов два, картина другая: у каждого пакета свои части, а внутри пакета — обратный порядок.

```
Пакет A (400 байт, offset=0), pos=150
Пакет B (300 байт, offset=400), pos=550 (нормализованный: 150)

=== Обработка пакета A ===
  Часть A1: [0..149]   150 байт
  Часть A2: [150..399]  250 байт
  Отправка: A2 (seq=150), A1 (seq=0)

=== Обработка пакета B ===
  Часть B1: [0..149]   150 байт  (соответствует offset 400..549)
  Часть B2: [150..299]  150 байт  (соответствует offset 550..699)
  Отправка: B2 (seq=550), B1 (seq=400)

Итог на проводе: A2, A1, B2, B1
Между пакетами A и B --- ПРЯМОЙ порядок.
Внутри каждого --- ОБРАТНЫЙ.
```

> [!note] Проще говоря
> Место разреза 550 принадлежит пакету B, который начинается со смещения 400. Внутри пакета B оно превращается в 150 (550 − 400). Пакет A уходит целиком раньше пакета B, а внутри каждого пакета порядок обратный: A2, A1, B2, B1.

### Пример с seqovl

Теперь то же с `seqovl`. Он привязан ко второму куску в исходной очередности; этот кусок уходит предпоследним.

```
Payload (600 байт, один пакет), pos=100,300, seqovl=80:

seqovl применяется к сегменту i=1 (второй в оригинальной очередности = предпоследний отсылаемый):

Порядок отправки:
  Часть 3: [300..599]              seq=300   len=300
  Часть 2: [PATTERN(79)][100..299] seq=21    len=279  <-- seqovl=80, ovl=79
  Часть 1: [0..99]                 seq=0     len=100  <-- переписывает паттерн

Часть 1 отправляется последней и переписывает ложные данные из seqovl_pattern
в буфере TCP-стека сервера.
```

Как читать пример. Для `seqovl=80` перекрытие `ovl` на единицу меньше (79 байт): так указано на схеме (`ovl=79`), а причина — в разделе [[Zapret2/desync/multidisorder#Нюанс реализации: ovl = seqovl - 1|«Нюанс реализации: ovl = seqovl - 1»]]. У части 2 на схеме стоит `seq=21` вместо 100, а перед её настоящими данными (с байта 100) идут 79 байт паттерна; 100 − 79 = 21. Часть 1 уходит последней, и, как сказано на схеме, она переписывает ложные данные из `seqovl_pattern` в буфере TCP-стека сервера. Что происходит в таком буфере по шагам, показано в [[Zapret2/desync/multidisorder#ASCII-диаграмма: хронология буфера сокета на сервере|хронологии буфера у multidisorder]].

---

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

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

| Стадия общего скелета | Что делает `multidisorder_legacy` |
|:----------------------|:----------------|
| 1. Отсев транспорта | только TCP; на не-TCP делает `instance_cutoff` (кроме связанного ICMP) |
| 2. Направление | `dir=out` по умолчанию, отключается от входящего |
| 3. Аргументы | обязательных нет; `optional` влияет только на `seqovl_pattern` |
| 4. Данные | **отклонение**: режется `desync.dis.payload` — payload текущего пакета, а `reasm_data` берётся отдельно, только чтобы разрешить по нему маркеры |
| 5. Гварды | штатные `#data>0`, `direction_check`, `payload_check` (по умолчанию `known`) |
| 6. replay | **отклонение**: развилка `replay_first`/`replay_drop` не используется вовсе — функция работает на **каждой** части, нормализуя позиции разреза под её границы (`pos_array_normalize`) |
| 7. **Своя техника** | нормализация позиций под текущий пакет, `seqovl` внутри того же пакета, отправка сегментов в обратном порядке внутри каждой части |
| 8. Вердикт | `VERDICT_DROP` после успешной отправки; если в этом пакете не оказалось ни одной позиции разреза — пакет отправляется как есть с применёнными опциями и тоже дропается

Два отклонения — на стадиях 4 и 6 — и есть весь смысл legacy-варианта. Новый [[multidisorder]] работает с реасмом целиком, а этот повторяет поведение nfqws1: сохраняет исходную сегментацию потока и переставляет сегменты только внутри каждого отдельного пакета. Поэтому при многопакетных запросах порядок сегментов у двух функций получается разным.

> [!note] Проще говоря
> Стадия 4 — выбор данных. Большинство функций берут сначала blob, потом собранный reasm, потом текущий пакет (см. [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload|стадию 4]]); legacy режет только текущий пакет, а reasm нужен ему для разрешения маркеров. Стадия 6 — развилка перепроигрывания: обычная функция работает на первой части replay и дропает остальные, а legacy вызывается на каждой части ([[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)|стадия 6]]). Всё остальное — как у других функций.

---

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

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

Код ниже читается сверху вниз как рассказ: проверить транспорт, определить направление, взять текущий пакет и весь запрос, вычислить границы пакета, найти места разрезов, пересчитать их, при наличии `seqovl` пересчитать и его, отправить части. Комментарии в коде нумеруют шаги самой функции. Тот же порядок в общем виде описан в [[Zapret2/жизненный цикл desync-функции|жизненном цикле desync-функции]].

```lua
function multidisorder_legacy(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. Источники данных
    local data = desync.dis.payload           -- текущий пакет (для нарезки)
    local fulldata = desync.reasm_data        -- весь reasm (для resolve_pos)

    -- 4. Проверки: данные не пусты, направление OK, payload OK
    if #data > 0 and direction_check() and payload_check() then

        -- 5. Вычисление диапазона текущего пакета
        local range_low = (desync.reasm_offset or 0) + 1
        local range_hi = range_low + #data

        -- 6. Разрешение маркеров по FULLDATA
        local pos = resolve_multi_pos(fulldata, l7payload, pos_arg or "2")

        -- 7. Нормализация позиций по диапазону текущего пакета
        pos_array_normalize(pos, range_low, range_hi)
        delete_pos_1(pos)  -- удалить Lua-позицию 1 (маркер 0)

        if #pos > 0 then
            -- 8. Разрешение и нормализация seqovl (если задан)
            local seqovl = nil
            if desync.arg.seqovl then
                seqovl = resolve_pos(fulldata, l7payload, desync.arg.seqovl)
                if seqovl then
                    seqovl = pos_normalize(seqovl, range_low, range_hi)
                    -- seqovl может стать nil если вне диапазона пакета
                end
            end

            -- 9. Отправка через общую функцию (обратный порядок)
            return multidisorder_send(desync, data, seqovl, pos)
            -- multidisorder_send: for i = #pos, 0, -1 do ... end
            -- seqovl применяется к сегменту i=1 (2-й по оригинальной очередности)
        else
            -- 10. Нет позиций в этом пакете -> отправить как есть
            if rawsend_payload_segmented(desync) then
                return VERDICT_DROP
            end
        end
    end
end
```

Обратите внимание: в отличие от нового [[multidisorder]], здесь **нет** `replay_first()` / `replay_drop()` / `replay_drop_set()`. Функция вызывается для **каждого** пакета replay и обрабатывает его самостоятельно. Это и есть попакетная обработка.

> [!note] Проще говоря
> Новый `multidisorder` вызывает `replay_first()` и `replay_drop()`, чтобы обработать запрос один раз и отбросить остальные части. Legacy этих вызовов не делает: движок зовёт его на каждой части, и каждая часть обрабатывается сама по себе. Именно это отличие и называют попакетной обработкой.

---

## Сравнительная таблица: multidisorder vs multidisorder_legacy

В таблице по строкам сопоставлены два варианта функции. Последний столбец — `multidisorder_legacy`, средний — новый [[Zapret2/desync/multidisorder|multidisorder]]; ячейки со словами «Да»/«Нет» отвечают на вопрос «поддерживается ли». Разбор причин — в разделах [[#Ключевое отличие: попакетная обработка vs целый reasm|Ключевое отличие]] и [[#Нюансы и подводные камни|Нюансы и подводные камни]].

| Аспект | [[multidisorder]] (новый) | `multidisorder_legacy` |
|:-------|:--------------------------|:-----------------------|
| Совместимость с nfqws1 | Частичная (порядок может отличаться) | **Полная** |
| Источник данных для нарезки | `blob_or_def() or reasm_data or dis.payload` | `dis.payload` (текущий пакет) |
| Источник данных для resolve_pos | то же (data) | `reasm_data` (fulldata) |
| Оригинальная сегментация | **Не сохраняется** | **Сохраняется** |
| Нормализация позиций | Нет | Да, по диапазону каждого пакета |
| Порядок сегментов | Обратный по **всему** reasm | Обратный **только внутри** каждого пакета |
| Между пакетами | Обратный (как часть единого обратного порядка) | **Прямой** (порядок пакетов сохраняется) |
| seqovl | Применяется к целому reasm | **Нормализуется** по текущему пакету |
| seqovl тип | Маркер | Маркер |
| blob | **Да** | **Нет** |
| nodrop | **Да** | **Нет** |
| optional | Для blob и seqovl_pattern | Только для seqovl_pattern |
| replay_first/replay_drop | Да (обрабатывает только первый replay) | Нет (обрабатывает каждый пакет) |
| Пакет без позиций разреза | Не применимо (весь reasm обрабатывается) | Отправляется как есть с применёнными опциями |

> [!note] Проще говоря
> Главное в таблице — три строки: «Оригинальная сегментация», «Порядок сегментов» и «Между пакетами». Новый вариант теряет исходное деление на пакеты и разворачивает всё разом; legacy сохраняет пакеты и разворачивает только внутри каждого. Остальные строки — следствия этого (нормализация позиций, отсутствие replay-развилки) и разница в наборе аргументов (`blob`, `nodrop`).

---

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

### 1. Работает только с TCP

Если текущий пакет не TCP (UDP, ICMP и т.д.), `multidisorder_legacy` делает `instance_cutoff_shim` --- отключает себя для этого потока навсегда. Исключение: related ICMP не вызывает cutoff.

Почему эта техника вообще возможна только для TCP — у него сквозная нумерация байтов и сервер собирает поток по номерам — разобрано в нюансе [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|«Работает только с TCP — и почему» у multisplit]]; стадия отсева описана в [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|стадии 1 жизненного цикла]]. Instance cutoff (отсечение экземпляра) — отключение этого экземпляра функции от потока: как сказано в стадии 1 жизненного цикла, движок после этого не вызывает Lua для пакетов данного соединения.

### 2. Нет blob и nodrop

В отличие от нового [[multidisorder]], `multidisorder_legacy` **не поддерживает** аргументы `blob` и `nodrop`. Попытка использовать их не вызовет ошибку (Lua просто проигнорирует неизвестные аргументы), но и эффекта не будет:
- `blob` --- данные всегда берутся из `dis.payload`
- `nodrop` --- оригинальный пакет всегда дропается при успешной отправке (вердикт `VERDICT_DROP` из `multidisorder_send` или из `rawsend_payload_segmented`)

Проще говоря: `blob` подсовывал бы вместо настоящих данных заготовленные, а `nodrop` оставлял бы оригинал в отправке (и он ушёл бы второй раз). Legacy делает и то, и другое по-своему: данные берёт из пакета, оригинал дропает всегда. О дублировании при `nodrop` см. [[Zapret2/desync/multisplit#4. nodrop создаёт дублирование|нюанс 4 у multisplit]].

### 3. seqovl может не попасть ни в один пакет

Если маркер seqovl разрешается в позицию, которая оказывается на стыке двух пакетов и не попадает точно ни в один диапазон, seqovl будет отменён для всех пакетов. Например:

```
fulldata = 800 байт
Пакет A: offset=0, len=400   -> range [1, 401)
Пакет B: offset=400, len=400 -> range [401, 801)
seqovl разрешился в позицию 401

Пакет A: 401 >= 1 AND 401 < 401? -> НЕТ (граница не включена)
Пакет B: 401 >= 401 AND 401 < 801? -> ДА -> нормализованный seqovl = 1

Но seqovl=1 означает ovl=0 (seqovl - 1 = 0 в multidisorder_send),
поэтому фактически seqovl не применится.
```

> [!note] Проще говоря
> В примере место `seqovl` лежит ровно на границе пакетов: в пакет A оно не входит (граница A не включена), в пакете B оно нормализуется в 1, а это означает нулевое перекрытие, поэтому фейк фактически не применяется. Причина нулевого перекрытия описана в разделе [[Zapret2/desync/multidisorder#Нюанс реализации: ovl = seqovl - 1|«Нюанс реализации: ovl = seqovl - 1»]]; правило «`seqovl` должен быть меньше первой позиции разреза» — в разделе [[Zapret2/desync/multidisorder#Валидация seqovl: должен быть меньше pos[1]|«Валидация seqovl: должен быть меньше pos[1]»]].

### 4. Позиция 1 удаляется после нормализации

`delete_pos_1(pos)` вызывается **после** `pos_array_normalize`. Это значит, что позиция, которая нормализуется в 1 (например, `range_low` сама по себе), будет удалена. Это правильное поведение --- нельзя разрезать на самом первом байте пакета.

Проще говоря: если пересчёт поставил разрез на самое начало текущего пакета, разрез удаляется, потому что первая часть вышла бы пустой. Так же поступает и multisplit (см. его [[Zapret2/desync/multisplit#2. Разрез в самом начале данных отбрасывается|нюанс 2]]).

### 5. Пакет без позиций отправляется как есть

Если после нормализации в пакете не осталось ни одной позиции разреза, пакет **не пропускается**, а отправляется как есть через `rawsend_payload_segmented(desync)`. Это значит, что к нему всё равно будут применены fooling, ipid, ipfrag и прочие опции.

Проще говоря: пакет без разрезов не теряется. Он уходит целым, но с теми настройками порчи заголовков и IP ID, которые вы задали, и оригинал дропается.

### 6. Fooling применяется ко ВСЕМ сегментам

Как и в [[multisplit]] / [[multidisorder]], fooling в `multidisorder_legacy` идёт на **все** сегменты (включая реальные). Не путайте с [[fakedsplit]] / [[fakeddisorder]], где fooling идёт только на фейки.

Подробнее про то, почему порча заголовков у таких функций вредна, — в [[Zapret2/desync/multisplit#6. Fooling применяется ко ВСЕМ сегментам|нюансе 6 у multisplit]]; про сами опции — в [[Zapret2/ts-and-fooling|ts-and-fooling]].

### 7. Для однопакетных payload разницы нет

Если payload помещается в один TCP-сегмент (что характерно для большинства HTTP-запросов и многих TLS ClientHello без post-quantum), поведение `multidisorder_legacy` и нового [[multidisorder]] **идентично**. Разница проявляется только при многопакетных payload.

Проще говоря: разница между legacy и новым вариантом видна, только когда запрос состоит из нескольких пакетов. Если пакет один, подойдёт любой из двух.

### 8. Порядок вызова: каждый пакет --- отдельный вызов

В отличие от нового [[multidisorder]], который обрабатывает весь reasm за один `replay_first()` и дропает последующие replay через `replay_drop()`, legacy-функция вызывается для **каждого** пакета replay. Каждый вызов обрабатывает свой пакет независимо.

Как устроена развилка replay у нового варианта, см. [[Zapret2/desync/multidisorder#Поведение при replay / reasm|Поведение при replay / reasm]]. Проще говоря: legacy не «решает» за весь запрос, а отвечает за один пакет и забывает о нём.

### 9. seqovl может применяться к разным пакетам

Поскольку seqovl нормализуется попакетно, в теории seqovl может применяться к разным пакетам в разных сценариях. Но на практике маркер seqovl разрешается в одну конкретную позицию и попадает ровно в один пакет.

Проще говоря: механизм допускает, что `seqovl` попадёт в разные пакеты, но на деле его место — одно, поэтому и пакет один.

---

## Миграция с nfqws1

Этот раздел для тех, кто переносит настройки из первой версии zapret (программа `nfqws`) во вторую (`nfqws2`). В первой версии техника выбиралась ключом `--dpi-desync=…`, а её параметры — отдельными ключами `--dpi-desync-…`; во второй всё это записывается аргументами одного вызова `--lua-desync=multidisorder_legacy:…`. Именно для такого переноса legacy-вариант и создан.

### Когда использовать multidisorder_legacy

Ниже — короткая шпаргалка выбора между двумя вариантами; полное сравнение — в разделе [[#Сравнительная таблица: multidisorder vs multidisorder_legacy|Сравнительная таблица]].

| Сценарий | Рекомендация |
|:---------|:-------------|
| Миграция рабочего профиля nfqws1 с `--dpi-desync=multidisorder` | Используйте `multidisorder_legacy` для 100%-й совместимости |
| Новый профиль, однопакетный payload | Нет разницы, можно использовать [[multidisorder]] |
| Новый профиль, многопакетный payload | Попробуйте оба варианта --- DPI может реагировать по-разному |
| Нужен blob или nodrop | Используйте [[multidisorder]] (legacy не поддерживает) |

> [!note] Проще говоря
> Переносишь рабочий профиль nfqws1 — бери legacy. Пишешь новый профиль и запрос однопакетный — разницы нет. Запрос многопакетный — пробуй оба и смотри, как отреагирует DPI. Нужны `blob` или `nodrop` — только новый `multidisorder`.

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

В таблице слева — ключ первой версии, справа — то, как он записывается во второй.

| nfqws1 | nfqws2 (multidisorder_legacy) |
|:-------|:-------------------------------|
| `--dpi-desync=multidisorder` | `--lua-desync=multidisorder_legacy` |
| `--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` в инстансе |

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

В nfqws1 фейк и разрез задавались одним ключом `--dpi-desync=fake,multidisorder`. В nfqws2 каждая техника становится отдельным инстансом `--lua-desync` (инстанс — одна запись `--lua-desync=…` в профиле), а фейки для TLS и HTTP — разными инстансами под своими `--payload`, потому что им нужны разные [[Zapret2/desync/fake#Стандартные blob-ы|стандартные blob-ы]]:

```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

# nfqws2 (эквивалент с multidisorder_legacy):
nfqws2 \
  --payload=tls_client_hello \
    --lua-desync=fake:blob=fake_default_tls:tcp_md5 \
  --payload=http_req \
    --lua-desync=fake:blob=fake_default_http:tcp_md5 \
  --payload=tls_client_hello,http_req \
    --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=5:seqovl_pattern=0x1603030000
```

Обратите внимание, что `--dpi-desync-fooling=md5sig` из nfqws1 в nfqws2 записан как аргумент `tcp_md5` у `fake`, а не у `multidisorder_legacy`. В [[Zapret2/desync/multidisorder#Миграция с fake + multidisorder|Миграции с fake + multidisorder]] то же разобрано для нового варианта.

### Миграция с нового multidisorder на legacy

Если у вас уже есть профиль с новым [[multidisorder]] и вы хотите попробовать legacy-вариант:

```bash
# Было (новый multidisorder):
--lua-desync=multidisorder:pos=1,midsld:seqovl=midsld-1:blob=fake_ch:nodrop

# Стало (legacy) --- убираем blob и nodrop:
--lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=midsld-1
```

Проще говоря: `blob` и `nodrop` при переходе исчезают, а всё остальное остаётся.

**Внимание:** `blob` и `nodrop` потеряны при миграции. Если они необходимы --- legacy не подходит.

---

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

Каждый пример — готовая строка для [[Zapret2/profile#Анатомия профиля|профиля]]; под ней объяснено, что получится на выходе.

### 1. Минимальный (дефолт: pos=2, dir=out, payload=known)

```bash
--lua-desync=multidisorder_legacy
```

Разрезает payload после 2-го байта, отправляет в обратном порядке: сначала основная часть, потом первые 2 байта.

### 2. TLS: разрез посередине SNI

```bash
--payload=tls_client_hello --lua-desync=multidisorder_legacy:pos=midsld
```

SNI разрезан пополам. Сначала отправляется вторая половина (с конца SNI), затем первая (с начала ClientHello до середины SNI). DPI получает вторую половину раньше и не может собрать полный hostname.

### 3. TLS: два разреза с seqovl-маркером

```bash
--payload=tls_client_hello --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=midsld-1
```

Про `seqovl` как маркер — [[#seqovl --- маркер, а не число|раздел выше]].

3 сегмента в обратном порядке. seqovl привязан к позиции `midsld-1` --- фейковые данные перекрывают область SNI. Последний отправленный сегмент (1-й байт) переписывает буфер сокета реальными данными.

### 4. TLS: seqovl с паттерном маскировки под TLS record

```bash
--payload=tls_client_hello \
  --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=5:seqovl_pattern=0x1603030000
```

TLS record — заголовок записи TLS. Пять байт `0x16 0x03 0x03 0x00 0x00` из примера — паттерн, который DPI может принять за начало TLS record.

5 байт seqovl-области заполнены `0x16 0x03 0x03 0x00 0x00` --- DPI может принять это за начало TLS record.

### 5. HTTP: разрезы вокруг hostname

```bash
--payload=http_req --lua-desync=multidisorder_legacy:pos=host,midsld,endhost
```

4 сегмента: отправляются в обратном порядке (после hostname -> вторая половина hostname -> первая половина -> до hostname). DPI видит фрагменты в "неправильном" порядке.

### 6. Комбинация: fake + multidisorder_legacy

```bash
--payload=tls_client_hello \
  --lua-desync=fake:blob=fake_default_tls:tcp_md5 \
  --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=midsld-1:seqovl_pattern=0x1603030000
```

Отдельный пакет-фейк — работа функции [[Zapret2/desync/fake#Зачем нужен fake|fake]]; `tcp_md5` — способ испортить его так, чтобы сервер отбросил (см. [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]).

Сначала отправляется фейковый TLS ClientHello (с MD5 fooling --- сервер отбросит), затем реальный payload нарезан и отправлен в обратном порядке с seqovl.

### 7. Боевой пример для YouTube (TLS)

```bash
--filter-tcp=443 --hostlist=youtube.txt \
  --lua-desync=fake:blob=fake_default_tls:repeats=11:tcp_md5 \
  --lua-desync=multidisorder_legacy:pos=1,midsld
```

11 фейков подряд + реальный payload разрезан и отправлен в обратном порядке. Полная совместимость с поведением nfqws1.

### 8. Множественные разрезы с арифметикой маркеров

```bash
--payload=tls_client_hello \
  --lua-desync=multidisorder_legacy:pos=sniext+1,host,midsld,endhost-2,-10
```

5 маркеров -> до 6 сегментов (в зависимости от того, сколько маркеров попадут в текущий пакет после нормализации). Все 6 сегментов отправляются в обратном порядке внутри каждого пакета.

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

```bash
--payload=tls_client_hello \
  --lua-desync=multidisorder_legacy:pos=1,midsld:ipfrag:ipfrag_disorder:ipfrag_pos_tcp=32
```

Раздел с параметрами IP-фрагментации — [[#F) Standard ipfrag|F) Standard ipfrag]].

Каждый TCP-сегмент дополнительно фрагментируется на IP-уровне в обратном порядке. Двойной disorder: TCP-сегменты в обратном порядке внутри пакета + IP-фрагменты в обратном порядке.

### 10. С TCP timestamp и IP ID

```bash
--payload=tls_client_hello \
  --lua-desync=multidisorder_legacy:pos=1,midsld:tcp_ts_up:ip_id=seq:ip_id_conn
```

Разделы с параметрами: [[#D) Standard fooling|D) Standard fooling]] и [[#E) Standard ipid|E) Standard ipid]].

TCP timestamp поднят в начало заголовка, IP ID --- последовательные в рамках соединения.

### 11. Payload=all (обработка unknown протоколов)

```bash
--lua-desync=multidisorder_legacy:payload=all:pos=2
```

Обрабатывает **любой** payload, включая нераспознанные. Маркеры вроде `midsld` не разрешатся для unknown, но абсолютные позиции (как `2`) сработают.

### 12. С optional для защиты seqovl_pattern

```bash
--payload=tls_client_hello \
  --lua-desync=multidisorder_legacy:pos=1,midsld:seqovl=midsld-1:seqovl_pattern=my_blob:optional
```

Если blob `my_blob` не существует --- seqovl всё равно работает, но заполняется нулями вместо паттерна. Без `optional` отсутствие blob вызвало бы ошибку.

## 📚 См. также

- [[desync]] — обзор всех функций `--lua-desync` и общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты
- [[жизненный цикл desync-функции]] — восемь стадий, общих для всех техник дурения: отсев транспорта, выбор данных, replay, вердикты
- [[структура desync и диссекта]] — устройство таблицы `desync`, диссекта и приёма многопакетных запросов
- [[multidisorder]] — новый вариант: тот же обратный порядок, но по всему reasm, с поддержкой `blob` и `nodrop`
- [[multisplit]] — тот же разрез, но куски идут по порядку
- [[fakedsplit]] · [[fakeddisorder]] — разрез по одной позиции с подмешиванием фейковых сегментов
- [[fake]] — отдельный фейковый пакет, с которым `multidisorder_legacy` чаще всего комбинируют
- [[blob]] — как объявлять и передавать данные для аргумента `seqovl_pattern`
- [[payload]] — распознавание типов протоколов, от которого зависит работа относительных маркеров
- [[ts-and-fooling]] — опции fooling; в `multidisorder_legacy` осмысленны лишь немногие из них
- [[verify-strategy]] — как честно проверить, работает ли подобранная комбинация
- [[последовательность аргументов]] — как выстраивается цепочка инстансов в профиле
- [[profile]] — где `--lua-desync` живёт среди остальных настроек
- [[DPI/DPI|DPI и ТСПУ]] — как устроена инспекция трафика, против которой работает `multidisorder_legacy`

---

> **Источники:** `lua/zapret-antidpi.lua:530-684`, `lua/zapret-lib.lua`, `docs/manual.md:4098-4121` из репозитория zapret2.

---

> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/desync/multidisorder_legacy.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).
