todo/Zapret2/desync/fakedsplit.md
loop-uh baf0091fa3
Some checks failed
Published content check / validate (push) Failing after 17s
git.zapret.moe → git2.zapret.moe во всём хранилище, защита от возврата старых имён
git.zapret.moe в России заблокирован по имени, как и wiki.zapret.moe. Ссылки
на Forgejo во всех заметках, футерах, wizard.html и главной заменены на
git2.zapret.moe (включая закодированные адреса значков shields.io).

Какое имя рабочее, теперь решают реестры хоста /etc/zapret-*-domains.conf через
zapret-site-names: pre-commit хук vault отклоняет добавленные строки с
заблокированным именем, сборка Quartz сама переводит ссылки и baseUrl на
рабочие имена. Правило в AGENTS.md переписано под этот механизм.

scripts/wiki-stats.py: переходы с любых имён вики и git (wikiN, gitN) больше
не считаются внешними сайтами. content-check: проверка зеркал сторонних
проектов узнаёт любое имя git.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:51:32 -04:00

1259 lines
116 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
- fakedsplit
- fake
- seqovl
aliases:
- fakedsplit
description: "Техника fakedsplit в zapret2/nfqws2: разрез TCP payload с фейковыми сегментами-«ретрансмиссиями», маркеры pos, seqovl, fooling и примеры команд."
---
> [!mirror] Резервное зеркало
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Zapret2/desync/fakedsplit](https://wiki2.zapret.moe/Zapret2/desync/fakedsplit)
# `fakedsplit` — 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]]. Когда один и тот же кусок отправляется второй раз с тем же номером seq, в TCP это называется ретрансмиссией — повторной отправкой (её делают, когда считают, что первая копия потерялась).
Теперь о том, что такое фейк. Фейк — пакет-обманка: он выглядит как часть соединения, но вместо настоящих данных в нём мусор (по умолчанию нулевые байты). Если бы сервер принял такой пакет как настоящий, вместо запроса он получил бы мусор, и TCP-поток сломался бы ([[#4. Fooling ОБЯЗАТЕЛЕН для фейков|нюанс 4]]). Поэтому фейк «портят»: меняют в его заголовке поле так, чтобы TCP-стек сервера молча выбросил пакет. Такая порча называется fooling («одурачивание»; разбор — [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). В командах этой статьи портится поле ACK (номер подтверждения: до какого байта собеседника отправитель уже всё получил): `tcp_ack=-66000` сдвигает его на 66000, и сервер такой пакет не принимает. Вторая опция, `tcp_ts_up`, переставляет TCP-опцию timestamp в начало заголовка: на Linux-серверах пакет с неверным ACK надёжно отбрасывается только при таком порядке опций ([[#5. tcp_ts_up рекомендуется вместе с tcp_ack/tcp_seq|нюанс 5]]). Есть и другие способы испортить фейк (TTL, контрольная сумма, MD5-опция), их каталог — в разделе [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|Как заставить fake умереть]].
`fakedsplit` перехватывает исходящий пакет с ClientHello или HTTP-запросом и делает с ним три шага. Сначала режет данные ровно в одном месте (аргумент `pos`, по умолчанию после второго байта) на две настоящие части. Потом отправляет шесть пакетов подряд: фейк первой части, настоящую первую часть, ещё один фейк первой части, фейк второй части, настоящую вторую часть и ещё один фейк второй части. Каждый фейк — того же размера, что и его часть, и с тем же seq, то есть претендует на то же место в потоке. Испорчены только фейки: настоящие части уходят с правильными заголовками (из всей порчи к ним добавляется лишь безвредная `tcp_ts_up`; исключение — `seqovl` первой части, о нём ниже). В конце перехваченный оригинал выбрасывается, чтобы те же данные не ушли второй раз. Проще говоря: вокруг каждой настоящей части стоят два фейка того же размера. Порядок и опции каждого из шести пакетов — в разделе [[#Последовательность отправки (6 пакетов)|Последовательность отправки (6 пакетов)]].
Сервер выбрасывает все четыре фейка из-за испорченного ACK, принимает две настоящие части, раскладывает их по seq и получает исходный запрос целиком, поэтому отвечает как обычно. ТСПУ видит другое: на каждое место в потоке приходится по три сегмента с одним и тем же seq, и снаружи это похоже на серию ретрансмиссий. Какой из трёх сегментов настоящий, DPI может не понять. К тому же в фейках вместо имени сайта по умолчанию нули, а сама настоящая часть содержит имя не целиком, потому что разрез проходит внутри него (при `pos=midsld` — посередине домена второго уровня, главной части имени сайта: у `www.example.com` это `example`). Это расчёт, а не гарантия: подробнее о том, что видит DPI и что видит сервер, — в разделах [[#Что видит DPI|Что видит DPI]] и [[#Что видит сервер|Что видит сервер]].
Команда из быстрого старта `fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up` режет ClientHello посередине домена второго уровня и окружает каждую настоящую часть двумя фейками того же размера и с тем же seq: FAKE1, REAL1, FAKE1, FAKE2, REAL2, FAKE2. Фейки заполнены мусором (на схеме — `X`). ТСПУ получает по три сегмента на каждую позицию и может не понять, какой из них настоящий; сервер отбрасывает фейки по испорченному номеру подтверждения (ACK сдвинут на 66000). Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.
![[desync-anim-fakedsplit.svg|Анимация fakedsplit: каждая часть ClientHello окружена фейками с тем же seq, сервер отбрасывает фейки по неверному ACK]]
Минимальная команда включает `fakedsplit` со значениями по умолчанию: один разрез после второго байта (`pos=2`), обработка только распознанных протоколов (`payload=known`) и только исходящих пакетов (`dir=out`), а `tcp_ack=-66000` портит фейки. Строку `--lua-desync=…` пишут в [[Zapret2/profile#Анатомия профиля|профиль]] — набор правил Zapret 2 для определённого трафика:
```bash
--lua-desync=fakedsplit:tcp_ack=-66000
```
Без хотя бы одного способа испортить фейки сервер примет их за настоящие данные и поток сломается, а `tcp_ts_up` рекомендуется ставить вместе с `tcp_ack` всегда. Для HTTPS берут вариант с ClientHello, как в команде со схемы: `--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up`; здесь `--payload=tls_client_hello` значит «срабатывать только на TLS ClientHello». К разрезу можно добавить `seqovl` — несколько лишних байт слева от первой настоящей части, которые сервер выбросит, а DPI может прочитать вместе с остальным сегментом ([[#seqovl — скрытый фейк внутри сегмента|подробно]]). Фейки можно отключать по одному флагами `nofake1`…`nofake4` ([[#nofake1..nofake4 — отключение отдельных фейков|подробно]]), а начинку фейков задаёт аргумент `pattern` ([[#pattern — заполнение фейков|подробно]]). Остальные готовые команды собраны в разделах [[#Быстрый старт|Быстрый старт]] и [[#Практические примеры|Практические примеры]].
Замысел `fakedsplit` — обойти два известных слабых места отдельных приёмов: отдельный фейк-пакет DPI может отбросить по эвристикам (слишком другой TTL, плохой seq), а простой разрез он может просто собрать обратно. `fakedsplit` соединяет оба приёма, и фейки в нём устроены как повторы настоящих сегментов ([[#Зачем нужен fakedsplit|Зачем нужен fakedsplit]]). Сработает ли это против конкретного DPI, заранее не сказать. На случай, когда DPI всё же научился отбрасывать фейки, в статье предусмотрен `seqovl`: его лишние байты внутри настоящего сегмента DPI может не распознать. Работает техника только с TCP, а надёжность порчи фейков зависит от места запуска: один и тот же вид fooling бывает отличным в одном месте запуска (например, на роутере) и бесполезным в другом (за роутером) ([[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталог способов]]). Цена — до шести пакетов вместо одного, а `nofake2` и `nofake4` сокращают их до четырёх ([[#8. Без "задних" фейков (оптимизация трафика)|пример 8]]). Способ порчи фейков выбирают по месту запуска и подтверждают тестами, а не на слух ([[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталог способов]]); результат проверяют по [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листу честной проверки]].
У `fakedsplit` есть близкие родственники. [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]] режет так же, но фейков не подмешивает и позиций может быть сколько угодно. [[Zapret2/desync/fake#Зачем нужен fake|fake]] отправляет отдельный фейк-пакет перед настоящим и не трогает оригинал. [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] делает такой же один разрез с фейками вокруг частей, но первой отправляет вторую часть, то есть конец запроса раньше начала; сам приём обратного порядка разобран в [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|multidisorder]], где фейков нет. [[Zapret2/desync/hostfakesplit#Зачем нужен hostfakesplit|hostfakesplit]] режет запрос по границам имени хоста и подмешивает фейковое имя. Все отличия сведены в таблицу раздела [[#Отличия от других функций сегментации|Отличия от других функций сегментации]]; функцию `fake` можно поставить в одну цепочку с `fakedsplit` ([[#11. Цепочка: fake + fakedsplit|пример 11]]).
---
## Оглавление
- [Коротко и по-простому](#коротко-и-по-простому)
- [Справка: где функция в коде и родственники](#справка-где-функция-в-коде-и-родственники)
- [Зачем нужен fakedsplit](#зачем-нужен-fakedsplit)
- [Быстрый старт](#быстрый-старт)
- [Откуда берутся данные для нарезки](#откуда-берутся-данные-для-нарезки)
- [Маркер позиции (pos)](#маркер-позиции-pos)
- [Типы маркеров](#типы-маркеров)
- [Относительные маркеры](#относительные-маркеры)
- [Арифметика маркеров](#арифметика-маркеров)
- [Как маркер разрешается в коде](#как-маркер-разрешается-в-коде)
- [Важные нюансы pos](#важные-нюансы-pos)
- [Последовательность отправки (6 пакетов)](#последовательность-отправки-6-пакетов)
- [ASCII-диаграмма](#ascii-диаграмма)
- [Что видит DPI](#что-видит-dpi)
- [Что видит сервер](#что-видит-сервер)
- [Два набора опций: opts_orig vs opts_fake](#два-набора-опций-opts_orig-vs-opts_fake)
- [seqovl — скрытый фейк внутри сегмента](#seqovl--скрытый-фейк-внутри-сегмента)
- [Принцип работы seqovl](#принцип-работы-seqovl)
- [seqovl_pattern](#seqovl_pattern)
- [pattern — заполнение фейков](#pattern--заполнение-фейков)
- [nofake1..nofake4 — отключение отдельных фейков](#nofake1nofake4--отключение-отдельных-фейков)
- [Полный список аргументов](#полный-список-аргументов)
- [A) Собственные аргументы fakedsplit](#a-собственные-аргументы-fakedsplit)
- [B) Standard direction](#b-standard-direction)
- [C) Standard payload](#c-standard-payload)
- [D) Standard fooling](#d-standard-fooling)
- [E) Standard ipid](#e-standard-ipid)
- [F) Standard reconstruct](#f-standard-reconstruct)
- [G) Standard rawsend](#g-standard-rawsend)
- [Порядок отправки — подробно с seqovl](#порядок-отправки--подробно-с-seqovl)
- [Поведение при replay / reasm](#поведение-при-replay--reasm)
- [Автосегментация по MSS](#автосегментация-по-mss)
- [Место в общем скелете](#место-в-общем-скелете)
- [Псевдокод алгоритма](#псевдокод-алгоритма)
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
- [Отличия от других функций сегментации](#отличия-от-других-функций-сегментации)
- [Миграция с nfqws1](#миграция-с-nfqws1)
- [Практические примеры](#практические-примеры)
- [📚 См. также](#-см-также)
---
## Справка: где функция в коде и родственники
Паспорт функции для тех, кто сверяется с исходниками zapret2: в каком файле и на какой строке лежит код, какой ключ первой версии zapret она заменяет и как объявлена.
**Файл:** `lua/zapret-antidpi.lua:826`
**nfqws1 эквивалент:** `--dpi-desync=fakedsplit`
**Сигнатура:** `function fakedsplit(ctx, desync)`
`fakedsplit` разрезает TCP payload на **две** части по одной позиции и отправляет их вперемешку с **фейковыми** сегментами того же размера. Фейки содержат мусор (pattern), но имеют те же TCP sequence и размеры, что и оригиналы. Для DPI это выглядит как серия ретрансмиссий — он не может понять, какой сегмент настоящий, а какой — подделка. Для сервера фейки отбрасываются благодаря fooling (badseq, md5sig, badsum и т.д.).
> [!note] Проще говоря
> Payload — полезные данные пакета без сетевых заголовков: сам HTTP-запрос или сам ClientHello. Pattern — заполнитель, которым набивают фейки; TCP sequence — тот самый порядковый номер seq. Fooling — намеренная порча заголовков фейка, чтобы сервер его отбросил ([[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). Seqovl — приём с лишними байтами слева от первой настоящей части ([[#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]]).
Родственные функции: [[multisplit]] (простая сегментация), [[multidisorder]] (обратный порядок), [[fakeddisorder]] (фейки + обратный порядок), [[hostfakesplit]] (по hostname), [[tcpseg]] (диапазон), [[oob]] (urgent byte).
> [!note] Проще говоря
> Фейки есть не у всех родственников: у [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]] их нет совсем, у [[Zapret2/desync/fake#Зачем нужен fake|fake]] это единственный пакет, а `fakedsplit` и [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] замешивают их между частями. Полная таблица различий — в разделе [[#Отличия от других функций сегментации|Отличия от других функций сегментации]].
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `fakedsplit` от этого скелета отличается.
---
## Зачем нужен fakedsplit
Обычная сегментация ([[multisplit]]) разрезает payload, но DPI может просто реассемблировать поток. Отдельная отправка фейка ([[fake]]) работает, но DPI может отбросить фейк по эвристикам (слишком другой TTL, плохой sequence).
`fakedsplit` комбинирует оба подхода: он **одновременно** разрезает и замешивает фейки. При этом фейки:
1. **Совпадают по размеру** с реальными частями
2. **Имеют те же TCP sequence numbers** — выглядят как ретрансмиссии
3. **Окружают** каждую реальную часть с двух сторон (до и после)
Для DPI поток выглядит так: 6 TCP-сегментов, из которых по 3 сегмента (две тройки) — "ретрансмиссии" одного и того же. Какой из трёх сегментов каждой тройки настоящий? DPI не знает. Сервер знает — потому что фейки имеют невалидные заголовки (fooling), и TCP-стек их молча отбросит.
> [!note] Проще говоря
> Реассемблировать поток — склеить сегменты обратно в исходные данные; так может поступать DPI, который следит за всем соединением, а не за отдельными пакетами. Ретрансмиссия — повторная отправка того же куска с тем же номером seq: TCP так делает, когда считает, что первая отправка потерялась. `fakedsplit` рисует для DPI такую картину нарочно: на каждое из двух мест в потоке приходится по три сегмента с одинаковым seq (фейк, настоящая часть, фейк). Сервер отличает настоящий, потому что фейки он выбрасывает из-за испорченных заголовков; DPI этого различия может не увидеть. Отдельный фейк-пакет разобран в [[Zapret2/desync/fake#Зачем нужен fake|fake]], разрез без фейков — в [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]], а их сочетание с обратным порядком — в [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]].
---
## Быстрый старт
Ниже четыре готовые строки для [[Zapret2/profile#Анатомия профиля|профиля]] Zapret 2, от самой короткой к более сложным (полный набор — в разделе [[#Практические примеры|Практические примеры]]). Флаг `--payload=…` работает как фильтр для всех вызовов `--lua-desync`, которые идут после него (до следующего `--payload` или до конца профиля): они срабатывают только на данных указанного [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|типа payload]]. Поэтому `--payload` пишут перед `--lua-desync`, а не после (подробно — в [[последовательность аргументов]]).
Минимально (разрез по позиции 2, payload=known, dir=out):
```bash
--lua-desync=fakedsplit:tcp_ack=-66000
```
Здесь `payload=known` означает «только распознанные протоколы», а `dir=out` — «только исходящие пакеты»; оба аргумента разобраны в разделах [[#B) Standard direction|B) Standard direction]] и [[#C) Standard payload|C) Standard payload]]. `tcp_ack=-66000` портит ACK у фейков, чтобы сервер их выбросил. В этой строке `tcp_ts_up` нет, хотя статья рекомендует ставить его вместе с `tcp_ack` ([[#5. tcp_ts_up рекомендуется вместе с tcp_ack/tcp_seq|нюанс 5]]).
Типовой TLS-разрез с badseq:
```bash
--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up
```
Функция срабатывает только на TLS ClientHello и режет его посередине домена второго уровня (SLD; у `www.example.com` это `example`): каждая из двух частей окружена фейками, всего шесть пакетов. `tcp_ts_up` нужен, чтобы сервер надёжно отбрасывал фейки с испорченным ACK.
HTTP с разрезом по методу:
```bash
--payload=http_req --lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
Здесь `--payload=http_req` значит «только HTTP-запросы», а `method+2` — две позиции после начала метода: `GET` разрежется после `GE`.
С seqovl для дополнительной маскировки:
```bash
--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:seqovl=5:seqovl_pattern=0x1603030000
```
Здесь к первой настоящей части слева приклеиваются 5 байт `0x1603030000`, похожих на начало TLS record — заголовка записи TLS. Сервер отбросит эти байты, DPI — может не отбросить; как это работает, разобрано в разделе [[#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]].
---
## Откуда берутся данные для нарезки
Внутри `fakedsplit` данные (`data`) выбираются в следующем порядке приоритетов:
```
1. blob_or_def(desync, desync.arg.blob) — если задан blob= и он существует
2. desync.reasm_data — если есть реассемблированные данные (multi-packet payload)
3. desync.dis.payload — текущий пакет (fallback)
```
**Следствие:** все маркеры `pos`, `seqovl` и прочие аргументы применяются именно к тем данным, которые реально выбраны. Если вы задали `blob=myblob`, маркеры вроде `midsld` будут работать только если `myblob` содержит валидный TLS/HTTP payload, который zapret может распознать.
> [!note] Проще говоря
> Функция сначала спрашивает: «мне явно подсунули blob? — режу его». Если нет: «есть payload, собранный из нескольких пакетов? — режу его». Если и его нет: «режу тело текущего пакета». Blob — заранее заготовленные данные, которые можно подставить вместо настоящих (что это такое и как их задавать — в заметке [[blob]]). Reasm (`reasm_data`) — тот же запрос, собранный движком из нескольких TCP-пакетов, если в один он не поместился ([[#Поведение при replay / reasm|Поведение при replay / reasm]]). Payload — полезные данные пакета без сетевых заголовков. Та же цепочка общая для большинства функций дурения и описана как [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload|стадия 4 жизненного цикла]]. Распознанный тип данных (`http_req`, `tls_client_hello`) объяснён в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|о типах payload]]: от него зависит, сработают ли относительные маркеры.
---
## Маркер позиции (pos)
![[desync-anim-hub-markers.svg|Анимация: маркеры позиций pos на HTTP-запросе и на TLS ClientHello, разрез по pos=host,midsld,endhost]]
`pos` — главный аргумент `fakedsplit`. Определяет **где** внутри payload будет произведён разрез. В отличие от [[multisplit]], `pos` — это **один маркер**, а не список. Payload всегда делится ровно на **две части**.
Маркер — способ указать место разреза: либо числом байт, либо названием места в запросе (например, «середина домена»). В командной строке числа считаются с нуля: `pos=N` значит «первые N байт уходят отдельным сегментом». Внутри Lua те же позиции хранятся как индексы с единицы, отсюда и два нюанса ниже. Список маркеров через запятую и полную таблицу маркеров разбирает статья о multisplit ([[Zapret2/desync/multisplit#Маркеры позиций (pos)|Маркеры позиций (pos)]]); здесь то же самое сказано применительно к одному маркеру.
### Типы маркеров
| Тип | Описание | Пример |
|:----|:---------|:-------|
| **Абсолютный положительный** | Смещение от начала payload, считая с 0. `pos=N` → первые N байт становятся отдельным сегментом | `2`, `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. 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 (абсолютный, не относительный)
```
### Как маркер разрешается в коде
Разрешить маркер — значит превратить его название в конкретный номер байта в выбранных данных. В отличие от [[multisplit]], который вызывает `resolve_multi_pos` (парсит список через запятую; как это устроено, описано в разделе [[Zapret2/desync/multisplit#Как маркеры разрешаются в коде|Как маркеры разрешаются в коде]]), `fakedsplit` вызывает **`resolve_pos`** напрямую — для одного маркера:
```lua
local pos = resolve_pos(data, desync.l7payload, spos)
```
Функция `resolve_pos`:
1. Разбирает маркер (имя + арифметика)
2. Если маркер не может быть разрешён (например, `midsld` для `unknown` payload) — возвращает `nil`
3. При успехе возвращает абсолютную позицию (1-based, как в Lua)
Далее проверяется:
```lua
if pos == 1 then
DLOG("fakedsplit: split pos resolved to 0. cannot split.")
-- ничего не делает
end
```
> [!note] Проще говоря
> Функция превращает маркер в номер байта. Если превратить не получилось (например, `midsld` для `unknown` payload — данных, тип которых движок не распознал) или получился разрез перед самым первым байтом, разрезать нечего, и `fakedsplit` ничего не отправляет. Что в этом случае происходит с оригинальным пакетом, сказано в [[#11. Неразрешённый маркер != ошибка|нюансе 11]].
### Важные нюансы pos
- **Только ОДИН маркер.** `pos=midsld,endhost` НЕ работает. Запятая не парсится — это не список. Если нужно несколько точек разреза с фейками — используйте цепочку нескольких `fakedsplit`
- **Разрез в самом начале данных отбрасывается.** Если маркер разрешается в Lua-позицию 1 — а это маркер `0`, поскольку в командной строке смещения считаются с нуля, — функция логирует "split pos resolved to 0. cannot split." и ничего не делает. Под запрет попадает `pos=0`, а не `pos=1`; частая жертва правила — `pos=method` для HTTP, где метод лежит на смещении 0
- **Неразрешимый маркер => ничего не делается.** Если `resolve_pos` вернул `nil` (маркер не разрешился), fakedsplit логирует "cannot resolve pos" и пропускает
- **По умолчанию `pos=2`.** Если `pos` не задан, payload делится на 2 части: первые 2 байта отдельным сегментом, весь остаток — вторым
> [!note] Проще говоря
> В командной строке пишите, сколько байт должно уйти в первом куске. `pos=0` не сработает, и `pos=method` для HTTP тоже: метод лежит на смещении 0, поэтому нужен сдвиг, например `pos=method+2`. Тот же разрез по числу байт удобнее всего проверять на примерах в разделе [[#Порядок отправки — подробно с seqovl|Порядок отправки — подробно с seqovl]].
---
## Последовательность отправки (6 пакетов)
![[desync-anim-hub-fakedsplit6.svg|Анимация: шесть пакетов fakedsplit — две тройки, в каждой настоящая часть зажата между фейками]]
`fakedsplit` отправляет **до 6 пакетов** (при включённых всех фейках). В таблице пакеты перечислены в порядке отправки. Столбец «Тип» показывает, что это: фейк (fake) или настоящая часть (orig, «оригинальная»). Столбец «Отключение» — флаг, которым пакет можно не отправлять ([[#nofake1..nofake4 — отключение отдельных фейков|nofake1..nofake4]]); у настоящих частей отключения нет. Столбец «Опции» — какой из двух наборов настроек отправки применяется к пакету ([[#Два набора опций: opts_orig vs opts_fake|Два набора опций]]):
| # | Что | Тип | Отключение | Опции |
|:--|:----|:----|:-----------|:------|
| 1 | Фейк 1-й части | fake | `nofake1` | `opts_fake` (полный fooling + reconstruct + repeats) |
| 2 | Реальная 1-я часть (+seqovl) | orig | -- | `opts_orig` (только tcp_ts_up, без repeats) |
| 3 | Фейк 1-й части (повтор) | fake | `nofake2` | `opts_fake` |
| 4 | Фейк 2-й части | fake | `nofake3` | `opts_fake` |
| 5 | Реальная 2-я часть | orig | -- | `opts_orig` |
| 6 | Фейк 2-й части (повтор) | fake | `nofake4` | `opts_fake` |
**Ключевой момент:** каждая реальная часть **окружена** фейками того же размера и с тем же TCP sequence. DPI видит "тройки ретрансмиссий" и не может определить, какая из трёх — настоящая.
> [!note] Проще говоря
> Тройка — это три сегмента с одним seq: фейк, настоящая часть, ещё один фейк. Для первой части это пакеты №1–№3, для второй — №4–№6. Настоящая часть всегда стоит в середине тройки, а фейк идёт и до неё, и после. Что такое ретрансмиссия и почему тройка выглядит для DPI как повторы, — в разделе [[#Зачем нужен fakedsplit|Зачем нужен fakedsplit]].
### ASCII-диаграмма
| # | Пакет | seq | len | Опции |
|:--|:--|:--|:--|:--|
| 1 | FAKE1 | `0` | `pos-1` | `opts_fake` |
| 2 | REAL1 | `-seqovl` | `pos-1+sovl` | `opts_orig` |
| 3 | FAKE1 | `0` | `pos-1` | `opts_fake` |
| 4 | FAKE2 | `pos-1` | `#data-pos+1` | `opts_fake` |
| 5 | REAL2 | `pos-1` | `#data-pos+1` | `opts_orig` |
| 6 | FAKE2 | `pos-1` | `#data-pos+1` | `opts_fake` |
FAKE — мусор (pattern), REAL — настоящие данные.
Номера seq здесь — смещения от начала данных: у первой части это `0`, у второй записано `pos-1` (в нём `pos` — позиция в нумерации Lua, с единицы). Колонки `len` показывают длину данных пакета (`sovl` — то же, что `seqovl`), а `opts_orig` и `opts_fake` — какой набор опций применён к пакету.
### Что видит DPI
DPI видит три сегмента с одним seq в начале запроса и три с одним seq в начале второй части. Это похоже на ретрансмиссии, и какой сегмент в каждой тройке настоящий, неясно.
Начало второй части в таблице выше и в псевдокоде равно `pos-1` (в примерах ниже при `pos=4` это `seq=3`); `PPP` — байты `seqovl_pattern`, которые есть только при заданном `seqovl`, `sovl` — число этих байт. Это картина «может не понять», а не гарантия; если DPI научился отбрасывать фейковые сегменты, на помощь приходит `seqovl` ([[#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]]).
### Что видит сервер
| seq | Данные | Что делает сервер |
|:--|:--|:--|
| `0` | мусор | отбрасывает: fooling (badseq, md5, badsum) |
| `-sovl` | PPP + REAL1 | часть seqovl левее окна отбрасывает, REAL1 принимает |
| `0` | мусор | отбрасывает: fooling |
| `pos` | мусор | отбрасывает: fooling |
| `pos` | REAL2 | принимает: нормальный пакет |
| `pos` | мусор | отбрасывает: fooling |
Сервер собрал `[REAL1][REAL2]` — полный исходный payload.
Слова «за window» здесь значат «левее границы окна приёма»: окно — диапазон номеров seq, которые сервер готов принять, а всё левее считается повтором и выбрасывается (подробно — [[Zapret2/desync/multisplit#Принцип работы seqovl|Принцип работы seqovl]]). Фейки сервер выбрасывает целиком из-за fooling: например, при `tcp_ack=-66000` ACK в них заведомо неверный. Проще говоря: сервер выбрасывает пакеты с испорченными заголовками, а принятые куски склеивает по seq.
---
## Два набора опций: opts_orig vs opts_fake
Это ключевое отличие `fakedsplit` от [[multisplit]]. Внутри функции создаются **два набора** опций.
Опции здесь — настройки того, как строится и отправляется пакет: какие поля заголовков портить (fooling), портить ли контрольную сумму (`badsum`; reconstruct — сборка готового «сырого» пакета со всеми заголовками), сколько раз повторять отправку (`repeats`), какие IP ID ставить (`ipid`), делить ли пакет на IP-фрагменты (`ipfrag`). Rawsend — отправка готовых пакетов в сеть самой функцией. Общий разбор того, кто применяет какие наборы, — в разделе [[Zapret2/жизненный цикл desync-функции#Наборы опций: кто их применяет и к чему|Наборы опций: кто их применяет и к чему]], а суть fooling — в [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]].
```lua
-- Для оригинальных частей:
local opts_orig = {
rawsend = rawsend_opts_base(desync), -- БЕЗ repeats!
reconstruct = {}, -- пустой (без badsum)
ipfrag = {}, -- пустой (без IP-фрагментации)
ipid = desync.arg, -- ip_id применяется
fooling = {tcp_ts_up = desync.arg.tcp_ts_up} -- ТОЛЬКО tcp_ts_up
}
-- Для фейковых частей:
local opts_fake = {
rawsend = rawsend_opts(desync), -- С repeats!
reconstruct = reconstruct_opts(desync), -- badsum если задан
ipfrag = {}, -- пустой (без IP-фрагментации)
ipid = desync.arg, -- ip_id применяется
fooling = desync.arg -- ВСЁ fooling целиком
}
```
**Сводная таблица:**
| Что | opts_orig (оригиналы) | opts_fake (фейки) |
|:----|:----------------------|:-------------------|
| **fooling** | Только `tcp_ts_up` | Всё (tcp_ack, tcp_seq, tcp_md5, ip_ttl, badseq, ...) |
| **reconstruct** | Нет (пустой `{}`) | Да (`badsum` если задан) |
| **rawsend.repeats** | Нет (`rawsend_opts_base`) | Да (`rawsend_opts`) |
| **rawsend.ifout** | Да | Да |
| **rawsend.fwmark** | Да | Да |
| **ipid** | Да | Да |
| **ipfrag** | Нет (пустой `{}`) | Нет (пустой `{}`) |
**Почему так:**
- Оригиналы **должны** быть приняты сервером => никакого fooling (кроме безвредного tcp_ts_up), никакого badsum
- Фейки **должны** быть отброшены сервером => полный fooling + badsum
- `repeats` — множественная отправка имеет смысл только для фейков (больше шума для DPI)
- `ipfrag` **не используется** ни для чего — fakedsplit работает исключительно на уровне TCP-сегментов
- `ipid` и базовый `rawsend` (ifout, fwmark) нужны обоим — это уровень IP/маршрутизации
> [!note] Проще говоря
> Настоящие части уходят почти чистыми: из всей порчи заголовков к ним добавляется только безвредная перестановка опции timestamp (`tcp_ts_up`). На фейки навешивается всё сразу: испорченные заголовки, при желании испорченная контрольная сумма и повторы. Отсюда правило из нюансов: `repeats` действует только на фейки ([[#6. repeats применяются ТОЛЬКО к фейкам|нюанс 6]]), а `ipfrag` не используется вовсе ([[#7. ipfrag НЕ используется|нюанс 7]]).
---
## seqovl — скрытый фейк внутри сегмента
**seqovl** (Sequence Overlap) — техника скрытого замешивания фейковых данных в реальный TCP-сегмент через манипуляцию TCP sequence number. В `fakedsplit` seqovl применяется **только к первому реальному сегменту** (пакет #2).
Подробная механика (как получатель решает, какие байты принять, и раскладка по байтам) разобрана в статье о multisplit: [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]], а сравнение с обычным fooling — в разделе [[Zapret2/desync/multisplit#Зачем seqovl лучше обычного fooling|Зачем seqovl лучше обычного fooling]]. Здесь то же самое сказано применительно к `fakedsplit`, где seqovl добавляется поверх уже имеющихся фейков. В [[Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие|multidisorder]] приём с тем же названием устроен иначе, поэтому переносить знания между функциями нужно осторожно.
### Принцип работы seqovl
![[desync-anim-hub-seqovl.svg|Анимация: seqovl дописывает слева паттерн и сдвигает seq на 990; DPI читает сегмент целиком, сервер выбрасывает байты левее 1000]]
```
Без seqovl:
TCP seq: 0
Данные: [РЕАЛЬНАЯ_ЧАСТЬ_1]
Сервер: принимает [РЕАЛЬНАЯ_ЧАСТЬ_1] целиком
С seqovl=10:
TCP seq: -10 (уменьшен на 10)
Данные: [PATTERN_10_БАЙТ][РЕАЛЬНАЯ_ЧАСТЬ_1]
Что видит DPI:
Единый TCP-сегмент начиная с seq -10.
DPI анализирует весь блок, включая PATTERN.
Если PATTERN содержит ложный SNI — DPI может принять его за настоящий.
Что видит сервер (TCP-стек):
TCP window начинается с seq 0.
Байты -10..-1 выходят за левую границу window => отбрасываются.
Байты с 0 (РЕАЛЬНАЯ_ЧАСТЬ_1) => принимаются.
```
**Визуализация:**
```
TCP window boundary
|
| ОТБРОСИТЬ | ПРИНЯТЬ |
| PATTERN(10) | РЕАЛЬНАЯ_ЧАСТЬ_1 |
^seq=-10 ^seq=0
```
> [!note] Проще говоря
> Перед настоящей первой частью стоят лишние байты, а seq сдвинут назад ровно на их число. Сервер держит окно приёма: он принимает номера, начиная с того, который ждёт следующим, а всё левее выбрасывает как дубликат или мусор. Поэтому лишние байты пропадают, а настоящая часть принимается. DPI видит один цельный сегмент вместе с лишними байтами.
**seqovl в fakedsplit — дополнительный уровень защиты** поверх фейков. Даже если DPI научился отбрасывать фейковые сегменты, seqovl-данные внутри реального сегмента он может не распознать.
**Важно:** в `fakedsplit` `seqovl` — только **число** (не маркер). Маркеры поддерживаются в [[fakeddisorder]] и [[multidisorder]], но не здесь. Как задаётся seqovl-маркер там, разобрано в разделах [[Zapret2/desync/fakeddisorder#seqovl — маркер, а не число|seqovl — маркер, а не число]] и [[Zapret2/desync/multidisorder#seqovl как маркер|seqovl как маркер]].
### seqovl_pattern
Паттерн, которым заполняется seqovl-область (N байт слева от реальных данных). По умолчанию — `0x00` (нули).
В `fakedsplit` `seqovl_pattern` — это **имя blob**. Паттерн повторяется до нужной длины `seqovl`. Blob можно записать прямо в команде шестнадцатеричной строкой (inline hex, `0x…`) или заранее объявить флагом `--blob` и сослаться на него по имени; подробно — в заметке [[blob]].
```bash
# Inline hex blob (маскировка под начало TLS record)
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:seqovl=5:seqovl_pattern=0x1603030000
# Предзагруженный blob
--blob=tlspat:0x1603030100 \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:seqovl=8:seqovl_pattern=tlspat
```
Если `optional` задан и blob `seqovl_pattern` отсутствует — используется нулевой паттерн (seqovl не отменяется). Сам флаг описан в разделе [[#A) Собственные аргументы fakedsplit|A) Собственные аргументы fakedsplit]].
---
## pattern — заполнение фейков
Аргумент `pattern` задаёт содержимое фейковых сегментов. Это имя blob, из которого генерируется паттерн для заполнения фейков. Blob — заранее заготовленные данные под именем (про них — заметка [[blob]]); про фейки как таковые — в разделе [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]].
```lua
fakepat = desync.arg.pattern and blob(desync, desync.arg.pattern) or "\x00"
```
- По умолчанию: `\x00` (нулевые байты)
- Фейк первой части: `pattern(fakepat, 1, pos-1)` — паттерн со смещением 1, длиной pos-1
- Фейк второй части: `pattern(fakepat, pos, #data-pos+1)` — паттерн со смещением pos, длиной остатка
Смещение паттерна (`pos`) соответствует смещению TCP sequence отсылаемой части. Это значит, что если DPI сравнивает байты фейка и оригинала побайтово — он увидит разные данные в одних и тех же позициях. Но если он просто реассемблирует поток — паттерн не сможет "обмануть" конкретную сигнатуру (для этого нужен осмысленный blob).
> [!note] Проще говоря
> По умолчанию фейки набиты нулями. Если нужно, чтобы фейк был похож на что-то осмысленное, задайте `pattern` с подходящим blob: паттерн повторяется до нужной длины фейка, а его смещение совпадает с местом фейка в потоке. Сигнатурой в этих словах названа заранее известная DPI строка данных, на которую он реагирует; нулями такую строку не сымитировать. Готовый пример с HTTP-подобным заполнением — [[#5. С кастомным паттерном для фейков|пример 5]].
```bash
# Фейки заполнены случайными данными из blob
--blob=rndpat:0xDEADBEEFCAFEBABE \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:pattern=rndpat
# Фейки заполнены нулями (по умолчанию)
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000
```
---
## nofake1..nofake4 — отключение отдельных фейков
Каждый из 4 фейковых пакетов можно отключить индивидуально. Номер флага — номер фейка в порядке отправки: `nofake1` и `nofake2` относятся к первой части (до неё и после), `nofake3` и `nofake4` — ко второй:
| Флаг | Отключает | Позиция в потоке |
|:-----|:----------|:-----------------|
| `nofake1` | Фейк 1-й части (до реальной 1-й) | #1 |
| `nofake2` | Фейк 1-й части (после реальной 1-й) | #3 |
| `nofake3` | Фейк 2-й части (до реальной 2-й) | #4 |
| `nofake4` | Фейк 2-й части (после реальной 2-й) | #6 |
Минимально необходимые пакеты — только два реальных (#2, #5). Все 4 фейка можно отключить:
```bash
# Без фейков вообще (по сути = multisplit с одной позицией)
--lua-desync=fakedsplit:pos=midsld:nofake1:nofake2:nofake3:nofake4
```
Типичные комбинации (о том, сколько пакетов остаётся, — в примерах [[#8. Без "задних" фейков (оптимизация трафика)|8]] и [[#9. С повторами фейков (максимальный шум)|9]]):
```bash
# Только "передние" фейки (до реальных частей)
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:nofake2:nofake4
# Только "задние" фейки (после реальных частей)
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:nofake1:nofake3
# Фейки только для 1-й части
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:nofake3:nofake4
```
---
## Полный список аргументов
Аргументы делятся на собственные аргументы `fakedsplit` (раздел A) и стандартные (разделы B–G): их понимают и другие функции дурения, а общий обзор есть в [[desync]]. Формат вызова:
```
--lua-desync=fakedsplit[:arg1[=val1][:arg2[=val2]]...]
```
Все `val` приходят в Lua как строки. Если `=val` не указан, значение = пустая строка `""` (в Lua это truthy), поэтому флаги пишутся просто как `:optional`, `:nodrop`, `:tcp_ts_up`.
### A) Собственные аргументы fakedsplit
#### `pos`
- **Формат:** `pos=<marker>`
- **Тип:** строка — **один** маркер (НЕ список!)
- **По умолчанию:** `"2"`
- **Описание:** Точка разреза. Payload делится на 2 части: `data[1..pos-1]` и `data[pos..#data]`. Виды маркеров — в разделе [[#Маркер позиции (pos)|Маркер позиции (pos)]]
- **Примеры:**
- `pos=2` — разрез после 2-го байта (дефолт)
- `pos=1` — первый байт уходит отдельным сегментом
- `pos=midsld` — разрез посередине SLD
- `pos=method+2` — после первых 2 символов HTTP-метода
- `pos=host` — перед началом hostname
- `pos=sniext+1` — после начала SNI extension data
#### `pattern`
- **Формат:** `pattern=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** `\x00` (нулевой байт)
- **Описание:** Данные для заполнения фейковых сегментов. Blob повторяется функцией `pattern()` до нужного размера. Подробнее — в разделе [[#pattern — заполнение фейков|pattern — заполнение фейков]]
- **Примеры:**
- `pattern=0xDEADBEEF` — inline hex
- `pattern=my_fake_pattern` — предзагруженный blob
#### `seqovl`
- **Формат:** `seqovl=N` (где N > 0)
- **Тип:** только число (маркеры **не поддерживаются** — в отличие от [[fakeddisorder]])
- **По умолчанию:** не задан (нет seqovl)
- **Описание:** Применяется **только к первому реальному сегменту** (пакет #2). К данным первого сегмента слева добавляется N байт `seqovl_pattern`, а TCP `th_seq` уменьшается на N. Сервер отбросит левую часть, DPI — может не отбросить. Механика — в разделе [[#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]]
- **Примеры:**
- `seqovl=5` — 5 байт фейка слева
- `seqovl=13` — 13 байт фейка слева
#### `seqovl_pattern`
- **Формат:** `seqovl_pattern=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** один байт `0x00`, повторяемый до длины `seqovl`
- **Описание:** Данные для заполнения seqovl-области. Blob повторяется функцией `pattern()` до нужного размера
- **Поведение с `optional`:** если `optional` задан и blob отсутствует — используется нулевой паттерн, seqovl не отменяется
- **Примеры:**
- `seqovl_pattern=0x1603030000` — inline hex (маскировка под TLS)
- `seqovl_pattern=my_pattern_blob` — предзагруженный blob
#### `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 отсутствует => fakedsplit **ничего не делает** (тихий skip, без ошибок)
- Если задан `seqovl_pattern=...` и blob отсутствует => используется нулевой паттерн (seqovl не отменяется)
- **Использование:** защита от ошибок при использовании blob, которые могут отсутствовать
#### `nodrop`
- **Формат:** `nodrop` (флаг, без значения)
- **Описание:** После успешной отправки сегментов **не выносить** `VERDICT_DROP` (вместо этого вернуть `VERDICT_PASS`). Это означает, что оригинальный пакет тоже будет отправлен (наряду с нарезанными сегментами и фейками). `VERDICT_DROP` — решение «перехваченный пакет не отправлять», `VERDICT_PASS` — «пропустить пакет как есть» ([[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадия 8 жизненного цикла]])
- **Использование:** для отладки
- **Предупреждение:** в боевых профилях `nodrop` обычно нежелателен (см. [[#9. nodrop создаёт сильное дублирование|нюанс 9]])
#### `nofake1`
- **Формат:** `nofake1` (флаг, без значения)
- **Описание:** Не отправлять фейк 1-й части **до** реальной 1-й части (пакет #1)
#### `nofake2`
- **Формат:** `nofake2` (флаг, без значения)
- **Описание:** Не отправлять фейк 1-й части **после** реальной 1-й части (пакет #3)
#### `nofake3`
- **Формат:** `nofake3` (флаг, без значения)
- **Описание:** Не отправлять фейк 2-й части **до** реальной 2-й части (пакет #4)
#### `nofake4`
- **Формат:** `nofake4` (флаг, без значения)
- **Описание:** Не отправлять фейк 2-й части **после** реальной 2-й части (пакет #6)
---
### B) Standard direction
Направление говорит, в какую сторону идёт пакет: от вашего компьютера к серверу (исходящий) или обратно (входящий).
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |
Фильтр по направлению пакета. `fakedsplit` по умолчанию работает только с исходящими (`out`).
- `dir=out` — только исходящие (от клиента к серверу)
- `dir=in` — только входящие (от сервера к клиенту)
- `dir=any` — оба направления
При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` — отсекает себя от противоположного направления. Cutoff (отсечение) значит, что движок больше не будет вызывать функцию на пакетах этого направления в текущем соединении; подробно — в [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадии 2 жизненного цикла]].
---
### C) Standard payload
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `payload` | список типов через запятую | `known` |
Фильтр по [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|типу payload]] на уровне Lua. Это **дополнительный** фильтр к `--payload=...` на уровне [[Zapret2/profile#Анатомия профиля|профиля]].
- `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-фильтр.
Проще говоря: фильтр `--payload=` на уровне профиля работает в быстром ядре программы, написанном на C, а аргумент `payload=` проверяется уже в Lua, то есть медленнее. Поэтому основной отбор лучше делать на уровне профиля.
---
### D) Standard fooling
Fooling — намеренная порча заголовков пакета, чтобы сервер отбросил фейк, а DPI его прочитал (подробно — [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]; каталог способов, где какой ломается, — [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|Как заставить fake умереть]]). Модификации L3/L4 заголовков, то есть заголовков IP (сетевой уровень) и TCP (транспортный уровень). В `fakedsplit` применяются **только к фейкам** (opts_fake). К оригиналам из всего fooling идёт **только tcp_ts_up**.
| Параметр | Описание | Пример |
|:---------|:---------|:-------|
| `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` |
**Важно для fakedsplit:** `tcp_ts_up` — единственный параметр fooling, который применяется и к оригиналам. Все остальные — только к фейкам. Это значит, что `tcp_ack=-66000` безопасен — оригиналы будут иметь валидный ACK, а фейки — невалидный.
**Заметка про tcp_ts_up:** На Linux-серверах пакеты с инвалидным ACK стабильно отбрасываются **только если** TCP timestamp option идёт первой в заголовке. `tcp_ts_up` перемещает её в начало, обеспечивая корректную работу badseq-fooling. Поскольку `tcp_ts_up` безвреден (не портит данные, просто переупорядочивает опции), он применяется и к оригиналам.
---
### 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 в рамках соединения (требует tracking) | -- |
`ip_id` применяется **и к фейкам, и к оригиналам** (оба набора опций содержат `ipid = desync.arg`).
---
### F) Standard reconstruct
Реконструкция — сборка готового raw-пакета («сырого», со всеми заголовками) перед отправкой.
| Параметр | Описание |
|:---------|:---------|
| `badsum` | Испортить L4 (TCP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет |
В `fakedsplit` `badsum` применяется **только к фейкам** (`opts_fake`). Оригиналы имеют пустой `reconstruct = {}`.
---
### G) Standard rawsend
Rawsend — отправка готовых сегментов в сеть самой функцией; в `fakedsplit` её выполняет `rawsend_payload_segmented` (см. [[#Автосегментация по MSS|Автосегментация по MSS]]). Эти параметры управляют отправкой.
| Параметр | Описание |
|:---------|:---------|
| `repeats=N` | Отправить каждый сегмент N раз (идентичные повторы) |
| `ifout=<iface>` | Интерфейс для отправки (по умолчанию определяется автоматически) |
| `fwmark=N` | Firewall mark (только Linux, nftables/iptables) |
**Важная деталь:** `repeats` применяется **только к фейкам**. Оригиналы используют `rawsend_opts_base`, который не включает `repeats`. `ifout` и `fwmark` применяются к обоим.
---
## Порядок отправки — подробно с seqovl
В примерах ниже payload — десять байт `ABCDEFGHIJ`, настоящие данные обозначены буквами, а фейки набиты байтом `0xFF`. В них `pos=4` — это позиция в нумерации Lua (с единицы), поэтому первая часть (`data[1..pos-1]` из описания аргумента `pos`) содержит три байта `ABC`; в командной строке такой разрез записывался бы как `pos=3` ([[#Важные нюансы pos|Важные нюансы pos]]). Номера seq здесь — смещения от начала данных.
### Без seqovl
```
Payload "ABCDEFGHIJ" (10 байт), pos=4, pattern=0xFF
Пакет #1 (fake1): seq=0 data=[FF FF FF] len=3 opts_fake
Пакет #2 (real1): seq=0 data=[A B C ] len=3 opts_orig
Пакет #3 (fake1'): seq=0 data=[FF FF FF] len=3 opts_fake
Пакет #4 (fake2): seq=3 data=[FF FF FF FF FF FF FF] len=7 opts_fake
Пакет #5 (real2): seq=3 data=[D E F G H I J] len=7 opts_orig
Пакет #6 (fake2'): seq=3 data=[FF FF FF FF FF FF FF] len=7 opts_fake
```
### С seqovl=5
```
Payload "ABCDEFGHIJ" (10 байт), pos=4, pattern=0xFF, seqovl=5, seqovl_pattern=0x00
Пакет #1 (fake1): seq=0 data=[FF FF FF] len=3 opts_fake
Пакет #2 (real1): seq=-5 data=[00 00 00 00 00 A B C ] len=8 opts_orig
Пакет #3 (fake1'): seq=0 data=[FF FF FF] len=3 opts_fake
Пакет #4 (fake2): seq=3 data=[FF FF FF FF FF FF FF] len=7 opts_fake
Пакет #5 (real2): seq=3 data=[D E F G H I J] len=7 opts_orig
Пакет #6 (fake2'): seq=3 data=[FF FF FF FF FF FF FF] len=7 opts_fake
Сервер:
Пакет #2: seq=-5, байты -5..-1 за window => отброшены; байты 0..2 = [A B C] => приняты
Пакет #5: seq=3, байты 3..9 = [D E F G H I J] => приняты
Итого: [A B C D E F G H I J] = полный payload
```
> [!note] Проще говоря
> Пакет №2 стал на 5 байт длиннее (слева пять нулей) и начинается с seq −5. Сервер выбрасывает то, что левее его окна приёма (нули), и принимает `ABC`; вторую часть он принимает целиком. Фейки в обоих примерах отброшены, поэтому в итоге сервер собрал полный `ABCDEFGHIJ`. Механика seqovl — в разделе [[#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]].
---
## Поведение при replay / reasm
![[desync-anim-hub-reasm.svg|Анимация: большой ClientHello приходит тремя пакетами, ядро собирает reasm_data, функция режет весь буфер на первой части, остальные части дропаются]]
Многопакетный запрос (например, большой TLS ClientHello, не влезающий в один TCP-сегмент) `nfqws2` придерживает, собирает в буфер `reasm_data` (reasm — повторная сборка) и перепроигрывает через desync-функции. Перепроигрывание (replay) означает, что движок по очереди вызывает функции на каждой придержанной части, но уже с собранным целым на руках. `fakedsplit` использует штатную развилку из общего скелета desync-функций ([[жизненный цикл desync-функции]], стадия 6) без изменений: разрезает и отправляет **весь** собранный буфер вместе с фейками на первой части, а остальные части дропает. Если отправка сорвалась, флаг «реасм уже отправлен» не ставится и оставшиеся части проходят как есть.
Специфика функции здесь одна, зато существенная: фейковые сегменты строятся по размерам реальных частей **всего реасма**, а не отдельного пакета. Поэтому при многопакетном ClientHello фейки получаются крупными — ровно такими же, как реальные куски.
> [!note] Проще говоря
> Если запрос пришёл несколькими пакетами, `fakedsplit` дожидается, пока движок соберёт его целиком, режет собранное и отправляет шесть пакетов один раз — на первой части; остальные придержанные части выбрасываются, потому что их содержимое уже ушло. Как движок собирает и перепроигрывает такие запросы, описано в [[структура desync и диссекта#Приём многопакетных пейлоадов (reasm / decrypt / replay)|заметке о структуре desync]], а как то же самое устроено у соседней функции — в [[Zapret2/desync/multisplit#Поведение при replay / reasm|multisplit]].
---
## Автосегментация по MSS
![[desync-anim-hub-mss.svg|Анимация: кусок данных больше MSS автоматически режется на под-сегменты с растущим seq]]
MSS (Maximum Segment Size) — наибольший объём данных, который помещается в один TCP-сегмент данного соединения. О размерах TCP-сегментов думать **не нужно**. Функция `rawsend_payload_segmented` из `zapret-lib.lua` автоматически:
1. Отслеживает MSS для каждого TCP-соединения
2. Если часть payload превышает MSS — дополнительно режет по MSS
3. Каждый под-сегмент отправляется с корректным TCP sequence
Это относится и к фейкам, и к оригиналам. Та же автосегментация (и что применяется к под-сегментам) описана в статье о multisplit: [[Zapret2/desync/multisplit#Автосегментация по MSS|Автосегментация по MSS]].
---
## Место в общем скелете
Тело `fakedsplit` построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в [[жизненный цикл desync-функции]] — здесь только отклонения:
| Стадия общего скелета | Что делает `fakedsplit` |
|:----------------------|:----------------|
| 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. **Своя техника** | одна позиция разреза (`resolve_pos`, не список), два реальных сегмента и до четырёх фейков между ними — этому посвящена вся заметка |
| 8. Вердикт | `VERDICT_DROP` после успешной отправки, `VERDICT_PASS` при `nodrop` или сбое `rawsend` |
> [!note] Проще говоря
> Из восьми стадий общего скелета своя у `fakedsplit` одна — седьмая: один разрез, два реальных сегмента и до четырёх фейков между ними. Остальные стадии такие же, как у других функций. Про стадию 6 подробнее сказано в [[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)|стадии 6]], про вердикты (`VERDICT_DROP` — «перехваченный пакет не отправлять», `VERDICT_PASS` — «пропустить») — в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8]].
Отдельно стоит запомнить, как `fakedsplit` обращается с опциями. В отличие от [[multisplit]], он собирает **два разных набора**: фейкам достаются fooling и reconstruct в полном объёме (фейк обязан быть отвергнут сервером), а реальным частям — только `tcp_ts_up` и политика `ip_id`. Повторы `repeats` тоже уходят лишь на фейки. Общий разбор наборов опций — в [[жизненный цикл desync-функции]].
---
## Псевдокод алгоритма
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл desync-функции]]. Комментарии `-- N.` нумеруют шаги: первые шаги совпадают со стадиями общего скелета, шаги с разрезом и отправкой шести пакетов — это собственно техника, а последние — пометка об отправке и вердикт. Проще говоря, функция находит место разреза, собирает два набора опций и отправляет шесть пакетов в порядке фейк 1-й части, настоящая 1-я, фейк 1-й, фейк 2-й, настоящая 2-я, фейк 2-й, а затем выбрасывает оригинал (если не задан `nodrop`):
```lua
function fakedsplit(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_pos(data, l7payload, pos_arg or "2")
if pos == nil then
DLOG("cannot resolve pos")
elseif pos == 1 then
DLOG("split pos resolved to 0. cannot split.")
else
-- 8. Создание двух наборов опций
opts_orig = {rawsend=base, reconstruct={}, ipfrag={},
ipid=arg, fooling={tcp_ts_up=arg.tcp_ts_up}}
opts_fake = {rawsend=full, reconstruct=reconstruct_opts,
ipfrag={}, ipid=arg, fooling=arg}
-- 9. Паттерн для фейков
fakepat = arg.pattern blob or "\x00"
-- 10. ФЕЙК 1-й части
fake = pattern(fakepat, 1, pos-1)
if not nofake1 then
rawsend_payload_segmented(fake, 0, opts_fake)
end
-- 11. РЕАЛЬНАЯ 1-я часть (+seqovl)
part = data:sub(1, pos-1)
seqovl = 0
if arg.seqovl and tonumber(arg.seqovl) > 0 then
seqovl = tonumber(arg.seqovl)
pat = "\x00"
if arg.seqovl_pattern then
if optional and blob not exists then
pat = "\x00" -- fallback
else
pat = blob(seqovl_pattern)
end
end
part = pattern(pat, 1, seqovl) .. part
end
rawsend_payload_segmented(part, -seqovl, opts_orig)
-- 12. ФЕЙК 1-й части (повтор)
if not nofake2 then
rawsend_payload_segmented(fake, 0, opts_fake)
end
-- 13. ФЕЙК 2-й части
fake = pattern(fakepat, pos, #data-pos+1)
if not nofake3 then
rawsend_payload_segmented(fake, pos-1, opts_fake)
end
-- 14. РЕАЛЬНАЯ 2-я часть
part = data:sub(pos)
rawsend_payload_segmented(part, pos-1, opts_orig)
-- 15. ФЕЙК 2-й части (повтор)
if not nofake4 then
rawsend_payload_segmented(fake, pos-1, opts_fake)
end
-- 16. Пометить как отправленное
replay_drop_set()
return nodrop and VERDICT_PASS or VERDICT_DROP
end
else
-- 17. Не первый replay
DLOG("not acting on further replay pieces")
end
-- 18. Drop replayed packets если ранее успешно отправлено
if replay_drop() then
return nodrop and VERDICT_PASS or VERDICT_DROP
end
end
end
```
---
## Нюансы и подводные камни
### 1. Работает только с TCP
Если текущий пакет не TCP (UDP, ICMP и т.д.), `fakedsplit` делает `instance_cutoff_shim` — отключает себя для этого потока навсегда. Исключение: ICMP-пакеты (связанные с TCP) не вызывают cutoff. Почему приём вообще завязан на TCP (сквозная нумерация байтов, окно приёма), разобрано в статье о multisplit: [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|1. Работает только с TCP — и почему]]; механика cutoff — в [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|стадии 1 жизненного цикла]].
### 2. Только ОДНА позиция разреза
В отличие от [[multisplit]], `fakedsplit` принимает **один** маркер, не список. `pos=midsld,endhost` не будет работать как два разреза — строка целиком передастся в `resolve_pos`, который не умеет парсить запятые. Если нужно несколько точек разреза с фейками — используйте цепочку инстансов или [[fakeddisorder]]. Инстанс — одна запись `--lua-desync=…` в профиле.
### 3. Позиция 1 не работает
Если `resolve_pos` возвращает Lua-позицию 1, fakedsplit логирует "split pos resolved to 0. cannot split." и ничего не делает. Это соответствует маркеру `0` (смещения в командной строке считаются с нуля), а не `pos=1`. Практический пример: `pos=method` для HTTP не сработает, рабочий вариант — `pos=method+2`.
### 4. Fooling ОБЯЗАТЕЛЕН для фейков
Без fooling фейки будут приняты сервером как валидные данные, что сломает TCP-поток. Всегда указывайте хотя бы один метод fooling (что это такое — в разделе [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]):
```bash
# НЕПРАВИЛЬНО — фейки будут приняты сервером!
--lua-desync=fakedsplit:pos=midsld
# ПРАВИЛЬНО — фейки отброшены по невалидному ACK
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up
```
### 5. tcp_ts_up рекомендуется вместе с tcp_ack/tcp_seq
На Linux-серверах `tcp_ack=-66000` надёжно отбрасывает пакеты только если TCP timestamp option идёт первой. `tcp_ts_up` обеспечивает это (то же сказано в таблице [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|Как заставить fake умереть]], строка про bad ack). Всегда комбинируйте:
```bash
:tcp_ack=-66000:tcp_ts_up
```
### 6. repeats применяются ТОЛЬКО к фейкам
`repeats=N` — сколько раз отправить каждый сегмент. Если задать `repeats=5`, каждый фейк отправится 5 раз, а каждый оригинал — 1 раз. Это сделано специально: больше фейков => больше шума для DPI, но данные не дублируются.
### 7. ipfrag НЕ используется
IP-фрагментация — деление пакета на части на уровне IP, а не TCP. В отличие от [[multisplit]] и [[multidisorder]], `fakedsplit` не поддерживает IP-фрагментацию. Оба набора опций имеют пустой `ipfrag = {}`. Если нужна IP-фрагментация поверх фейков — это не поддерживается напрямую.
### 8. badsum идёт только на фейки
`badsum` портит контрольную сумму TCP: по ней получатель проверяет, что пакет не повреждён. Это часть `reconstruct`, который применяется только к фейкам. Оригиналы всегда имеют валидный checksum. Это безопасно и является ещё одним методом fooling.
### 9. nodrop создаёт сильное дублирование
С `nodrop` fakedsplit отправляет до 6 сегментов (фейки + оригиналы) И пропускает оригинальный пакет. Сервер получит данные как минимум дважды. Используйте `nodrop` только для отладки. О том, как движок обращается с `VERDICT_DROP` и `VERDICT_PASS`, — в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]].
### 10. Порядок инстансов важен
Если перед `fakedsplit` стоит `pktmod` с модификацией — pktmod изменит payload, и fakedsplit порежет уже изменённые данные. Если после fakedsplit стоит ещё один инстанс — он увидит VERDICT_DROP и не получит оригинальный payload.
Уточнение из разбора вердиктов: `VERDICT_DROP` не обрывает цепочку. Движок проходит все инстансы профиля и складывает их вердикты (`VERDICT_DROP` перебивает `VERDICT_PASS` и `VERDICT_MODIFY`), так что инстансы после `fakedsplit` вызываются как обычно, а дроп означает лишь то, что оригинальный пакет в итоге не уйдёт ([[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадия 8 жизненного цикла]]; то же — в [[Zapret2/desync/multisplit#7. Порядок инстансов важен|нюансе о порядке инстансов в multisplit]]). Как аргументы и инстансы выстраиваются в цепочку — в [[последовательность аргументов]].
### 11. Неразрешённый маркер != ошибка
Если `resolve_pos` не смог разрешить маркер (например, `midsld` для `unknown` payload), fakedsplit логирует "cannot resolve pos" и **ничего не делает** — ни фейков, ни разреза. Оригинальный пакет проходит как есть.
---
## Отличия от других функций сегментации
Четыре функции ниже режут данные на TCP-сегменты, но различаются числом разрезов, порядком отправки, наличием фейков и тем, как устроен `seqovl`. Строки таблицы читаются слева направо: сначала обычная сегментация, затем варианты с фейками. Подробности о каждой функции — в разделах [[Zapret2/desync/multisplit#Отличия от других функций сегментации|multisplit]], [[Zapret2/desync/multidisorder#Отличия от других функций сегментации|multidisorder]] и [[Zapret2/desync/fakeddisorder#Отличия от fakedsplit, multisplit и multidisorder|fakeddisorder]].
| Аспект | `multisplit` | `multidisorder` | `fakedsplit` | `fakeddisorder` |
|:-------|:-------------|:----------------|:-------------|:----------------|
| Количество позиций | Список (любое кол-во) | Список (любое кол-во) | **Одна** | **Одна** |
| Резолвер позиций | `resolve_multi_pos` | `resolve_multi_pos` | **`resolve_pos`** | **`resolve_pos`** |
| Порядок отправки | Прямой (1->2->3) | Обратный (3->2->1) | Прямой (с фейками) | Обратный (с фейками) |
| Фейковые сегменты | **Нет** | **Нет** | Да (до 4 шт.) | Да (до 4 шт.) |
| Пакетов на выходе | N+1 (N позиций) | N+1 (N позиций) | До 6 (2 реальных + 4 фейка) | До 6 (2 реальных + 4 фейка) |
| seqovl тип | Только число | **Маркер** | Только число | **Маркер** |
| seqovl к какому сегменту | 1-й | 2-й (предпоследний) | 1-й реальный | 2-й реальный |
| Fooling к | Всем сегментам | Всем сегментам | **Только к фейкам** | **Только к фейкам** |
| reconstruct к | Всем сегментам | Всем сегментам | **Только к фейкам** | **Только к фейкам** |
| repeats к | Всем сегментам | Всем сегментам | **Только к фейкам** | **Только к фейкам** |
| ipfrag | Да | Да | **Нет** | **Нет** |
| ipid к | Всем сегментам | Всем сегментам | И фейкам, и оригиналам | И фейкам, и оригиналам |
| tcp_ts_up к оригиналам | Да (как часть fooling) | Да (как часть fooling) | **Да (единственный fooling)** | **Да (единственный fooling)** |
Проще говоря: главная развилка — есть ли фейки и в каком порядке уходят части. У `multisplit` и `multidisorder` фейков нет (порядок прямой и обратный), у `fakedsplit` и `fakeddisorder` они есть и достаются только фейкам (порядок прямой и обратный).
---
## Миграция с nfqws1
nfqws1 — движок первой версии zapret; там техники включались ключами `--dpi-desync-…`, а в nfqws2 они стали Lua-функциями `--lua-desync=<функция>:<аргументы>`. Таблица ниже сопоставляет ключи `fakedsplit`, а примеры показывают перевод команд целиком. Про сам fooling и то, чем `badseq` отличается от `tcp_ack`, — в разделе [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|Как заставить fake умереть]].
### Соответствие параметров
| nfqws1 | nfqws2 |
|:-------|:-------|
| `--dpi-desync=fakedsplit` | `--lua-desync=fakedsplit` |
| `--dpi-desync-split-pos=midsld` | `:pos=midsld` |
| `--dpi-desync-split-pos=method+2` | `:pos=method+2` |
| `--dpi-desync-fooling=badseq` | `:tcp_ack=-66000:tcp_ts_up` (или другой fooling) |
| `--dpi-desync-badseq-increment=0` | `:tcp_seq=0` (если нужен нулевой seq инкремент) |
| `--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=fakedsplit \
--dpi-desync-fooling=badseq \
--dpi-desync-badseq-increment=0 \
--dpi-desync-split-pos=method+2
# nfqws2 (эквивалент):
nfqws2 \
--payload=http_req \
--lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
```bash
# nfqws1:
nfqws --dpi-desync=fakedsplit \
--dpi-desync-fooling=md5sig \
--dpi-desync-split-pos=midsld \
--dpi-desync-split-seqovl=5 \
--dpi-desync-split-seqovl-pattern=0x1603030000
# nfqws2 (эквивалент):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_md5:tcp_ts_up:seqovl=5:seqovl_pattern=0x1603030000
```
```bash
# nfqws1 с несколькими protocol:
nfqws --dpi-desync=fakedsplit \
--dpi-desync-fooling=badseq \
--dpi-desync-split-pos=2
# nfqws2 (разделяем по payload):
nfqws2 \
--payload=tls_client_hello,http_req \
--lua-desync=fakedsplit:pos=2:tcp_ack=-66000:tcp_ts_up
```
---
## Практические примеры
Примеры собраны от простого к сложному. В каждом после команды сказано, что уходит в сеть и что может увидеть DPI. Флаг `--payload=…` работает как фильтр для всех `--lua-desync`, которые идут после него; аргументы и порядок инстансов в профиле — в разделе [[последовательность аргументов]], а как проверить, что подобранная команда действительно работает, — в [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листе честной проверки]].
### 1. Минимальный (дефолт: pos=2, fooling=badseq)
```bash
--lua-desync=fakedsplit:tcp_ack=-66000:tcp_ts_up
```
Разрезает payload после 2-го байта => 2 реальных + 4 фейка = 6 пакетов. Фейки отбрасываются по невалидному ACK.
### 2. TLS: разрез посередине SNI
```bash
--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up
```
SNI разрезан пополам. В каждом фейке — нули вместо реальных данных. DPI не может собрать hostname ни из одного отдельного сегмента.
### 3. HTTP: разрез после метода
```bash
--payload=http_req --lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
Для `GET /path...` разрежет после `GE`. DPI видит 6 сегментов — 3 "ретрансмиссии" для `GE*` и 3 для `T /path...`.
### 4. С seqovl (двойная защита)
```bash
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:seqovl=5:seqovl_pattern=0x1603030000
```
Фейки + seqovl в первом реальном сегменте. Даже если DPI отбросит фейки, seqovl-данные (выглядящие как начало TLS record) могут его сбить.
### 5. С кастомным паттерном для фейков
```bash
--blob=fakepat:0x474554202F HTTP --payload=http_req \
--lua-desync=fakedsplit:pos=host:tcp_ack=-66000:tcp_ts_up:pattern=fakepat
```
Фейки заполнены данными, похожими на HTTP-запрос. DPI ещё больше запутывается. Как задавать blob и inline hex — в заметке [[blob]].
### 6. С badsum вместо badseq
```bash
--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:badsum:tcp_ts_up
```
Фейки отбрасываются сервером из-за невалидного TCP checksum. Не все DPI проверяют checksum, поэтому для многих этого достаточно. Где `badsum` ломается (например, за NAT), сказано в [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталоге способов]].
### 7. С TCP MD5 signature
```bash
--payload=tls_client_hello --lua-desync=fakedsplit:pos=midsld:tcp_md5:tcp_ts_up
```
Фейки содержат TCP MD5 option. Сервер без настроенного MD5 отбросит такие пакеты.
### 8. Без "задних" фейков (оптимизация трафика)
```bash
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:nofake2:nofake4
```
Только 4 пакета вместо 6. Фейки идут только **до** реальных частей. Для многих DPI этого достаточно.
### 9. С повторами фейков (максимальный шум)
```bash
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:repeats=3
```
Каждый фейк отправляется 3 раза => 4*3 + 2 = 14 пакетов. Оригиналы — по 1 разу. Максимальное замусоривание потока для DPI.
### 10. С TTL-fooling (вместо badseq)
```bash
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:ip_ttl=4:tcp_ts_up
```
Фейки имеют TTL=4 — не доживут до сервера, но DPI на пути их увидит. Менее надёжно, чем badseq (зависит от числа хопов). TTL — счётчик «прыжков» (хопов) пакета по сети: он уменьшается на каждом маршрутизаторе, и при нуле пакет выбрасывается; про TTL-fooling подробнее — в [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталоге способов]].
### 11. Цепочка: fake + fakedsplit
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up
```
Сначала отправляется полноценный TLS-фейк (поддельный ClientHello), затем реальный payload нарезается с замешиванием фейков. Двойной удар по DPI. Как устроена функция [[Zapret2/desync/fake#Зачем нужен fake|fake]] и что означает `tls_mod`, — в её статье: [[Zapret2/desync/fake#tls_mod — модификации TLS в фейке|tls_mod — модификации TLS в фейке]]; `fake_default_tls` — один из [[Zapret2/desync/fake#Стандартные blob-ы|стандартных blob-ов]].
### 12. Комбинация с wssize и syndata
```bash
--lua-desync=wssize:wsize=1:scale=6 \
--lua-desync=syndata \
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:seqovl=5
```
Window size manipulation + SYN data + fakedsplit с seqovl. Многоуровневая защита. `wssize` меняет размер окна приёма (перечень таких функций — в [[desync]]), `syndata` отправляет SYN с данными ([[syndata]]).
### 13. Защита от отсутствующего blob
```bash
--lua-desync=fakedsplit:blob=maybe_missing:optional:pos=midsld:tcp_ack=-66000:tcp_ts_up
```
Если blob не существует — тихий пропуск, без ошибок и без VERDICT_DROP. Флаг `optional` описан в разделе [[#A) Собственные аргументы fakedsplit|A) Собственные аргументы fakedsplit]].
### 14. Отладка: не блокировать оригинал
```bash
--payload=http_req --lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up:nodrop
```
Отправляет нарезанные сегменты с фейками И пропускает оригинальный пакет (для экспериментов).
### 15. Боевой пример для YouTube (TLS)
```bash
--filter-tcp=443 --hostlist=youtube.txt \
--payload=tls_client_hello \
--lua-desync=fakedsplit:pos=midsld:tcp_ack=-66000:tcp_ts_up:seqovl=5:seqovl_pattern=0x1603030000
```
Разрез SNI пополам + seqovl маскировка + badseq fooling. Оптимальная комбинация для TLS-трафика.
### 16. Боевой пример для HTTP
```bash
--filter-tcp=80 --hostlist=blocked.txt \
--payload=http_req \
--lua-desync=fakedsplit:pos=host:tcp_ack=-66000:tcp_ts_up
```
Разрез перед hostname в HTTP Host header. Ни один сегмент не содержит полный `Host: blocked.example.com`.
### 17. Вариант из каталога GUI: fake + fakedsplit для порта 80
В каталоге стратегий [[guide|Zapret 2 GUI]] (`strategy_catalogs/winws2/tcp.txt`, запись `fake_fakedsplit_autottl_2` с подписью «рекомендуется для 80 порта», проверено в сентябре 2026) есть связка из фейкового запроса и `fakedsplit` с одинаковым fooling. `--payload` и `--out-range` ниже добавлены для наглядности, в каталоге их подставляет категория:
```bash
--payload=http_req --out-range=-d10 \
--lua-desync=fake:blob=fake_default_http:ip_autottl=2,3-20:ip6_autottl=2,3-20:tcp_ack=-66000:tcp_ts_up \
--lua-desync=fakedsplit:ip_autottl=2,3-20:ip6_autottl=2,3-20:tcp_ack=-66000:tcp_ts_up
```
Сначала уходит фейковый HTTP-запрос ([[fake]]), затем настоящий режется по позиции по умолчанию (`pos=2`, после второго байта) с фейками вокруг частей. У `fakedsplit` fooling применяется только к фейкам (см. таблицу «Отличия от других функций сегментации»), поэтому настоящие части доходят до сервера без изменений.
> [!note] Положительный autottl не убивает фейки
> `ip_autottl=2,3-20` означает «TTL на два хопа **больше** оценённого пути, но в пределах 3–20». С положительной дельтой фейк доходит до сервера, а не умирает по дороге, как при `ip_autottl=-2`. Отбрасывает его сервер уже по `tcp_ack=-66000` вместе с `tcp_ts_up`. Расчёт на то, что так фейк точно пройдёт мимо всех узлов DPI на пути; но autottl — эвристика, которая угадывает длину пути не всегда, а убивать фейк здесь остаётся только badseq; если сервер фейк не отбросит, он получит мусор. Механика autottl — в [[основные флаги]], зачем фейк должен «умереть» — в [[ts-and-fooling]].
---
## 📚 См. также
- [[desync]] — обзор всех функций `--lua-desync` и общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты
- [[жизненный цикл desync-функции]] — восемь стадий, общих для всех техник дурения: отсев транспорта, выбор данных, replay, вердикты
- [[структура desync и диссекта]] — устройство таблицы `desync` и диссекта пакета
- [[multisplit]] — тот же разрез без фейков, с любым числом позиций; там же полный разбор `seqovl` и маркеров
- [[multidisorder]] — разрез с обратным порядком отправки, без фейков
- [[fakeddisorder]] — один разрез, фейки вокруг частей, но части уходят в обратном порядке
- [[hostfakesplit]] — разрез по границам имени хоста с подмешиванием фейкового имени
- [[fake]] — отдельный фейковый пакет, с которым `fakedsplit` можно ставить в одну цепочку
- [[ts-and-fooling]] — что такое fooling, чем `tcp_ts_up` отличается от `ts` и как фейк «умирает» до сервера
- [[blob]] — как объявлять данные для аргументов `pattern`, `seqovl_pattern` и `blob`
- [[payload]] — типы payload, от которых зависят относительные маркеры
- [[verify-strategy]] — как честно проверить, работает ли подобранная комбинация
- [[последовательность аргументов]] — как выстраивается цепочка инстансов в профиле
- [[profile]] — где `--lua-desync` живёт среди остальных настроек
- [[DPI/DPI|DPI и ТСПУ]] — как устроена инспекция трафика, против которой работает `fakedsplit`
---
> **Источники:** `lua/zapret-antidpi.lua:803-906`, `lua/zapret-lib.lua:385-422`, `docs/manual.md:4122-4164` из репозитория zapret2.
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/desync/fakedsplit.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).