todo/Zapret2/desync/tcpseg.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

1055 lines
119 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
- tcpseg
- seqovl
aliases:
- tcpseg
description: "Справочник по функции tcpseg в Zapret2/nfqws2: отправка TCP-сегмента из диапазона pos, seqovl без сегментации, связка с drop и примеры стратегий."
---
> [!mirror] Резервное зеркало
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Zapret2/desync/tcpseg](https://wiki2.zapret.moe/Zapret2/desync/tcpseg)
# `tcpseg` -- отправка TCP-сегмента из диапазона (zapret2 / nfqws2)
## Коротко и по-простому
Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке `Host:` запроса. ТСПУ (Технические средства противодействия угрозам — оборудование [[DPI/DPI|DPI]], то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения и по нему решает, что делать с соединением дальше.
HTTP-запросы и TLS-соединения идут поверх протокола TCP, а TCP отправляет данные кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер по этим номерам. Кроме того, сервер принимает только те байты, чьи номера попадают в его окно приёма (receive window): оно начинается с номера следующего байта, которого сервер ждёт. Всё, что лежит левее этой границы, TCP-стек молча отбрасывает как повтор или мусор — это штатное поведение, а не ошибка. Почему сборку по номерам гарантирует именно TCP, разобрано в [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|нюансе multisplit «Работает только с TCP»]].
Движок Zapret 2 (`nfqws2`) перехватывает исходящий пакет с ClientHello или HTTP-запросом, а функция `tcpseg` берёт из его данных один кусок и отправляет его в сеть отдельным TCP-сегментом, который она собирает сама. Данные пакета без сетевых заголовков называют payload: это сам HTTP-запрос или сам ClientHello. Границы куска задаёт аргумент `pos` — ровно два маркера через запятую, начало и конец диапазона. Маркер — это число (`0` — первый байт данных, `-1` — последний) или название места в запросе, например `midsld` — середина домена второго уровня, то есть главной части имени сайта (у `www.example.com` это `example`). Запись `pos=0,-1` означает «весь payload», а `pos=0,midsld` — «от начала до середины домена». В отличие от [[Zapret2/desync/multisplit#Маркеры позиций (pos)|multisplit]], у `tcpseg` нет значения `pos` по умолчанию: без него функция выдаёт ошибку. Все виды маркеров разобраны в разделе [[#Маркеры позиций (pos) -- диапазон из двух маркеров|Маркеры позиций (pos) -- диапазон из двух маркеров]].
Кусок отправляют отдельным сегментом, который функция собирает сама, и его параметры задаются аргументами команды: какие байты берутся (`pos`), сколько байт фальшивки приклеивается слева и как сдвигается seq (`seqovl`), сколько раз сегмент отправляется (`repeats`) и какие заголовки в нём меняются (fooling, о нём ниже). Этим можно воспользоваться тремя способами. Первый — отправить весь ClientHello одним сегментом, но с фальшивым префиксом слева (приём seqovl, о нём ниже). Второй — многократно отправить начало запроса в расчёте на то, что буфер DPI окажется забит. Третий — поставить `tcpseg` кирпичиком в цепочку рядом с другими инстансами, то есть записями `--lua-desync=…` в [[Zapret2/profile#Анатомия профиля|профиле]] (наборе правил Zapret 2 для определённого трафика), например рядом с `drop` (отдельной функцией, которая выбрасывает оригинальный пакет). Подробно — в разделе [[#Зачем нужен tcpseg|Зачем нужен tcpseg]].
К отправляемому куску можно добавить приём seqovl (Sequence Overlap, «перекрытие по номерам последовательности»). `tcpseg` дописывает слева N байт фальшивки — по умолчанию это нули, другой узор задаёт `seqovl_pattern` — и сдвигает seq сегмента назад на те же N. Для сервера эти байты оказываются левее границы окна приёма: он их выбрасывает и принимает остальное. DPI же может прочитать сегмент целиком и принять фальшивку за начало потока: так бывает, если он читает сегмент от начала и не сверяется с тем, какие байты примет сервер. Обычно фейк прячут от сервера иначе: отдельному пакету-обманке ([[Zapret2/desync/fake#Зачем нужен fake|fake]]) портят заголовки, например TTL (счётчик «времени жизни» пакета) или номера seq и ack (номер подтверждения), чтобы пакет не дошёл до сервера или сервер его отбросил, а DPI прочитал; такая порча называется fooling ([[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). У seqovl заголовки остаются правильными, портить их не приходится. В `tcpseg` seqovl относится к единственному отправляемому сегменту, поэтому весь ClientHello можно отправить одним сегментом с префиксом, без нарезки. Механика по байтам — в разделе [[#seqovl -- скрытый фейк внутри сегмента|seqovl -- скрытый фейк внутри сегмента]], а как seqovl устроен в `multisplit` — в [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|его разделе]].
Вердикт — это решение движка о судьбе перехваченного оригинального пакета: пропустить его как есть (`VERDICT_PASS`), выбросить (`VERDICT_DROP`) или заменить изменённым (`VERDICT_MODIFY`). Если функция вердикт не вернула, движок считает это за `VERDICT_PASS`. `multisplit` после отправки кусков сам выносит `VERDICT_DROP`. `tcpseg` не выносит вердикт вообще, поэтому оригинал остаётся жить: если после `tcpseg` в профиле нет других инстансов, отсутствие вердикта означает «пропустить как есть», оригинал уходит как обычно, и сервер получает данные дважды, из сегмента `tcpseg` и из оригинала. Вердикты всех инстансов профиля движок складывает, и `VERDICT_DROP` перебивает остальные, а инстансы после него всё равно вызываются. Поэтому, чтобы оригинал не ушёл, следом ставят отдельный инстанс `drop`: он только выносит `VERDICT_DROP`. Подробно — в разделе [[#Вердикт и комбинация с drop|Вердикт и комбинация с drop]], а как движок складывает вердикты нескольких инстансов — в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]]. Есть и нюанс с фильтрами: `drop` по умолчанию выбрасывает пакеты любого типа, а `tcpseg` работает только с распознанными протоколами, см. [[#5. drop по умолчанию дропает ВСЕ payload|нюанс 5]].
Связка из быстрого старта `tcpseg:pos=0,-1:seqovl=1` + `drop` заменяет оригинальный пакет одним сегментом. Сам `tcpseg` вердикт не выносит, поэтому оригинал выбрасывает второй инстанс `drop`, а в сеть уходит тот же ClientHello с одним байтом мусора спереди и seq, сдвинутым на единицу назад. ТСПУ видит поток, который начинается с этого байта, и разбор ClientHello может сбиться; сервер отрезает байт, лежащий левее окна приёма (на схеме — левее вертикальной линии seq=0). Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.
![[desync-anim-tcpseg.svg|Анимация tcpseg: оригинал выброшен, вместо него уходит ClientHello с байтом seqovl левее окна приёма]]
Самая короткая команда — та, что на схеме: весь payload (`pos=0,-1`), один нулевой байт спереди (`seqovl=1`) и `drop` следом. Строку `--lua-desync=…` пишут в профиль. Для HTTPS к ней добавляют фильтр `--payload=tls_client_hello`, чтобы функция срабатывала только на TLS ClientHello, как в [[#1. Минимальный seqovl без сегментации (TLS)|практическом примере 1]]; остальные готовые команды собраны в разделах [[#Быстрый старт|Быстрый старт]] и [[#Практические примеры|Практические примеры]]. Вот команда без фильтра:
```bash
--lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop
```
`tcpseg` пригождается и без `drop`. Строка `--payload=http_req --lua-desync=tcpseg:pos=0,method+2:ip_id=rnd:repeats=20` отправляет начало HTTP-запроса до маркера `method+2` (что именно попадает в кусок, показано в примере 6) 20 раз подряд со случайным идентификатором IP-пакета (IP ID), а потом уходит и оригинал. Смысл в том, чтобы забить буфер DPI: он может переполниться, и тогда DPI перестанет анализировать поток. См. [[#6. Повторная отправка начала HTTP-запроса (без drop)|пример 6]] и [[#11. Повторная отправка (repeats) без drop -- атака повторами|нюанс 11]].
Готового рецепта на все случаи `tcpseg` не даёт. Расчёт seqovl — на DPI, который читает сегмент от начала и не сверяется с окном приёма сервера (так устроены не все DPI), а работает ли выбранная комбинация у конкретного провайдера, проверяют на практике по [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листу честной проверки]]. `tcpseg` работает только с TCP и самостоятельной стратегией обычно не служит: это строительный блок, который ставят в связку с другими инстансами. Близкие соседи: [[multisplit]] режет весь payload на несколько сегментов и сам выбрасывает оригинал; [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|multidisorder]] отправляет куски в обратном порядке; [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] и [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] подмешивают к настоящим кускам фейки; [[Zapret2/desync/fake#Зачем нужен fake|fake]] — отдельный пакет-обманка. Если к `tcpseg` без `drop` добавить fooling, из-за которого сервер отбросит сам сегмент (например, `tcp_ack=-66000`), по смыслу он станет ближе к `fake`, чем к сегментации: об этом раздел [[#Варианты tcpseg без drop из набора стратегий GUI|Варианты tcpseg без drop из набора стратегий GUI]]. Все отличия от соседей сведены в таблицу раздела [[#Отличия от других функций сегментации|Отличия от других функций сегментации]].
---
## Оглавление
- [Коротко и по-простому](#коротко-и-по-простому)
- [Справка: где функция в коде и родственники](#справка-где-функция-в-коде-и-родственники)
- [Зачем нужен tcpseg](#зачем-нужен-tcpseg)
- [Быстрый старт](#быстрый-старт)
- [Откуда берутся данные](#откуда-берутся-данные)
- [Маркеры позиций (pos) -- диапазон из двух маркеров](#маркеры-позиций-pos----диапазон-из-двух-маркеров)
- [Отличие от multisplit: resolve_range vs resolve_multi_pos](#отличие-от-multisplit-resolve_range-vs-resolve_multi_pos)
- [Типы маркеров](#типы-маркеров)
- [Относительные маркеры](#относительные-маркеры)
- [Арифметика маркеров](#арифметика-маркеров)
- [Поведение resolve_range при неразрешённых маркерах](#поведение-resolve_range-при-неразрешённых-маркерах)
- [Важные нюансы pos](#важные-нюансы-pos)
- [seqovl -- скрытый фейк внутри сегмента](#seqovl----скрытый-фейк-внутри-сегмента)
- [Принцип работы seqovl](#принцип-работы-seqovl)
- [Зачем seqovl лучше обычного fooling](#зачем-seqovl-лучше-обычного-fooling)
- [seqovl_pattern](#seqovl_pattern)
- [Вердикт и комбинация с drop](#вердикт-и-комбинация-с-drop)
- [Полный список аргументов](#полный-список-аргументов)
- [A) Собственные аргументы tcpseg](#a-собственные-аргументы-tcpseg)
- [B) Standard direction](#b-standard-direction)
- [C) Standard payload](#c-standard-payload)
- [D) Standard fooling](#d-standard-fooling)
- [E) Standard ipid](#e-standard-ipid)
- [F) Standard ipfrag](#f-standard-ipfrag)
- [G) Standard reconstruct](#g-standard-reconstruct)
- [H) Standard rawsend](#h-standard-rawsend)
- [Порядок отправки](#порядок-отправки)
- [Поведение при replay / reasm](#поведение-при-replay--reasm)
- [Автосегментация по MSS](#автосегментация-по-mss)
- [Место в общем скелете](#место-в-общем-скелете)
- [Псевдокод алгоритма](#псевдокод-алгоритма)
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
- [Отличия от других функций сегментации](#отличия-от-других-функций-сегментации)
- [Практические примеры](#практические-примеры)
- [Варианты tcpseg без drop из набора стратегий GUI](#варианты-tcpseg-без-drop-из-набора-стратегий-gui)
- [📚 См. также](#-см-также)
---
## Справка: где функция в коде и родственники
Паспорт функции для тех, кто сверяется с исходниками zapret2: в каком файле и на какой строке лежит код, какой ключ первой версии zapret она заменяет и как объявлена.
**Файл:** `lua/zapret-antidpi.lua:1043`
**nfqws1 эквивалент:** отсутствует (NEW функция в nfqws2)
**Сигнатура:** `function tcpseg(ctx, desync)`
`tcpseg` -- функция отправки **части** текущего payload (или reasm, или blob), ограниченной **двумя** маркерами позиций (диапазоном), в виде TCP-сегмента. В отличие от [[multisplit]], которая разрезает весь payload на множество сегментов по списку позиций, `tcpseg` вырезает и отправляет **один** конкретный диапазон данных. При этом `tcpseg` **не выносит вердикт** (ни `VERDICT_DROP`, ни `VERDICT_PASS`) -- для замещения оригинального пакета необходимо комбинировать с функцией `drop`.
> [!note] Проще говоря
> Payload — полезные данные пакета без сетевых заголовков: сам HTTP-запрос или сам ClientHello. Reasm — те же данные, собранные движком из нескольких пакетов, если запрос в один не влез. Blob — заранее заготовленные данные, которые можно подсунуть вместо настоящих. Какой из трёх источников выбирается, разобрано в разделе [[#Откуда берутся данные|Откуда берутся данные]]. Маркер — способ указать место в данных, диапазон — кусок между двумя маркерами ([[#Маркеры позиций (pos) -- диапазон из двух маркеров|Маркеры позиций (pos) -- диапазон из двух маркеров]]). `VERDICT_DROP` — решение «перехваченный пакет не отправлять», `VERDICT_PASS` — «пропустить как есть»; как движок складывает такие решения от разных функций, описано в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]]. Функция `drop` — отдельный инстанс, который только выносит `VERDICT_DROP`.
Родственные функции: [[multisplit]] (множественная сегментация), [[multidisorder]] (обратный порядок), [[fakedsplit]] (с фейками), [[fakeddisorder]] (фейки + обратный порядок), [[hostfakesplit]] (по hostname), [[oob]] (urgent byte).
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `tcpseg` от этого скелета отличается.
---
## Зачем нужен tcpseg
`tcpseg` решает задачу, которую не решает [[multisplit]]: отправить **конкретный фрагмент** payload (или весь payload целиком) как отдельный TCP-сегмент, с опциональным seqovl, **без автоматического вынесения вердикта**.
Как seqovl устроен в `multisplit`, разобрано в [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|его разделе]]; здесь он разобран для `tcpseg` в разделе [[#seqovl -- скрытый фейк внутри сегмента|seqovl -- скрытый фейк внутри сегмента]], а вердикт — в разделе [[#Вердикт и комбинация с drop|Вердикт и комбинация с drop]].
Основные сценарии использования:
1. **seqovl без сегментации.** Отправить весь payload целиком с seqovl-префиксом, подменяющим начало потока для DPI. Маркеры `pos=0,-1` задают диапазон "от начала до конца" -- весь payload
2. **Повторная отправка начала потока.** Отправить первые N байт payload многократно (`repeats=N`), чтобы забить буфер DPI мусором. Маркеры `pos=0,method+2` или `pos=0,midsld` отправляют только начало
3. **Модульная композиция.** Поскольку `tcpseg` не выносит вердикт, его можно комбинировать с другими инстансами (например, `drop`, `luaexec`) в цепочке
Первый сценарий в двух словах: весь ClientHello уходит одним сегментом, но с фальшивым префиксом слева, который для DPI выглядит как начало потока, а сервер его отбрасывает (пример: [[#1. Минимальный seqovl без сегментации (TLS)|Минимальный seqovl без сегментации (TLS)]]). Второй: начало запроса отправляется много раз, и DPI, по описанию в примере [[#7. Повторная отправка до midsld (TLS)|Повторная отправка до midsld (TLS)]], может переполнить свой буфер и перестать анализировать поток. Третий: `tcpseg` сам оригинал не заменяет, поэтому его ставят в цепочку рядом с инстансом, который это сделает, например с `drop`, или с `luaexec` (он выполняет произвольный Lua-код, как в примере [[#4. Динамический случайный seqovl через luaexec|Динамический случайный seqovl через luaexec]]).
**Ключевое отличие от multisplit:** [[multisplit]] разрезает и отправляет **весь** payload, затем дропает оригинал. `tcpseg` отправляет **часть** (или весь) payload и **не** дропает оригинал -- нужна отдельная команда `drop`.
> [!note] Проще говоря
> [[multisplit]] работает по схеме «разрезал всё, отправил куски, оригинал выбросил». `tcpseg` работает по схеме «вырезал один кусок и отправил, а оригинал остаётся на усмотрение остальных инстансов профиля». Если после `tcpseg` не поставить `drop`, оригинал уйдёт вслед за ним.
---
## Быстрый старт
Ниже четыре готовые строки для [[Zapret2/profile#Анатомия профиля|профиля]] Zapret 2, от самой короткой к более сложным. Флаг `--payload=…` работает как фильтр для всех вызовов `--lua-desync`, которые идут после него (до следующего `--payload` или до конца профиля): они срабатывают только на данных указанного типа. Поэтому `--payload` пишут перед `--lua-desync`, а не после (подробно — в [[последовательность аргументов]]).
Минимальный seqovl (весь payload + 1 байт нулевого seqovl, dir=out, payload=known):
```bash
--lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop
```
Здесь `pos=0,-1` — весь payload, `seqovl=1` — один нулевой байт слева, а второй инстанс `drop` выбрасывает оригинал. Значения `dir=out` и `payload=known` берутся по умолчанию: только исходящие пакеты и только распознанные протоколы; оба аргумента разобраны в разделах [B) Standard direction](#b-standard-direction) и [C) Standard payload](#c-standard-payload).
seqovl с кастомным паттерном (TLS):
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,-1:seqovl=5:seqovl_pattern=0x1603030000 \
--lua-desync=drop
```
Функция срабатывает только на TLS ClientHello, отправляет его целиком, а слева приклеивает 5 байт `0x1603030000`, похожих на начало TLS record — заголовка записи TLS. Эти байты задаёт `seqovl_pattern`, см. раздел [[#seqovl_pattern|seqovl_pattern]].
Повторная отправка начала HTTP-запроса:
```bash
--payload=http_req --lua-desync=tcpseg:pos=0,method+2:ip_id=rnd:repeats=20
```
Здесь отправляется начало запроса до `method+2` (что именно попадает в кусок, показано в примере 6), `ip_id=rnd` задаёт случайные IP ID, а `repeats=20` — 20 повторов. `drop` нет, поэтому оригинал тоже уходит. Разбор — в [[#6. Повторная отправка начала HTTP-запроса (без drop)|примере 6]].
Динамический seqovl со случайным паттерном:
```bash
--lua-desync=luaexec:code='desync.rnd=brandom_az(math.random(5,10))' \
--lua-desync=tcpseg:pos=0,-1:seqovl=#rnd:seqovl_pattern=rnd \
--lua-desync=drop:payload=known
```
Перед `tcpseg` `luaexec` на каждый пакет генерирует случайную строку из букв a-z длиной от 5 до 10 символов; она служит и паттерном (`seqovl_pattern=rnd`), и источником длины (`seqovl=#rnd` — размер этого blob). `drop:payload=known` выбрасывает оригинал только для распознанных протоколов. Разбор — в [[#4. Динамический случайный seqovl через luaexec|примере 4]].
---
## Откуда берутся данные
Внутри `tcpseg` данные (`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 может распознать.
Разберём три источника. Первый — blob: заранее заготовленные данные, которые можно подсунуть вместо настоящих; что это такое и как их объявлять, описано в [[blob]]. Если задан `blob=имя`, функция берёт его содержимое и полностью игнорирует реальный payload. Второй — реассемблированные данные (reasm, от английского reassembly — «повторная сборка»): запрос, склеенный движком из нескольких TCP-пакетов, если он в один не влез; как такой запрос отправляется, описано в разделе [[#Поведение при replay / reasm|Поведение при replay / reasm]]. Третий — payload текущего пакета, `desync.dis.payload`: здесь `dis` — диссект, то есть пакет, заранее разобранный движком на поля (его устройство — в [[структура desync и диссекта]]). Lua-оператор `or` возвращает первый истинный операнд, поэтому цепочка читается «сверху вниз, берём первое, что есть». Та же цепочка общая для большинства функций дурения и описана как [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload|стадия 4 жизненного цикла]].
Если blob задан, а относительные маркеры вроде `midsld` не срабатывают, причина может быть в том, что движок не распознал содержимое blob как TLS или HTTP. Тип payload — это название распознанного протокола вроде `tls_client_hello` или `http_req`; как он определяется, описано в [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|заметке о типах payload]].
> [!note] Проще говоря
> Функция спрашивает: «мне подсунули blob? — беру его. Есть запрос, собранный из кусков? — беру его. Иначе беру тело текущего пакета». Маркеры и seqovl всегда работают по тем данным, которые выиграли этот выбор.
---
## Маркеры позиций (pos) -- диапазон из двух маркеров
![[desync-anim-hub-markers.svg|Анимация: маркеры позиций pos на HTTP-запросе и на TLS ClientHello, разрез по pos=host,midsld,endhost]]
`pos` -- **обязательный** аргумент `tcpseg`. Если `pos` не задан, функция вызовет `error("tcpseg: no pos specified")`. Это отличает `tcpseg` от [[multisplit]], где `pos` по умолчанию `"2"`.
Задаётся как строка с **ровно двумя** маркерами через запятую. Первый маркер -- начало диапазона, второй -- конец.
Маркер — это «адрес» байта в данных: число или название места в запросе. Превратить маркер в конкретный номер байта — значит его разрешить; для диапазона это делает функция `resolve_range`. Диапазон — кусок данных между первым и вторым маркерами: именно он вырезается и отправляется. Как те же маркеры служат точками разреза в `multisplit`, разобрано в [[Zapret2/desync/multisplit#Маркеры позиций (pos)|его разделе]].
> [!note] Проще говоря
> `pos=A,B` значит «отправь кусок от A до B». Например, `pos=0,-1` — весь запрос, а `pos=0,midsld` — от начала до середины домена второго уровня.
### Отличие от multisplit: resolve_range vs resolve_multi_pos
Таблица показывает, чем `tcpseg` отличается от [[Zapret2/desync/multisplit#Как маркеры разрешаются в коде|multisplit]] при разборе `pos`. «Функция разрешения» — та, что превращает названия маркеров в номера байтов; обе написаны на C и вызываются из Lua. Главная разница: `multisplit` получает список точек разреза, а `tcpseg` — начало и конец одного куска.
| Аспект | `tcpseg` | `multisplit` |
|:-------|:---------|:-------------|
| Функция разрешения | `resolve_range` (C-код) | `resolve_multi_pos` (C-код) |
| Количество маркеров | **Ровно 2** (ошибка, если не 2) | Любое количество |
| Семантика маркеров | Начало и конец **диапазона** | Точки **разреза** |
| Результат | Таблица из 2 позиций `{start, end}` или `nil` | Массив уникальных позиций |
| Что отправляется | `data:sub(pos[1], pos[2])` -- данные **между** маркерами | Весь payload, разрезанный по позициям |
### Типы маркеров
Маркеры бывают трёх видов. Абсолютные отсчитывают байты от начала или от конца данных и не зависят от содержимого. Относительные ищут место по смыслу — начало имени хоста, середину домена — и потому работают, только если движок распознал протокол; как он это делает, описано в [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|заметке о типах payload]]. Те же маркеры с примерами для разрезов разобраны в [[Zapret2/desync/multisplit#Типы маркеров|multisplit]].
| Тип | Описание | Пример |
|:----|:---------|:-------|
| **Абсолютный положительный** | Смещение от начала payload. `0` = первый байт (в resolve_range используется zero-based нумерация на входе, но внутренне конвертируется в 1-based Lua) | `0`, `5`, `100` |
| **Абсолютный отрицательный** | Смещение от конца payload. `-1` = последний байт | `-1`, `-10`, `-50` |
| **Относительный** | Логическая позиция внутри распознанного payload. Привязана к структуре протокола | `midsld`, `host`, `sniext` |
### Относительные маркеры
В правой колонке указано, в каких типах payload маркер имеет смысл: `http_req` — HTTP-запрос, `tls_client_hello` — TLS ClientHello. 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 /` диапазон `0..method+2` захватит `GE`) |
| `sniext+1` | один байт после начала данных SNI extension |
| `host+3` | три байта после начала hostname |
| `0` | начало payload |
| `-1` | последний байт payload |
### Поведение resolve_range при неразрешённых маркерах
Маркер называют неразрешённым, если движок не нашёл для него места в данных: например, `midsld` для `unknown` payload, то есть для данных, тип которых не распознан. Таблица ниже говорит, что делает `resolve_range` в таких случаях; «non-strict режим» означает, что функция вызвана без флага `strict`.
`resolve_range` реализована в C-коде (`nfq2/lua.c:3281`). Поведение при неразрешённых маркерах:
| Ситуация | Поведение (non-strict режим) |
|:---------|:-----------------------------|
| Оба маркера разрешены | Возвращает `{pos[0], pos[1]}` -- нормальный диапазон |
| Первый маркер не разрешён, второй разрешён | Первый маркер заменяется на `0` (начало данных) |
| Первый маркер разрешён, второй не разрешён | Второй маркер заменяется на `len-1` (конец данных) |
| Оба маркера не разрешены | Возвращает `nil` -- диапазон не может быть определён |
| `pos[0] > pos[1]` (начало после конца) | Возвращает `nil` -- невалидный диапазон |
**Важно:** `tcpseg` вызывает `resolve_range` **без** флага `strict`. Это означает, что если один из маркеров не разрешается, он расширяется до границы данных. Если нужна строгая логика -- используйте абсолютные маркеры.
> [!note] Проще говоря
> Если не нашёлся один маркер, диапазон растягивается до края данных: пропавший первый маркер становится началом данных, пропавший второй — концом. Если не нашлись оба или начало оказалось правее конца, `tcpseg` ничего не отправляет и пишет об этом в лог (подробнее — [[#10. При неразрешённом диапазоне -- тихий пропуск|нюанс 10]]).
### Важные нюансы pos
- **pos обязателен.** В отличие от [[multisplit]], где `pos` по умолчанию `"2"`, в `tcpseg` отсутствие `pos` вызывает `error()`. Это жёсткое требование
- **Ровно 2 маркера.** Если передать 1 или 3+ маркера, `resolve_range` вызовет `luaL_error("resolve_range require 2 markers")`
- **Маркер `0` -- начало данных.** В отличие от [[multisplit]], где позиция 1 удаляется (`delete_pos_1`), в `tcpseg` маркер `0` -- это валидное начало диапазона
- **Маркеры `0,-1` -- весь payload.** Самый частый паттерн: `pos=0,-1` означает "от начала до конца" -- весь payload целиком. Используется для seqovl без сегментации
Те же требования разобраны отдельно в нюансах [[#2. pos обязателен -- нет дефолта|2]], [[#3. Ровно 2 маркера -- не больше и не меньше|3]] и [[#6. Маркер 0 -- это начало, не "нулевая позиция Lua"|6]]. Про удаление позиции в `multisplit` — [[Zapret2/desync/multisplit#2. Разрез в самом начале данных отбрасывается|нюанс 2]] его статьи.
---
## seqovl -- скрытый фейк внутри сегмента
**seqovl** (Sequence Overlap) -- техника скрытого замешивания фейковых данных в реальный TCP-сегмент через манипуляцию TCP sequence number. В `tcpseg` seqovl применяется к отправляемому сегменту (он всегда один).
Смысл приёма в том, что TCP-получатель принимает не все байты подряд. Он держит окно приёма (receive window) и принимает байты, номера которых попадают в него, начиная с номера следующего ожидаемого байта; всё левее этой границы отбрасывается как повтор или мусор. Если приклеить слева фальшивку и сдвинуть seq назад на её длину, фальшивка окажется левее границы: сервер её выбросит, а DPI, читающий сегмент от начала, увидит её вместе с остальным. Полное объяснение по шагам — в [[Zapret2/desync/multisplit#Принцип работы seqovl|принципе работы seqovl в multisplit]]; ниже те же цифры для `tcpseg`, где сегмент один.
В [[Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие|multidisorder]] приём с тем же названием устроен иначе: там он перезаписывает буфер сокета на сервере через перекрытие сегментов, поэтому переносить знания о seqovl между функциями нужно осторожно.
### Принцип работы seqovl
![[desync-anim-hub-seqovl.svg|Анимация: seqovl дописывает слева паттерн и сдвигает seq на 990; DPI читает сегмент целиком, сервер выбрасывает байты левее 1000]]
Схема выше показывает один сегмент с `pos=0,-1` и `seqovl=10`. `TCP seq: 1000` — номер, с которого начинались бы данные без seqovl; после seqovl он уменьшается на 10, а перед данными встают 10 байт паттерна.
![[wiki-anim-z2-tcpseg-seqovl.svg|Анимация: tcpseg с pos=0,midsld и seqovl=5 отправляет начало с паттерном]]
Во втором случае отправляется только часть payload — от начала до `midsld`; `-5` в `seq` — сдвиг относительно начала отправляемого куска.
> [!note] Проще говоря
> `tcpseg` дописывает перед куском лишние байты и подписывает их номерами, которые сервер уже не ждёт. Сервер выбрасывает эти байты, а DPI, читающий сегмент целиком, видит их вместе с настоящими данными. В `tcpseg` сегмент один, поэтому seqovl относится ровно к нему; в `multisplit` он относится только к первому из нескольких сегментов.
### Зачем seqovl лучше обычного fooling
Обычный способ спрятать фейк от сервера — [[Zapret2/ts-and-fooling#Что такое fooling|fooling]]: пакету-обманке портят заголовки (TTL, номера seq или ack, опцию md5sig), и сервер (или маршрутизатор по пути) отбрасывает такой пакет целиком. `seqovl` добивается похожего результата другим путём; таблица сравнивает оба подхода и совпадает с той, что приведена в [[Zapret2/desync/multisplit#Зачем seqovl лучше обычного fooling|multisplit]].
| Критерий | Обычный fooling (TTL, badseq, md5sig) | seqovl |
|:---------|:---------------------------------------|:-------|
| Заголовки | Модифицируются (TTL, seq, ack, md5) | **Не модифицируются** -- пакет выглядит полностью легитимным |
| Обнаружение | DPI может детектировать подозрительные заголовки | DPI видит "честный" сегмент с правильными заголовками |
| Механизм отбрасывания | Сервер отбрасывает весь пакет из-за невалидных заголовков | Сервер отбрасывает только часть, выходящую за TCP window |
| Надёжность | Зависит от поведения конкретного стека | Основан на фундаментальном свойстве TCP |
**Вывод:** seqovl -- средство создания скрытых фейков, не требующее fooling. Это его ключевое преимущество. В `tcpseg` seqovl особенно силён в комбинации с `pos=0,-1`, где весь payload отправляется с фейковым префиксом.
### seqovl_pattern
Паттерн, которым заполняется seqovl-область (N байт слева от реальных данных). По умолчанию -- `0x00` (нули).
В `tcpseg` `seqovl_pattern` -- это **имя blob**. Паттерн повторяется до нужной длины `seqovl`. Blob можно записать прямо в команде шестнадцатеричной строкой (inline hex, `0x…`), заранее объявить флагом `--blob` и сослаться на него по имени или сгенерировать на лету через `luaexec`, как в третьем примере ниже; подробно про blob — в заметке [[blob]].
```bash
# Inline hex blob (маскировка под начало TLS record)
--lua-desync=tcpseg:pos=0,-1:seqovl=5:seqovl_pattern=0x1603030000
# Предзагруженный blob
--blob=tlspat:0x1603030100 \
--lua-desync=tcpseg:pos=0,-1:seqovl=8:seqovl_pattern=tlspat
# Динамически сгенерированный blob через luaexec
--lua-desync=luaexec:code='desync.rnd=brandom_az(math.random(5,10))' \
--lua-desync=tcpseg:pos=0,-1:seqovl=#rnd:seqovl_pattern=rnd
```
Если `optional` задан и blob `seqovl_pattern` отсутствует -- используется нулевой паттерн (операция не отменяется). Сам флаг описан в разделе [[#A) Собственные аргументы tcpseg|A) Собственные аргументы tcpseg]].
**Важно в tcpseg:** `seqovl` -- только **число**, маркеры не поддерживаются (как и в [[multisplit]], в отличие от [[multidisorder]] и [[fakeddisorder]], где seqovl может быть маркером). Как seqovl задаётся маркером там, разобрано в разделах [[Zapret2/desync/multidisorder#seqovl как маркер|seqovl как маркер]] и [[Zapret2/desync/fakeddisorder#seqovl — маркер, а не число|seqovl — маркер, а не число]].
---
## Вердикт и комбинация с drop
Вердикт — решение движка о судьбе перехваченного оригинального пакета: `VERDICT_PASS` пропускает его как есть, `VERDICT_DROP` выбрасывает, `VERDICT_MODIFY` заменяет пакет изменённым; отсутствие вердикта приравнивается к `VERDICT_PASS`. Полная таблица и правила сложения вердиктов от разных инстансов — в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]].
**Ключевая особенность `tcpseg`:** функция **не выносит вердикт**. Она не возвращает ни `VERDICT_DROP`, ни `VERDICT_PASS`. Это означает:
1. После выполнения `tcpseg` оригинальный пакет **не блокируется**
2. Следующие инстансы в цепочке **продолжают обработку**
3. Если после `tcpseg` нет других инстансов -- оригинальный пакет уйдёт как есть
Вердикты нескольких инстансов складываются по правилу «`VERDICT_MODIFY` перебивает `VERDICT_PASS`, `VERDICT_DROP` перебивает оба»: поэтому `drop` в профиле выбрасывает оригинал, даже если сам `tcpseg` ничего не решил. Дроп цепочку не обрывает: инстансы после него всё равно вызываются и видят тот же пакет.
Для замещения оригинального пакета `tcpseg` нужно комбинировать с `drop`:
```bash
# tcpseg отправляет payload с seqovl, drop блокирует оригинал
--lua-desync=tcpseg:pos=0,-1:seqovl=5 --lua-desync=drop
```
**Нюанс с payload-фильтрами:** по умолчанию `tcpseg` работает только с `known` payload, а `drop` -- с `all`. Если вы хотите дропать только известные payload (чтобы неизвестные проходили без изменений), укажите:
```bash
--lua-desync=tcpseg:pos=0,-1:seqovl=5 --lua-desync=drop:payload=known
```
Здесь `known` — только распознанные протоколы (`http_req`, `tls_client_hello` и другие), а `all` — любой payload, включая `unknown`, то есть данные, тип которых движок не распознал. Подробнее — в разделе [[#C) Standard payload|C) Standard payload]] и в [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|заметке о типах payload]].
![[wiki-anim-z2-tcpseg-drop.svg|Схема: tcpseg без drop и с drop]]
> [!note] Проще говоря
> `tcpseg` только добавляет в сеть свой сегмент, а оригинал не трогает, пока этого не сделает другой инстанс. С `drop` данные уходят один раз, сегментом `tcpseg`; без `drop` дважды. Как `multisplit` решает то же самое сам и зачем ему для отладки аргумент `nodrop`, описано в [[Zapret2/desync/multisplit#4. nodrop создаёт дублирование|его нюансе 4]].
---
## Полный список аргументов
Аргументы делятся на собственные аргументы `tcpseg` (раздел A) и стандартные (разделы B–H): их понимают и другие функции дурения, а общий обзор есть в [[desync]]. Формат вызова:
```
--lua-desync=tcpseg[:arg1[=val1][:arg2[=val2]]...]
```
Все `val` приходят в Lua как строки. Если `=val` не указан, значение = пустая строка `""` (в Lua это truthy, то есть считается истинным значением), поэтому флаги пишутся просто как `:optional`.
### A) Собственные аргументы tcpseg
#### `pos`
- **Формат:** `pos=<marker1,marker2>`
- **Тип:** строка с **ровно двумя** маркерами через запятую
- **По умолчанию:** нет (обязательный параметр; если не задан -- `error()`)
- **Описание:** Диапазон данных для отправки. Первый маркер -- начало, второй -- конец (включительно). Из данных вырезается `data:sub(pos[1], pos[2])` и отправляется как TCP-сегмент (в Lua `sub` берёт кусок строки от первого номера до второго). Подробно о маркерах — в разделе [[#Маркеры позиций (pos) -- диапазон из двух маркеров|Маркеры позиций (pos) -- диапазон из двух маркеров]]
- **Примеры:**
- `pos=0,-1` -- весь payload (от начала до конца)
- `pos=0,midsld` -- от начала до середины SLD
- `pos=0,method+2` -- от начала до 2 байт после начала HTTP-метода
- `pos=host,endhost` -- hostname и ещё один байт сразу после него (`endhost` указывает на байт после имени, а конец диапазона включительный)
- `pos=0,1` -- первые два байта (для TLS: первые два байта TLS record)
- `pos=sniext,endhost` -- от начала SNI extension data до конца hostname и один байт после него
> [!warning] Конец диапазона включительный — проверено по коду
> `resolve_range` (`nfq2/lua.c`) переводит оба маркера в позиции и прибавляет единицу для Lua, а `data:sub(pos[1], pos[2])` берёт оба конца. Поэтому байт, на который указывает второй маркер, тоже попадает в сегмент: числовой маркер `1` — это второй байт payload, и `pos=0,1` отправляет **два** байта, а `pos=0,method+2` для `GET /` отправляет `GET`, а не `GE`. Чтобы закончить диапазон ровно перед маркером, вычтите единицу: `pos=0,method+1` даст `GE`, `pos=host,endhost-1` — ровно hostname. Комментарий «захватит `GE`» в схеме арифметики маркеров, раздел «Пример без seqovl (pos=host,endhost)» и заголовок примера 10 «Только первый байт TLS record» написаны без учёта этой единицы.
#### `seqovl`
- **Формат:** `seqovl=N` (где N > 0)
- **Тип:** только число (маркеры **не поддерживаются** -- как и в [[multisplit]])
- **По умолчанию:** не задан (нет seqovl)
- **Описание:** К данным сегмента слева добавляется N байт `seqovl_pattern`, а TCP `th_seq` (поле номера последовательности в TCP-заголовке) уменьшается на N. Сервер отбросит левую часть, DPI -- может не отбросить. Механика — в разделе [[#seqovl -- скрытый фейк внутри сегмента|seqovl -- скрытый фейк внутри сегмента]]
- **Примеры:**
- `seqovl=1` -- 1 байт фейка слева (минимальный)
- `seqovl=5` -- 5 байт фейка слева
- `seqovl=#rnd` -- размер blob `rnd` (C-подстановка через `#`)
- `seqovl=10000` -- 10000 байт (автосегментация по MSS разобьёт на несколько TCP-сегментов)
#### `seqovl_pattern`
- **Формат:** `seqovl_pattern=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** один байт `0x00`, повторяемый до длины `seqovl`
- **Описание:** Данные для заполнения seqovl-области. Blob повторяется функцией `pattern()` до нужного размера. Подробно — в разделе [[#seqovl_pattern|seqovl_pattern]]
- **Поведение с `optional`:** если `optional` задан и blob отсутствует -- используется нулевой паттерн, seqovl не отменяется
- **Примеры:**
- `seqovl_pattern=0x1603030000` -- inline hex (маскировка под TLS)
- `seqovl_pattern=rnd` -- динамически сгенерированный blob
- `seqovl_pattern=fake_default_tls` -- стандартный TLS-фейк как паттерн
#### `blob`
- **Формат:** `blob=<blobName>`
- **Тип:** имя blob-переменной
- **По умолчанию:** не задан
- **Описание:** Заменить текущий payload/reasm на указанный blob и отправить из него диапазон pos. Используется для отправки произвольных данных. Что такое blob и как его объявить — в [[blob]]
- **Примеры:**
- `blob=fake_default_tls` -- стандартный TLS-фейк
- `blob=0xDEADBEEF` -- inline hex
- `blob=my_custom_data` -- предзагруженный blob
#### `optional`
- **Формат:** `optional` (флаг, без значения)
- **Описание:** Мягкий режим:
- Если задан `blob=...` и blob отсутствует -- tcpseg **ничего не делает** (тихий skip, без ошибок)
- Если задан `seqovl_pattern=...` и blob отсутствует -- используется нулевой паттерн (seqovl не отменяется)
- **Использование:** защита от ошибок при использовании blob, которые могут отсутствовать (например, если blob генерируется другой функцией). Та же проверка описана в [[Zapret2/жизненный цикл desync-функции#Стадия 3. Ранняя проверка аргументов|стадии 3 жизненного цикла]]
---
### B) Standard direction
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |
Фильтр по направлению пакета. `tcpseg` по умолчанию работает только с исходящими (`out`).
- `dir=out` -- только исходящие (от клиента к серверу)
- `dir=in` -- только входящие (от сервера к клиенту)
- `dir=any` -- оба направления
При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` -- отсекает себя от противоположного направления. Проще говоря, инстанс перестаёт получать пакеты не своего направления, и движок не тратит время на Lua-вызовы там, где они заведомо бесполезны; проверка направления текущего пакета (`direction_check`) выполняется позже, см. [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадию 2 жизненного цикла]].
---
### C) Standard payload
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `payload` | список типов через запятую | `known` |
Фильтр по типу payload на уровне Lua. Это **дополнительный** фильтр к `--payload=...` на уровне профиля. Тип payload — название протокола, который движок распознал в данных пакета; см. [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|заметку о типах payload]].
- `payload=known` -- только распознанные протоколы (`http_req`, `tls_client_hello`, `quic_initial` и т.д.)
- `payload=all` -- любой payload, включая `unknown`
- `payload=tls_client_hello,http_req` -- конкретные типы
- `payload=~unknown` -- инверсия: всё кроме unknown
**Важно:** лучше ставить `--payload=...` на уровне профиля (C-код, быстрее), а не полагаться только на Lua-фильтр.
---
### D) Standard fooling
Модификации L3/L4 заголовков (то есть заголовков IP и TCP), то самое «портящее» вмешательство — fooling; общий разбор — в [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]] и [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталоге способов]]. В `tcpseg` применяются к отправляемому сегменту.
| Параметр | Описание | Пример |
|:---------|:---------|:-------|
| `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 в `tcpseg` применяется к **реальному** сегменту (не к фейку). Если задать `tcp_ack=-66000`, сервер отбросит сегмент. Fooling в tcpseg имеет смысл только для безопасных вещей: `tcp_ts_up`, `ip_id`, IPv6 extension headers.
> [!note] Проще говоря
> Fooling в `tcpseg` бьёт по тому самому сегменту, который должен дойти до сервера. Поэтому опции, из-за которых сервер отбросит пакет (например, `tcp_ack=-66000`), портят задуманное. Исключение — вариант из раздела [[#Варианты tcpseg без drop из набора стратегий GUI|Варианты tcpseg без drop из набора стратегий GUI]], где сегмент с fooling нарочно превращается в фейк. Подробнее про сами опции — в [[ts-and-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 в рамках соединения (требует tracking) | -- |
`ip_id` применяется к отправляемому сегменту (включая под-сегменты при MSS-сегментации, см. [[#Автосегментация по MSS|Автосегментация по MSS]]).
---
### F) Standard ipfrag
IP-фрагментация **поверх** TCP-сегмента. Отправленный TCP-сегмент дополнительно фрагментируется на уровне IP. Проще говоря: сегмент, уже вырезанный `tcpseg`, при отправке ещё раз делится на части, но теперь средствами протокола IP, а собирает их обратно получатель на уровне IP. Пример — [[#9. seqovl без сегментации + IP-фрагментация|seqovl без сегментации + IP-фрагментация]].
| Параметр | Описание | По умолчанию |
|:---------|:---------|:-------------|
| `ipfrag[=func]` | Включить IP-фрагментацию. Если без значения -- `ipfrag2` | -- |
| `ipfrag_disorder` | Отправить IP-фрагменты в обратном порядке | -- |
| `ipfrag_pos_tcp=N` | Позиция фрагментации TCP (кратно 8) | `32` |
| `ipfrag_pos_udp=N` | Позиция фрагментации UDP (кратно 8). Для tcpseg бесполезно -- он только TCP | `8` |
| `ipfrag_next=N` | IPv6: next protocol во 2-м фрагменте (penetration атака на фаерволы) | -- |
---
### G) Standard reconstruct
Реконструкция — сборка готового raw-пакета («сырого», со всеми заголовками) перед отправкой.
| Параметр | Описание |
|:---------|:---------|
| `badsum` | Испортить L4 (TCP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет |
---
### H) Standard rawsend
Rawsend — отправка готовых сегментов в сеть самой функцией; в `tcpseg` её выполняет `rawsend_payload_segmented` (см. [[#Автосегментация по MSS|Автосегментация по MSS]]). Эти параметры управляют отправкой.
| Параметр | Описание |
|:---------|:---------|
| `repeats=N` | Отправить сегмент N раз (идентичные повторы) |
| `ifout=<iface>` | Интерфейс для отправки (по умолчанию определяется автоматически) |
| `fwmark=N` | Firewall mark (только Linux, nftables/iptables) |
---
## Порядок отправки
`tcpseg` отправляет **один** сегмент (диапазон данных между двумя маркерами), опционально с seqovl-префиксом. Для сравнения: `multisplit` отправляет несколько сегментов и следит за их порядком ([[Zapret2/desync/multisplit#Порядок отправки сегментов|его раздел]]), а здесь сегмент один. Подпись `seq=` на схемах ниже — сдвиг номера последовательности первого байта сегмента: смещение первого байта сегмента относительно начала данных. В примере с `host` это `host_offset`. Если seqovl задан, смещение уменьшается на его длину; отрицательным оно получается в примерах `pos=0,…` (`-10`, `-5`), где кусок начинается с нулевого байта. В коде это третий аргумент вызова `rawsend_payload_segmented`, `pos[1]-1-seqovl`.
### Пример без seqovl (pos=host,endhost)
![[wiki-anim-z2-tcpseg-host.svg|Анимация: tcpseg с pos=host,endhost отправляет только имя сайта]]
Диапазон между маркерами `host` и `endhost` уходит отдельным сегментом, а оригинал остаётся нетронутым: у `tcpseg` вердикта нет.
### Пример с seqovl=10 (pos=0,-1)
```
Payload (600 байт), pos=0,-1, seqovl=10:
Сегмент: [PATTERN(10)][ВЕСЬ_PAYLOAD_600_БАЙТ]
seq=-10 len=610
Сервер: отбросит первые 10 байт, примет 600
DPI: проанализирует все 610 байт
```
Проще говоря: `tcpseg` дописывает 10 байт паттерна спереди и сдвигает seq на −10. Сервер принимает 600 настоящих байт, а 10 байт паттерна отбрасывает как вышедшие за окно приёма; DPI видит все 610.
### Пример с частичной отправкой и seqovl (pos=0,midsld, seqovl=5)
![[wiki-anim-z2-tcpseg-seqovl.svg|Анимация: tcpseg с pos=0,midsld и seqovl=5 отправляет начало с паттерном]]
Здесь отправляется только начало payload: от первого байта до `midsld`. Перед ним стоят 5 байт паттерна с seq на 5 меньше. Чтобы оригинал не ушёл следом, нужен `drop`.
---
## Поведение при replay / reasm
Многопакетный запрос `nfqws2` придерживает, собирает в буфер `reasm_data` и перепроигрывает через desync-функции ([[жизненный цикл desync-функции]], стадия 6). `tcpseg` использует из этой развилки только первую половину: на первой части вырезает диапазон из собранного буфера и отправляет его, на остальных частях логирует «not acting on further replay pieces» и возвращает `nil`.
Вторую половину — дроп последующих частей — функция не применяет сознательно: она ничего не заменяет, а лишь добавляет в поток дополнительный сегмент.
**Важный нюанс:** поскольку `tcpseg` не выносит вердикт, на последующих частях replay оригинальные пакеты **пройдут без изменений**. Если `drop` стоит после `tcpseg` -- он заблокирует все пакеты (и первую часть, и последующие). Поэтому связка `tcpseg` + `drop` корректно обрабатывает reasm: tcpseg отправляет весь reasm при первой части, drop блокирует все оригинальные части.
> [!note] Проще говоря
> Если запрос пришёл несколькими пакетами, движок сначала собирает его целиком (reasm), а затем по очереди вызывает функцию на каждой придержанной части (replay — «перепроигрывание»). `tcpseg` отправляет свой кусок один раз, на первой части. Остальные части сам не дропает: их судьбу решает `drop`, если он стоит в профиле. `multisplit` устроен по-другому: он сам дропает остальные части, см. [[Zapret2/desync/multisplit#Поведение при replay / reasm|его раздел]].
---
## Автосегментация по MSS
![[desync-anim-hub-mss.svg|Анимация: кусок данных больше MSS автоматически режется на под-сегменты с растущим seq]]
MSS (Maximum Segment Size) — наибольший объём данных, который помещается в один TCP-сегмент данного соединения. О размерах TCP-сегментов думать **не нужно**. Функция `rawsend_payload_segmented` из `zapret-lib.lua` автоматически:
1. Отслеживает MSS для каждого TCP-соединения
2. Если отправляемая часть (включая seqovl) превышает MSS -- дополнительно режет по MSS
3. Каждый под-сегмент отправляется с корректным TCP sequence
**Пример:** если вы задали `pos=0,-1:seqovl=10000`, это не вызовет ошибку. `rawsend_payload_segmented` отправит несколько TCP-сегментов с начальным sequence -(10000), общим размером 10000 байт seqovl-pattern, и в последних сегментах -- реальные данные payload.
> [!note] Проще говоря
> Слишком большой сегмент функция сама режет на допустимые куски и каждому ставит правильный seq; настраивать ничего не нужно. Так же это устроено в [[Zapret2/desync/multisplit#Автосегментация по MSS|multisplit]].
---
## Место в общем скелете
Тело `tcpseg` построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в [[жизненный цикл desync-функции]] — здесь только отклонения. Гвард (guard) — проверка, которая решает, работать ли функции с этим конкретным пакетом. Названия стадий в левой колонке — ссылки на их подробный разбор.
| Стадия общего скелета | Что делает `tcpseg` |
|:----------------------|:----------------|
| [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff\|1. Отсев транспорта]] | только TCP; на не-TCP делает `instance_cutoff` (кроме связанного ICMP) |
| [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления\|2. Направление]] | `dir=out` по умолчанию, отключается от входящего |
| [[Zapret2/жизненный цикл desync-функции#Стадия 3. Ранняя проверка аргументов\|3. Аргументы]] | **`pos` обязателен** — без него `error()`; `optional` + отсутствующий `blob` → тихий выход |
| [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload\|4. Данные]] | штатная цепочка `blob → reasm → payload` |
| [[Zapret2/жизненный цикл desync-функции#Стадия 5. Три гварда\|5. Гварды]] | штатные `#data>0`, `direction_check`, `payload_check` (по умолчанию `known`) |
| [[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)\|6. replay]] | использует только первую половину развилки: работает на первой части, остальные **не дропает** |
| [[Zapret2/жизненный цикл desync-функции#Стадия 7. Собственно техника\|7. Своя техника]] | `resolve_range` разрешает ровно два маркера в диапазон, отправляется один сегмент с этим куском данных |
| [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация\|8. Вердикт]] | **не выносит никакого** — оригинальный пакет уходит как обычно |
Последние две строки таблицы объясняют, почему `tcpseg` ведёт себя не как остальные техники сегментации. Он ничего не дропает и не заменяет исходный поток, а лишь **дополнительно** отправляет выбранный кусок данных. Поэтому его применяют как строительный блок в связке с другими инстансами, а не как самостоятельную стратегию.
> [!note] Проще говоря
> `tcpseg` проходит все стадии скелета, но на последней ничего не решает. Поэтому оригинальный пакет он сам не заменяет: это делает другой инстанс, например `drop`.
---
## Псевдокод алгоритма
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл desync-функции]]:
```lua
function tcpseg(ctx, desync)
-- 1. Проверка: только TCP
if not desync.dis.tcp then
if not desync.dis.icmp then instance_cutoff_shim(ctx, desync) end
return -- без вердикта
end
-- 2. Cutoff противоположного направления
direction_cutoff_opposite(ctx, desync)
-- 3. pos ОБЯЗАТЕЛЕН
if not desync.arg.pos then
error("tcpseg: no pos specified")
end
-- 4. Проверка optional blob
if optional and blob specified and blob not exists then
DLOG("tcpseg: blob not found. skipped")
return -- без вердикта, тихий skip
end
-- 5. Выбор данных
data = blob_or_def(blob) or reasm_data or dis.payload
-- 6. Проверки: данные не пусты, направление OK, payload OK
if #data > 0 and direction_check() and payload_check() then
-- 7. Только первый replay
if replay_first(desync) then
-- 8. Разрешение ДИАПАЗОНА (ровно 2 маркера)
pos = resolve_range(data, l7payload, pos_arg)
-- pos = {start, end} или nil
if pos then
-- 9. Вырезать диапазон
part = data:sub(pos[1], pos[2])
-- 10. seqovl
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 not blob_exist(seqovl_pattern) then
-- используем нулевой паттерн
else
pat = blob(seqovl_pattern)
end
end
part = pattern(pat, 1, seqovl) .. part
end
-- 11. Отправка с автосегментацией
rawsend_payload_segmented(desync, part, pos[1]-1-seqovl)
else
DLOG("tcpseg: range cannot be resolved")
end
else
-- 12. Не первый replay -- ничего не делаем
DLOG("tcpseg: not acting on further replay pieces")
end
end
-- 13. ВЕРДИКТ НЕ ВЫНОСИТСЯ -- return без значения
end
```
**Ключевые отличия от псевдокода [[multisplit]]:**
- Шаг 3: `error()` при отсутствии `pos` (в multisplit -- дефолт `"2"`)
- Шаг 8: `resolve_range` вместо `resolve_multi_pos` (2 маркера, не список)
- Шаг 9: `data:sub(pos[1], pos[2])` -- диапазон, не цикл по частям
- Шаг 11: одна отправка (не цикл)
- Шаг 13: **нет return VERDICT_DROP / VERDICT_PASS** -- нет `replay_drop_set()`, нет `nodrop`
> [!note] Проще говоря
> По шагам функция делает вот что. Проверяет, что пакет TCP, и отключается от противоположного направления. Требует `pos`. Если задан blob, а его нет и указан `optional`, тихо выходит. Выбирает данные. На первой части replay разрешает два маркера в диапазон, вырезает его, при `seqovl` приклеивает слева паттерн и отправляет результат одним вызовом с автосегментацией. На остальных частях ничего не делает. Вердикт не возвращает.
---
## Нюансы и подводные камни
### 1. Работает только с TCP
Если текущий пакет не TCP (UDP, ICMP и т.д.), `tcpseg` делает `instance_cutoff_shim` -- отключает себя для этого потока навсегда. Исключение: связанные ICMP-пакеты (related icmp) не вызывают cutoff. Почему TCP-приёмы не переносятся на UDP, разобрано в [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|нюансе multisplit]], а сам `instance_cutoff` описан в [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|стадии 1 жизненного цикла]].
### 2. pos обязателен -- нет дефолта
В отличие от [[multisplit]] (дефолт `"2"`), `tcpseg` без `pos` вызовет `error()`. Всегда указывайте `pos`. Это жёсткое требование: `error()` — ошибка Lua: `nfqws2` при этом не падает, ядро пишет её в лог и прекращает обработку пакета, но профиль с таким инстансом фактически не работает (см. [[Zapret2/жизненный цикл desync-функции#Стадия 3. Ранняя проверка аргументов|стадию 3 жизненного цикла]]).
### 3. Ровно 2 маркера -- не больше и не меньше
`resolve_range` в C-коде проверяет `ctm != 2` и вызывает `luaL_error`. Один маркер или три маркера -- ошибка. Проще говоря: пишите ровно два маркера через запятую, например `pos=0,-1` или `pos=0,midsld`.
### 4. Не выносит вердикт -- нужен drop
Самая частая ошибка: забыть `drop` после `tcpseg`. Без `drop` оригинальный пакет уйдёт, и сервер получит данные дважды. Это может работать (TCP-стек дедуплицирует), но неоптимально и может путать DPI. Проще говоря: оригинал `tcpseg` не выбрасывает, потому что вердикта у него нет; выбрасывает `drop`. Подробно — в разделе [[#Вердикт и комбинация с drop|Вердикт и комбинация с drop]].
### 5. drop по умолчанию дропает ВСЕ payload
Функция `drop` по умолчанию работает с `payload=all`. Если `tcpseg` работает только с `known`, то `drop` без ограничений заблокирует и `unknown` payload. Здесь `known` — распознанные протоколы, `unknown` — данные, тип которых движок не распознал ([[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|подробнее о типах payload]]); фильтры описаны в разделе [[#C) Standard payload|C) Standard payload]]. Решение:
```bash
--lua-desync=tcpseg:pos=0,-1:seqovl=5 --lua-desync=drop:payload=known
```
### 6. Маркер 0 -- это начало, не "нулевая позиция Lua"
В `resolve_range` маркер `0` -- это абсолютный маркер начала данных. Внутри C-кода `pos[0]=0` соответствует первому байту. При возврате в Lua конвертируется в 1-based (`pos[0]+1`). Проще говоря: в командной строке `0` — первый байт данных, а пересчёт в нумерацию Lua с единицы функция делает сама.
### 7. Fooling применяется к реальному сегменту
В отличие от [[fakedsplit]], где fooling идёт только на фейки, в `tcpseg` fooling модифицирует **реальный** отправляемый сегмент. Использование `tcp_ack=-66000` приведёт к тому, что сервер отбросит сегмент. Безопасные fooling-опции: `ip_id=rnd`, `tcp_ts_up`, IPv6 extension headers. Fooling у соседей описан в разделах [[Zapret2/desync/fakedsplit#D) Standard fooling|fakedsplit]] и [[Zapret2/desync/fakeddisorder#D) Standard fooling|fakeddisorder]], а сами опции — в [[ts-and-fooling]]; параметры `tcpseg` — в разделе [[#D) Standard fooling|D) Standard fooling]].
### 8. seqovl=10000 не вызовет ошибку
В отличие от nfqws1, где большие значения seqovl вызывали ошибку, nfqws2 автоматически сегментирует по MSS. Большой seqovl просто создаст много под-сегментов. Как они строятся, описано в разделе [[#Автосегментация по MSS|Автосегментация по MSS]].
### 9. Нет nodrop -- потому что нет drop
В `tcpseg` нет параметра `nodrop`, потому что функция и так никогда не дропает. Для управления вердиктом используйте отдельный инстанс `drop`. Проще говоря: `nodrop` нужен там, где функция сама выбрасывает оригинал и иногда этого не хочется; у `tcpseg` выбрасывать нечего. Как это выглядит у соседа — в [[Zapret2/desync/multisplit#4. nodrop создаёт дублирование|нюансе 4 multisplit]].
### 10. При неразрешённом диапазоне -- тихий пропуск
Если оба маркера не разрешаются (например, `pos=midsld,endhost` для `unknown` payload), `resolve_range` вернёт `nil`, и `tcpseg` залогирует "range cannot be resolved" и ничего не сделает. Без ошибки, без вердикта. Проще говоря: если диапазон определить не удалось, ничего не отправляется, а ошибки в работе нет; о причине можно узнать из лога.
### 11. Повторная отправка (repeats) без drop -- атака повторами
`tcpseg` с `repeats=N` без `drop` отправит N копий сегмента, а затем оригинальный пакет тоже уйдёт. Это осознанная стратегия для забивания буфера DPI (см. blockcheck2/15-misc.sh). Примеры повторов: [[#6. Повторная отправка начала HTTP-запроса (без drop)|6]], [[#7. Повторная отправка до midsld (TLS)|7]] и [[#10. Только первый байт TLS record (повторы)|10]]. Проще говоря: повторы вместе с оригиналом — задуманное поведение, а не ошибка.
---
## Отличия от других функций сегментации
Пять функций ниже отправляют данные TCP-сегментами, но различаются числом маркеров, порядком отправки, наличием фейков и тем, как устроен seqovl. Подробности о соседях — в разделах [[Zapret2/desync/multisplit#Отличия от других функций сегментации|multisplit]], [[Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder|multidisorder]], [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] и [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]].
| Аспект | `tcpseg` | `multisplit` | `multidisorder` | `fakedsplit` | `fakeddisorder` |
|:-------|:---------|:-------------|:----------------|:-------------|:----------------|
| Количество маркеров | **Ровно 2** (диапазон) | Список (любое кол-во) | Список (любое кол-во) | **Одна** | **Одна** |
| Что отправляется | **Часть** payload (диапазон) | **Весь** payload (разрезанный) | **Весь** payload (разрезанный) | **Весь** payload | **Весь** payload |
| Функция разрешения pos | `resolve_range` | `resolve_multi_pos` | `resolve_multi_pos` | `resolve_pos` | `resolve_pos` |
| pos обязателен | **Да** (error если нет) | Нет (дефолт "2") | Нет (дефолт "2") | Нет (дефолт "2") | Нет (дефолт "2") |
| Вердикт | **Нет** (не выносит) | `VERDICT_DROP` | `VERDICT_DROP` | `VERDICT_DROP` | `VERDICT_DROP` |
| nodrop | Нет (не нужен) | Да | Да | Да | Да |
| Порядок отправки | Один сегмент | Прямой (1->2->3) | Обратный (3->2->1) | Прямой | Обратный |
| Фейковые сегменты | **Нет** | **Нет** | **Нет** | Да (до 4 шт.) | Да (до 4 шт.) |
| seqovl тип | Только число | Только число | **Маркер** | Только число | **Маркер** |
| Fooling к | Реальному сегменту | Всем сегментам | Всем сегментам | Только к фейкам | Только к фейкам |
| ipfrag | Да | Да | Да | **Нет** | **Нет** |
| Нужен отдельный drop | **Да** | Нет (встроен) | Нет (встроен) | Нет (встроен) | Нет (встроен) |
Проще говоря: `multisplit`, `multidisorder`, `fakedsplit` и `fakeddisorder` режут весь payload и сами выбрасывают оригинал, а `tcpseg` отправляет один выбранный кусок и оригинал не трогает, поэтому ему нужен отдельный `drop`.
---
## Практические примеры
Каждый пример — готовая строка для [[Zapret2/profile#Анатомия профиля|профиля]]; под ней объяснено, что получится на выходе.
### 1. Минимальный seqovl без сегментации (TLS)
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,-1:seqovl=1 \
--lua-desync=drop
```
Отправляет весь payload с 1 нулевым байтом seqovl слева. Drop блокирует оригинал. Проще говоря: `pos=0,-1` — весь запрос, `seqovl=1` — один нулевой байт слева, `drop` не даёт оригиналу уйти следом (раздел [[#Вердикт и комбинация с drop|Вердикт и комбинация с drop]]).
### 2. seqovl с TLS-фейковым паттерном
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,-1:seqovl=5:seqovl_pattern=0x1603030000 \
--lua-desync=drop
```
5-байтовый TLS record header как seqovl-prefix. DPI видит начало "нового" TLS record перед настоящим ClientHello. Паттерн задаёт `seqovl_pattern`, см. [[#seqovl_pattern|seqovl_pattern]].
### 3. seqovl с предзагруженным фейком и tls_mod
```bash
--payload=tls_client_hello \
--blob=seqovl_pat:@fake_tls.bin \
--lua-init=seqovl_pat=tls_mod(seqovl_pat,'rnd') \
--lua-desync=tcpseg:pos=0,-1:seqovl=#seqovl_pat:seqovl_pattern=seqovl_pat \
--lua-desync=drop
```
Blob загружается из файла, рандомизируется через `tls_mod`, используется как seqovl_pattern с размером blob в качестве seqovl. Здесь `tls_mod` — набор изменений blob перед отправкой (он подстраивает ClientHello под текущее соединение; опции — в [[Zapret2/desync/fake#Опции tls_mod|Опции tls_mod]]), `--blob=seqovl_pat:@fake_tls.bin` загружает blob из файла, а `--lua-init` готовит blob заранее.
### 4. Динамический случайный seqovl через luaexec
```bash
--lua-desync=luaexec:code='desync.rnd=brandom_az(math.random(5,10))' \
--lua-desync=tcpseg:pos=0,-1:seqovl=#rnd:seqovl_pattern=rnd \
--lua-desync=drop:payload=known
```
На каждый пакет генерируется случайная строка из букв a-z длиной 5-10 символов. Используется как seqovl_pattern, а её размер -- как значение seqovl. Drop только для known payload. `luaexec` выполняет произвольный Lua-код ([[desync]]), а `seqovl=#rnd` берёт размер blob `rnd`.
### 5. seqovl с padencap и tls_mod (продвинутый)
```bash
--payload=tls_client_hello \
--blob=seqovl_pat:@fake_tls.bin \
--lua-desync=luaexec:code="desync.patmod=tls_mod(seqovl_pat,'rnd,dupsid,padencap',desync.reasm_data)" \
--lua-desync=tcpseg:pos=0,-1:seqovl=#patmod:seqovl_pattern=patmod \
--lua-desync=drop
```
Динамическая модификация фейка с учётом реального payload (через `desync.reasm_data`). Паттерн маскируется под TLS с padding, рандомизацией и дублированием session ID. `padencap` подкорректирует blob так, чтобы оригинальный payload стал частью padding extension — подробнее в разделе [[Zapret2/desync/fake#padencap — подробности|padencap — подробности]]; `rnd` и `dupsid` — другие опции `tls_mod` ([[Zapret2/desync/fake#Опции tls_mod|Опции tls_mod]]).
### 6. Повторная отправка начала HTTP-запроса (без drop)
```bash
--payload=http_req \
--lua-desync=tcpseg:pos=0,method+2:ip_id=rnd:repeats=20
```
Отправляет начало HTTP-запроса до маркера `method+2` включительно — по коду это первые 3 байта, `GET` из `GET /` (почему не `GE`, объяснено в предупреждении к описанию аргумента `pos`) 20 раз со случайными IP ID. Оригинальный пакет тоже уходит (нет drop). Стратегия забивания буфера DPI. Здесь `ip_id=rnd` — случайные IP ID, а `repeats=20` — 20 копий сегмента (параметры описаны в разделах [[#E) Standard ipid|E) Standard ipid]] и [[#H) Standard rawsend|H) Standard rawsend]]).
### 7. Повторная отправка до midsld (TLS)
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=100
```
Отправляет начало ClientHello до середины SNI 100 раз. DPI может переполнить свой буфер и перестать анализировать поток.
### 8. Отправка только hostname
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=host,endhost
```
Вырезает и отправляет **только** hostname из SNI. Без drop и без seqovl -- чисто для экспериментов или в составе сложной цепочки.
### 9. seqovl без сегментации + IP-фрагментация
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,-1:seqovl=5:ipfrag:ipfrag_disorder:ipfrag_pos_tcp=32 \
--lua-desync=drop
```
Весь payload с seqovl отправляется, TCP-сегмент дополнительно фрагментируется на IP-уровне в обратном порядке. Параметры `ipfrag` описаны в разделе [[#F) Standard ipfrag|F) Standard ipfrag]].
### 10. Только первый байт TLS record (повторы)
```bash
--payload=tls_client_hello \
--lua-desync=tcpseg:pos=0,1:ip_id=rnd:repeats=260
```
Отправляет начало TLS record 260 раз. Несмотря на заголовок примера, по коду `pos=0,1` захватывает первые **два** байта (см. предупреждение к описанию аргумента `pos`). Используется в blockcheck2 для тестирования misc-стратегий.
### 11. Комбинация: fake + tcpseg + drop (полная стратегия)
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--lua-desync=tcpseg:pos=0,-1:seqovl=5:seqovl_pattern=0x1603030000 \
--lua-desync=drop
```
1. `fake` отправляет фейковый TLS ClientHello с md5sig fooling
2. `tcpseg` отправляет настоящий payload с seqovl
3. `drop` блокирует оригинальный пакет
Здесь [[Zapret2/desync/fake#Зачем нужен fake|fake]] — отдельный пакет-обманка, который сервер отбрасывает из-за порчи заголовков (`tcp_md5` — вариант fooling), а `tls_mod` подстраивает фейковый ClientHello. Инстансы выполняются в порядке записи: сначала `fake`, затем `tcpseg`, затем `drop`.
### 12. Защита от отсутствующего blob
```bash
--lua-desync=tcpseg:pos=0,-1:blob=maybe_missing:optional:seqovl=5
```
Если blob `maybe_missing` не существует -- tcpseg тихо пропускает. Без ошибок. Флаг `optional` описан в разделе [[#A) Собственные аргументы tcpseg|A) Собственные аргументы tcpseg]].
### 13. HTTP: seqovl=1 + drop (минимальный рабочий пример)
```bash
--payload=http_req \
--lua-desync=tcpseg:pos=0,-1:seqovl=1 \
--lua-desync=drop
```
Один нулевой байт seqovl перед HTTP-запросом. Минимальная модификация, но может обмануть DPI, который ожидает HTTP-метод с первого байта TCP-потока.
### 14. wssize + tcpseg + drop (контроль TCP window)
```bash
--payload=tls_client_hello \
--lua-desync=wssize:wsize=1:scale=6 \
--lua-desync=tcpseg:pos=0,-1:seqovl=5:seqovl_pattern=fake_default_tls \
--lua-desync=drop
```
Сначала `wssize` устанавливает маленький TCP window, затем `tcpseg` отправляет payload с seqovl, и `drop` блокирует оригинал. Здесь `wssize` подменяет размер TCP window — поле в заголовке TCP, которым получатель сообщает, сколько байт он готов принять (`wsize=1:scale=6` — параметры окна); почему это крайняя мера, разобрано в [[Zapret/wssize|отдельной заметке]].
---
## Варианты tcpseg без drop из набора стратегий GUI
Для каталога стратегий [[guide|Zapret 2 GUI]] готовился набор «упрощённых» вариантов на `tcpseg`. Их идея: вместо того чтобы резать ClientHello на части, как [[multisplit]], отправить его одним сегментом и спрятать перед ним чужие данные через seqovl. Набор полезен как иллюстрация того, как `tcpseg` ведёт себя без `drop`, но в его исходном описании были ошибки: он обещал «один пакет вместо двух» и меньшую нагрузку на сеть. Ниже варианты разобраны заново, с поправками по исходному коду `lua/zapret-antidpi.lua`.
> [!warning] Названия и состав могут отличаться от текущего GUI
> Набор описан по черновику каталога стратегий. В каталоге GUI `strategy_catalogs/winws2/tcp.txt` этих вариантов на сентябрь 2026 нет, поэтому внутренние имена (`tcpseg_226_simple` и т.п.) и номера файлов `tls_clienthello_*.bin` проверяйте в своей версии программы. В черновике перед флагами стратегии стоял общий фильтр категории, который GUI подставляет сам, — ниже он опущен.
### Общая часть всех вариантов
Каждый вариант строится из одних и тех же деталей. Сначала через `--blob` загружается готовый TLS ClientHello из файла программы, например `--blob=bin_tls18:@bin\tls_clienthello_18.bin` (что такое блоб — в [[blob]]). Затем идут фильтры: [[payload|--payload=tls_client_hello]] ограничивает стратегию первым сообщением TLS, а [[out-range|--out-range=-d10]] — первыми десятью пакетами с данными. После них вызывается сама функция:
```bash
--lua-desync=tcpseg:pos=0,-1:seqovl=226:seqovl_pattern=bin_tls18
```
Маркеры `pos=0,-1` берут весь ClientHello целиком. `seqovl=226` добавляет перед ним 226 байт из блоба `bin_tls18` и сдвигает номер последовательности сегмента на 226 назад (механика — в разделе [seqovl -- скрытый фейк внутри сегмента](#seqovl----скрытый-фейк-внутри-сегмента)). Сервер считает эти 226 байт уже пройденной частью потока и отбрасывает их, а DPI, который не учитывает перекрытие, видит в начале потока постороннее содержимое.
Проще говоря: DPI первым делом натыкается на начало чужого ClientHello из файла, а сервер получает только настоящий.
### Главная поправка: оригинал тоже уходит
Во всех вариантах набора нет `--lua-desync=drop`. Как разобрано в нюансе «Не выносит вердикт -- нужен drop», `tcpseg` не блокирует оригинальный пакет. Поэтому в сеть уходят **два** пакета: сегмент с seqovl и следом нетронутый оригинальный ClientHello с настоящим SNI. Утверждение «один пакет без резки» неверно, и экономии трафика по сравнению с [[multisplit]] нет: `multisplit` сам выносит `VERDICT_DROP` и отправляет только нарезанные части, а здесь к оригиналу добавляется ещё один полноразмерный сегмент.
Что это значит на практике, зависит от варианта:
- **без fooling** сервер принимает данные из сегмента с seqovl, а оригинал отбрасывает как повтор. DPI видит оба пакета, и сработает ли обход, зависит от того, по какому из них DPI принимает решение;
- **с fooling** (последние три строки таблицы ниже) сервер отбрасывает сам сегмент с seqovl, потому что fooling в `tcpseg` применяется к реальному сегменту (нюанс 7). Сегмент превращается в фейк, который видит только DPI, а соединение продолжается на оригинале. По смыслу это ближе к [[fake]], чем к сегментации.
Оба поведения подтверждены на стенде (сентябрь 2026, `nfqws2` и TLS-сервер на Linux): без fooling сервер принял данные из сегмента с seqovl, а оригинал пришёл повтором; с `tcp_ack=-66000` или `tcp_flags_unset=ack` сервер сегмент отверг и принял оригинал. В обоих случаях соединение устанавливалось без задержки.
Чтобы получить классическую схему, где до сервера доходит только сегмент с seqovl, добавьте `drop` в конце, как в разделе «Быстрый старт»: `--lua-desync=tcpseg:pos=0,-1:seqovl=226:seqovl_pattern=bin_tls18 --lua-desync=drop`.
### Таблица вариантов
| Вариант | Что добавлено к общей части | Флаги (без загрузки блоба) |
|:--|:--|:--|
| Простой seqovl | Перекрытие 211, 226, 286 или 308 байт шаблонами `tls_clienthello_5`, `_18`, `_11`, `_9` соответственно | `--lua-desync=tcpseg:pos=0,-1:seqovl=226:seqovl_pattern=bin_tls18` |
| Дубль первого пакета | Перед `tcpseg` копии ClientHello на первом пакете с данными | `--out-range=-d1 --lua-desync=send:repeats=2 --out-range=-d10 --lua-desync=tcpseg:…` |
| Дубль в первых трёх пакетах | То же, но окно считается по всем перехваченным пакетам | `--out-range=-n3 --lua-desync=send:repeats=2 --out-range=-d10 --lua-desync=tcpseg:…` |
| Google SNI без файлов | Шаблон собирается из встроенного `fake_default_tls` с SNI `www.google.com` | `--lua-init="tls_google = tls_mod(fake_default_tls,'sni=www.google.com')"` и `seqovl_pattern=tls_google` |
| datanoack | У сегмента снят флаг ACK, сервер его отбросит | `…:seqovl_pattern=bin_tls18:tcp_flags_unset=ack` |
| Фиксированный TTL | Сегмент живёт 5 хопов и не доходит до сервера | `…:seqovl_pattern=bin_tls18:ip_ttl=5:ip6_ttl=5` |
| badseq | Номер подтверждения сдвинут на −66000 | `…:seqovl_pattern=bin_tls18:tcp_ack=-66000` |
Про строки с дублями. Функция `send` отправляет копию текущего пакета и сама его не блокирует, поэтому `repeats=2` добавляет две копии поверх оригинала. Действует она только на ClientHello, потому что выше стоит `--payload=tls_client_hello`. В черновике вариант с `-n3` описывался как «дублирование первых трёх пакетов», но это не так: `n` считает все перехваченные пакеты соединения, включая рукопожатие, а фильтр payload пропускает к `send` только ClientHello. Какой по счёту пакет окажется ClientHello, зависит от того, какие пакеты перехватывает WinDivert, — это разобрано в [[zapret2_start_cutoff]]. Формат диапазонов — в [[out-range]].
Про строки с fooling. `ip_ttl=5` жёстко задаёт время жизни: если сервер ближе пяти хопов, сегмент до него дойдёт и схема поведёт себя как вариант без fooling. `tcp_ack=-66000` на Linux-серверах надёжно отбрасывается только вместе с `tcp_ts_up`. Подробнее обо всех обманках — в [[основные флаги]] и [[ts-and-fooling]].
### С чего начинать проверку
Разумный порядок — от простого к сложному: сначала вариант с Google SNI без дублей (ему не нужны файлы), затем тот же вариант с дублем, и только если оба не помогли — стратегии с резкой через [[multisplit]] или [[multidisorder]]. Проверять каждую ступень нужно новым соединением, а не обновлением вкладки: автоподбор и F5 легко показывают ложный успех, см. [[verify-strategy]].
---
## 📚 См. также
- [[desync]] — обзор всех функций `--lua-desync` и общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты
- [[жизненный цикл desync-функции]] — восемь стадий, общих для всех техник дурения: отсев транспорта, выбор данных, replay, вердикты
- [[структура desync и диссекта]] — устройство таблицы `desync` и диссекта пакета
- [[multisplit]] — разрез всего payload на несколько сегментов с собственным вердиктом `VERDICT_DROP`; там же подробный разбор seqovl
- [[multidisorder]] — тот же разрез, но сегменты уходят в обратном порядке; seqovl там работает иначе
- [[fakedsplit]] · [[fakeddisorder]] — разрез по одной позиции с подмешиванием фейковых сегментов
- [[hostfakesplit]] — разрез по границам имени хоста с подмешиванием фейкового имени
- [[oob]] — сегментация с urgent-байтом вместо разреза
- [[fake]] — отдельный фейковый пакет, с которым `tcpseg` комбинируют в полной стратегии
- [[blob]] — как объявлять и передавать данные для аргументов `blob` и `seqovl_pattern`
- [[payload]] — распознавание типов протоколов, от которого зависит работа относительных маркеров
- [[ts-and-fooling]] — опции fooling и почему в `tcpseg` большинство из них вредны
- [[verify-strategy]] — как честно проверить, работает ли подобранная комбинация
- [[последовательность аргументов]] — как выстраивается цепочка инстансов в профиле
- [[profile]] — где `--lua-desync` живёт среди остальных настроек
- [[DPI/DPI|DPI и ТСПУ]] — как устроена инспекция трафика, против которой работает `tcpseg`
---
> **Источники:** `lua/zapret-antidpi.lua:1030-1076`, `nfq2/lua.c:3281-3328` (resolve_range), `docs/manual.md:4248-4274`, `docs/readme.md:394-412`, `blockcheck2.d/standard/15-misc.sh`, `blockcheck2.d/standard/23-seqovl.sh` из репозитория zapret2.
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/desync/tcpseg.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).