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

119 KiB
Raw Permalink Blame History

date tags aliases description
2026-02-17
zapret
zapret2
nfqws2
lua
lua-desync
antidpi
tcp
tcpseg
seqovl
tcpseg
Справочник по функции tcpseg в Zapret2/nfqws2: отправка TCP-сегмента из диапазона pos, seqovl без сегментации, связка с drop и примеры стратегий.

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

tcpseg -- отправка TCP-сегмента из диапазона (zapret2 / nfqws2)

Коротко и по-простому

Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке Host: запроса. ТСПУ (Технические средства противодействия угрозам — оборудование DPI/DPI, то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения и по нему решает, что делать с соединением дальше.

HTTP-запросы и TLS-соединения идут поверх протокола TCP, а TCP отправляет данные кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер по этим номерам. Кроме того, сервер принимает только те байты, чьи номера попадают в его окно приёма (receive window): оно начинается с номера следующего байта, которого сервер ждёт. Всё, что лежит левее этой границы, TCP-стек молча отбрасывает как повтор или мусор — это штатное поведение, а не ошибка. Почему сборку по номерам гарантирует именно TCP, разобрано в Zapret2/desync/multisplit#1. Работает только с 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), у tcpseg нет значения pos по умолчанию: без него функция выдаёт ошибку. Все виды маркеров разобраны в разделе #Маркеры позиций (pos) -- диапазон из двух маркеров.

Кусок отправляют отдельным сегментом, который функция собирает сама, и его параметры задаются аргументами команды: какие байты берутся (pos), сколько байт фальшивки приклеивается слева и как сдвигается seq (seqovl), сколько раз сегмент отправляется (repeats) и какие заголовки в нём меняются (fooling, о нём ниже). Этим можно воспользоваться тремя способами. Первый — отправить весь ClientHello одним сегментом, но с фальшивым префиксом слева (приём seqovl, о нём ниже). Второй — многократно отправить начало запроса в расчёте на то, что буфер DPI окажется забит. Третий — поставить tcpseg кирпичиком в цепочку рядом с другими инстансами, то есть записями --lua-desync=… в Zapret2/profile#Анатомия профиля (наборе правил Zapret 2 для определённого трафика), например рядом с drop (отдельной функцией, которая выбрасывает оригинальный пакет). Подробно — в разделе #Зачем нужен tcpseg.

К отправляемому куску можно добавить приём seqovl (Sequence Overlap, «перекрытие по номерам последовательности»). tcpseg дописывает слева N байт фальшивки — по умолчанию это нули, другой узор задаёт seqovl_pattern — и сдвигает seq сегмента назад на те же N. Для сервера эти байты оказываются левее границы окна приёма: он их выбрасывает и принимает остальное. DPI же может прочитать сегмент целиком и принять фальшивку за начало потока: так бывает, если он читает сегмент от начала и не сверяется с тем, какие байты примет сервер. Обычно фейк прячут от сервера иначе: отдельному пакету-обманке (Zapret2/desync/fake#Зачем нужен fake) портят заголовки, например TTL (счётчик «времени жизни» пакета) или номера seq и ack (номер подтверждения), чтобы пакет не дошёл до сервера или сервер его отбросил, а DPI прочитал; такая порча называется fooling (Zapret2/ts-and-fooling#Что такое fooling). У seqovl заголовки остаются правильными, портить их не приходится. В tcpseg seqovl относится к единственному отправляемому сегменту, поэтому весь ClientHello можно отправить одним сегментом с префиксом, без нарезки. Механика по байтам — в разделе #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, а как движок складывает вердикты нескольких инстансов — в Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация. Есть и нюанс с фильтрами: drop по умолчанию выбрасывает пакеты любого типа, а tcpseg работает только с распознанными протоколами, см. #5. drop по умолчанию дропает ВСЕ payload.

Связка из быстрого старта tcpseg:pos=0,-1:seqovl=1 + drop заменяет оригинальный пакет одним сегментом. Сам tcpseg вердикт не выносит, поэтому оригинал выбрасывает второй инстанс drop, а в сеть уходит тот же ClientHello с одним байтом мусора спереди и seq, сдвинутым на единицу назад. ТСПУ видит поток, который начинается с этого байта, и разбор ClientHello может сбиться; сервер отрезает байт, лежащий левее окна приёма (на схеме — левее вертикальной линии seq=0). Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.

!desync-anim-tcpseg.svg

Самая короткая команда — та, что на схеме: весь payload (pos=0,-1), один нулевой байт спереди (seqovl=1) и drop следом. Строку --lua-desync=… пишут в профиль. Для HTTPS к ней добавляют фильтр --payload=tls_client_hello, чтобы функция срабатывала только на TLS ClientHello, как в #1. Минимальный seqovl без сегментации (TLS); остальные готовые команды собраны в разделах #Быстрый старт и #Практические примеры. Вот команда без фильтра:

--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) и #11. Повторная отправка (repeats) без drop -- атака повторами.

Готового рецепта на все случаи tcpseg не даёт. Расчёт seqovl — на DPI, который читает сегмент от начала и не сверяется с окном приёма сервера (так устроены не все DPI), а работает ли выбранная комбинация у конкретного провайдера, проверяют на практике по Zapret2/verify-strategy#Короткий чек-лист честной проверки. tcpseg работает только с TCP и самостоятельной стратегией обычно не служит: это строительный блок, который ставят в связку с другими инстансами. Близкие соседи: multisplit режет весь payload на несколько сегментов и сам выбрасывает оригинал; Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder отправляет куски в обратном порядке; Zapret2/desync/fakedsplit#Зачем нужен fakedsplit и Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder подмешивают к настоящим кускам фейки; Zapret2/desync/fake#Зачем нужен fake — отдельный пакет-обманка. Если к tcpseg без drop добавить fooling, из-за которого сервер отбросит сам сегмент (например, tcp_ack=-66000), по смыслу он станет ближе к fake, чем к сегментации: об этом раздел #Варианты 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) -- диапазон из двух маркеров). VERDICT_DROP — решение «перехваченный пакет не отправлять», VERDICT_PASS — «пропустить как есть»; как движок складывает такие решения от разных функций, описано в Zapret2/жизненный цикл desync-функции#Стадия 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 -- скрытый фейк внутри сегмента, а вердикт — в разделе #Вердикт и комбинация с 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)). Второй: начало запроса отправляется много раз, и DPI, по описанию в примере #7. Повторная отправка до midsld (TLS), может переполнить свой буфер и перестать анализировать поток. Третий: tcpseg сам оригинал не заменяет, поэтому его ставят в цепочку рядом с инстансом, который это сделает, например с drop, или с luaexec (он выполняет произвольный Lua-код, как в примере #4. Динамический случайный 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):

--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 и C) Standard payload.

seqovl с кастомным паттерном (TLS):

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

Повторная отправка начала HTTP-запроса:

--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).

Динамический seqovl со случайным паттерном:

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


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

Внутри 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. Третий — payload текущего пакета, desync.dis.payload: здесь dis — диссект, то есть пакет, заранее разобранный движком на поля (его устройство — в структура desync и диссекта). Lua-оператор or возвращает первый истинный операнд, поэтому цепочка читается «сверху вниз, берём первое, что есть». Та же цепочка общая для большинства функций дурения и описана как Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload.

Если blob задан, а относительные маркеры вроде midsld не срабатывают, причина может быть в том, что движок не распознал содержимое blob как TLS или HTTP. Тип payload — это название распознанного протокола вроде tls_client_hello или http_req; как он определяется, описано в Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока).

[!note] Проще говоря Функция спрашивает: «мне подсунули blob? — беру его. Есть запрос, собранный из кусков? — беру его. Иначе беру тело текущего пакета». Маркеры и seqovl всегда работают по тем данным, которые выиграли этот выбор.


Маркеры позиций (pos) -- диапазон из двух маркеров

!

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#Как маркеры разрешаются в коде при разборе 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 (и чем он отличается от протокола потока). Те же маркеры с примерами для разрезов разобраны в Zapret2/desync/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. При неразрешённом диапазоне -- тихий пропуск).

Важные нюансы 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 обязателен -- нет дефолта, #3. Ровно 2 маркера -- не больше и не меньше и #6. Маркер 0 -- это начало, не "нулевая позиция Lua". Про удаление позиции в multisplit — Zapret2/desync/multisplit#2. Разрез в самом начале данных отбрасывается его статьи.


seqovl -- скрытый фейк внутри сегмента

seqovl (Sequence Overlap) -- техника скрытого замешивания фейковых данных в реальный TCP-сегмент через манипуляцию TCP sequence number. В tcpseg seqovl применяется к отправляемому сегменту (он всегда один).

Смысл приёма в том, что TCP-получатель принимает не все байты подряд. Он держит окно приёма (receive window) и принимает байты, номера которых попадают в него, начиная с номера следующего ожидаемого байта; всё левее этой границы отбрасывается как повтор или мусор. Если приклеить слева фальшивку и сдвинуть seq назад на её длину, фальшивка окажется левее границы: сервер её выбросит, а DPI, читающий сегмент от начала, увидит её вместе с остальным. Полное объяснение по шагам — в Zapret2/desync/multisplit#Принцип работы seqovl; ниже те же цифры для tcpseg, где сегмент один.

В Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие приём с тем же названием устроен иначе: там он перезаписывает буфер сокета на сервере через перекрытие сегментов, поэтому переносить знания о seqovl между функциями нужно осторожно.

Принцип работы seqovl

!desync-anim-hub-seqovl.svg

Схема выше показывает один сегмент с pos=0,-1 и seqovl=10. TCP seq: 1000 — номер, с которого начинались бы данные без seqovl; после seqovl он уменьшается на 10, а перед данными встают 10 байт паттерна.

!

Во втором случае отправляется только часть payload — от начала до midsld; -5 в seq — сдвиг относительно начала отправляемого куска.

[!note] Проще говоря tcpseg дописывает перед куском лишние байты и подписывает их номерами, которые сервер уже не ждёт. Сервер выбрасывает эти байты, а DPI, читающий сегмент целиком, видит их вместе с настоящими данными. В tcpseg сегмент один, поэтому seqovl относится ровно к нему; в multisplit он относится только к первому из нескольких сегментов.

Зачем seqovl лучше обычного fooling

Обычный способ спрятать фейк от сервера — Zapret2/ts-and-fooling#Что такое fooling: пакету-обманке портят заголовки (TTL, номера seq или ack, опцию md5sig), и сервер (или маршрутизатор по пути) отбрасывает такой пакет целиком. seqovl добивается похожего результата другим путём; таблица сравнивает оба подхода и совпадает с той, что приведена в Zapret2/desync/multisplit#Зачем seqovl лучше обычного fooling.

Критерий Обычный 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.

# 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.

Важно в tcpseg: seqovl -- только число, маркеры не поддерживаются (как и в multisplit, в отличие от multidisorder и fakeddisorder, где seqovl может быть маркером). Как seqovl задаётся маркером там, разобрано в разделах Zapret2/desync/multidisorder#seqovl как маркер и Zapret2/desync/fakeddisorder#seqovl — маркер, а не число.


Вердикт и комбинация с drop

Вердикт — решение движка о судьбе перехваченного оригинального пакета: VERDICT_PASS пропускает его как есть, VERDICT_DROP выбрасывает, VERDICT_MODIFY заменяет пакет изменённым; отсутствие вердикта приравнивается к VERDICT_PASS. Полная таблица и правила сложения вердиктов от разных инстансов — в Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация.

Ключевая особенность tcpseg: функция не выносит вердикт. Она не возвращает ни VERDICT_DROP, ни VERDICT_PASS. Это означает:

  1. После выполнения tcpseg оригинальный пакет не блокируется
  2. Следующие инстансы в цепочке продолжают обработку
  3. Если после tcpseg нет других инстансов -- оригинальный пакет уйдёт как есть

Вердикты нескольких инстансов складываются по правилу «VERDICT_MODIFY перебивает VERDICT_PASS, VERDICT_DROP перебивает оба»: поэтому drop в профиле выбрасывает оригинал, даже если сам tcpseg ничего не решил. Дроп цепочку не обрывает: инстансы после него всё равно вызываются и видят тот же пакет.

Для замещения оригинального пакета tcpseg нужно комбинировать с drop:

# tcpseg отправляет payload с seqovl, drop блокирует оригинал
--lua-desync=tcpseg:pos=0,-1:seqovl=5 --lua-desync=drop

Нюанс с payload-фильтрами: по умолчанию tcpseg работает только с known payload, а drop -- с all. Если вы хотите дропать только известные payload (чтобы неизвестные проходили без изменений), укажите:

--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 и в Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока).

!wiki-anim-z2-tcpseg-drop.svg

[!note] Проще говоря tcpseg только добавляет в сеть свой сегмент, а оригинал не трогает, пока этого не сделает другой инстанс. С drop данные уходят один раз, сегментом tcpseg; без drop дважды. Как multisplit решает то же самое сам и зачем ему для отладки аргумент nodrop, описано в Zapret2/desync/multisplit#4. nodrop создаёт дублирование.


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

Аргументы делятся на собственные аргументы 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=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=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
  • Поведение с 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. Ранняя проверка аргументов

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. Отсечение противоположного направления.


C) Standard payload

Параметр Значения По умолчанию
payload список типов через запятую known

Фильтр по типу payload на уровне Lua. Это дополнительный фильтр к --payload=... на уровне профиля. Тип payload — название протокола, который движок распознал в данных пакета; см. Zapret2/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 и 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, где сегмент с 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).


F) Standard ipfrag

IP-фрагментация поверх TCP-сегмента. Отправленный TCP-сегмент дополнительно фрагментируется на уровне IP. Проще говоря: сегмент, уже вырезанный tcpseg, при отправке ещё раз делится на части, но теперь средствами протокола IP, а собирает их обратно получатель на уровне IP. Пример — #9. 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). Эти параметры управляют отправкой.

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

!

Диапазон между маркерами 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)

!

Здесь отправляется только начало 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 (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.


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

Тело tcpseg построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в жизненный цикл desync-функции — здесь только отклонения. Гвард (guard) — проверка, которая решает, работать ли функции с этим конкретным пакетом. Названия стадий в левой колонке — ссылки на их подробный разбор.

Стадия общего скелета Что делает tcpseg
Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff только TCP; на не-TCP делает instance_cutoff (кроме связанного ICMP)
Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления dir=out по умолчанию, отключается от входящего
Zapret2/жизненный цикл desync-функции#Стадия 3. Ранняя проверка аргументов pos обязателен — без него error(); optional + отсутствующий blob → тихий выход
Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload штатная цепочка blob → reasm → payload
Zapret2/жизненный цикл desync-функции#Стадия 5. Три гварда штатные #data>0, direction_check, payload_check (по умолчанию known)
Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay) использует только первую половину развилки: работает на первой части, остальные не дропает
Zapret2/жизненный цикл desync-функции#Стадия 7. Собственно техника resolve_range разрешает ровно два маркера в диапазон, отправляется один сегмент с этим куском данных
Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация не выносит никакого — оригинальный пакет уходит как обычно

Последние две строки таблицы объясняют, почему tcpseg ведёт себя не как остальные техники сегментации. Он ничего не дропает и не заменяет исходный поток, а лишь дополнительно отправляет выбранный кусок данных. Поэтому его применяют как строительный блок в связке с другими инстансами, а не как самостоятельную стратегию.

[!note] Проще говоря tcpseg проходит все стадии скелета, но на последней ничего не решает. Поэтому оригинальный пакет он сам не заменяет: это делает другой инстанс, например drop.


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

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

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 — и почему, а сам instance_cutoff описан в Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff.

2. pos обязателен -- нет дефолта

В отличие от multisplit (дефолт "2"), tcpseg без pos вызовет error(). Всегда указывайте pos. Это жёсткое требование: error() — ошибка Lua: nfqws2 при этом не падает, ядро пишет её в лог и прекращает обработку пакета, но профиль с таким инстансом фактически не работает (см. Zapret2/жизненный цикл desync-функции#Стадия 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.

5. drop по умолчанию дропает ВСЕ payload

Функция drop по умолчанию работает с payload=all. Если tcpseg работает только с known, то drop без ограничений заблокирует и unknown payload. Здесь known — распознанные протоколы, unknown — данные, тип которых движок не распознал (Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)); фильтры описаны в разделе #C) Standard payload. Решение:

--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 и Zapret2/desync/fakeddisorder#D) Standard fooling, а сами опции — в ts-and-fooling; параметры tcpseg — в разделе #D) Standard fooling.

8. seqovl=10000 не вызовет ошибку

В отличие от nfqws1, где большие значения seqovl вызывали ошибку, nfqws2 автоматически сегментирует по MSS. Большой seqovl просто создаст много под-сегментов. Как они строятся, описано в разделе #Автосегментация по MSS.

9. Нет nodrop -- потому что нет drop

В tcpseg нет параметра nodrop, потому что функция и так никогда не дропает. Для управления вердиктом используйте отдельный инстанс drop. Проще говоря: nodrop нужен там, где функция сама выбрасывает оригинал и иногда этого не хочется; у tcpseg выбрасывать нечего. Как это выглядит у соседа — в Zapret2/desync/multisplit#4. nodrop создаёт дублирование.

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), #7. Повторная отправка до midsld (TLS) и #10. Только первый байт TLS record (повторы). Проще говоря: повторы вместе с оригиналом — задуманное поведение, а не ошибка.


Отличия от других функций сегментации

Пять функций ниже отправляют данные TCP-сегментами, но различаются числом маркеров, порядком отправки, наличием фейков и тем, как устроен seqovl. Подробности о соседях — в разделах Zapret2/desync/multisplit#Отличия от других функций сегментации, Zapret2/desync/multidisorder#Обратный порядок отправки — суть disorder, Zapret2/desync/fakedsplit#Зачем нужен fakedsplit и Zapret2/desync/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)

--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).

2. seqovl с TLS-фейковым паттерном

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

3. seqovl с предзагруженным фейком и tls_mod

--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), --blob=seqovl_pat:@fake_tls.bin загружает blob из файла, а --lua-init готовит blob заранее.

4. Динамический случайный seqovl через luaexec

--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 (продвинутый)

--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 — подробности; rnd и dupsid — другие опции tls_mod (Zapret2/desync/fake#Опции tls_mod).

6. Повторная отправка начала HTTP-запроса (без drop)

--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 и #H) Standard rawsend).

7. Повторная отправка до midsld (TLS)

--payload=tls_client_hello \
  --lua-desync=tcpseg:pos=0,midsld:ip_id=rnd:repeats=100

Отправляет начало ClientHello до середины SNI 100 раз. DPI может переполнить свой буфер и перестать анализировать поток.

8. Отправка только hostname

--payload=tls_client_hello \
  --lua-desync=tcpseg:pos=host,endhost

Вырезает и отправляет только hostname из SNI. Без drop и без seqovl -- чисто для экспериментов или в составе сложной цепочки.

9. seqovl без сегментации + IP-фрагментация

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

10. Только первый байт TLS record (повторы)

--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 (полная стратегия)

--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 — отдельный пакет-обманка, который сервер отбрасывает из-за порчи заголовков (tcp_md5 — вариант fooling), а tls_mod подстраивает фейковый ClientHello. Инстансы выполняются в порядке записи: сначала fake, затем tcpseg, затем drop.

12. Защита от отсутствующего blob

--lua-desync=tcpseg:pos=0,-1:blob=maybe_missing:optional:seqovl=5

Если blob maybe_missing не существует -- tcpseg тихо пропускает. Без ошибок. Флаг optional описан в разделе #A) Собственные аргументы tcpseg.

13. HTTP: seqovl=1 + drop (минимальный рабочий пример)

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

--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 готовился набор «упрощённых» вариантов на 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 ограничивает стратегию первым сообщением TLS, а out-range — первыми десятью пакетами с данными. После них вызывается сама функция:

--lua-desync=tcpseg:pos=0,-1:seqovl=226:seqovl_pattern=bin_tls18

Маркеры pos=0,-1 берут весь ClientHello целиком. seqovl=226 добавляет перед ним 226 байт из блоба bin_tls18 и сдвигает номер последовательности сегмента на 226 назад (механика — в разделе 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 — как устроена инспекция трафика, против которой работает 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: исходник этой заметки · скачать весь репозиторий одним zip-архивом.