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

981 lines
91 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
- udp
- fake
aliases:
- fake
description: "Справочник по fake в zapret2/nfqws2: фейковый пакет перед оригиналом, blob, tls_mod, fooling (tcp_md5, ttl), миграция с nfqws1 и 20 примеров."
---
> [!mirror] Резервное зеркало
> Актуальная версия этой страницы — на основной вики: [wiki2.zapret.moe/Zapret2/desync/fake](https://wiki2.zapret.moe/Zapret2/desync/fake)
# `fake` — прямой фейк (zapret2 / nfqws2)
## Коротко и по-простому
Когда браузер открывает сайт по HTTPS, первым сообщением с данными он отправляет TLS ClientHello («привет от клиента»). В нём браузер перечисляет, какое шифрование умеет, и открытым текстом называет нужный сайт: имя лежит в поле SNI (Server Name Indication, «указание имени сервера»). У обычного HTTP без шифрования то же имя стоит в строке `Host:` запроса. ТСПУ (Технические средства противодействия угрозам — оборудование [[DPI/DPI|DPI]], то есть глубокой инспекции трафика, которое стоит у провайдера) ищет это имя в первом пакете соединения и по нему решает, что делать с соединением дальше.
HTTP-запросы и TLS-соединения передаются по протоколу TCP, а TCP отправляет данные кусками — сегментами. У каждого байта в соединении есть порядковый номер (sequence number, коротко seq), и сервер раскладывает принятые куски в свой буфер по этим номерам. Если сервер примет чужие данные на те номера, где должен лежать настоящий запрос, поток развалится: настоящий ClientHello придёт с пересекающимися номерами, и, как описано в нюансе [[#2. Без fooling фейк бесполезен или вреден|«Без fooling фейк бесполезен или вреден»]], соединение оборвётся.
На этом строится `fake`. Движок Zapret 2 (`nfqws2`) перехватывает исходящий пакет с ClientHello или HTTP-запросом, а `fake` перед ним отправляет отдельный пакет-обманку — фейк, то есть заранее заготовленные данные с ложным содержимым, например ClientHello с безобидным именем `www.w3.org` вместо настоящего. Сам оригинал `fake` не трогает и не режет: он уходит следом как ни в чём не бывало. У функции нет вердикта, то есть она не приказывает выбросить перехваченный пакет, как это делают функции сегментации вроде [[Zapret2/desync/multisplit#Зачем нужен multisplit|multisplit]]. Отсюда порядок: сначала фейк, потом настоящий запрос. Данные для фейка берутся из аргумента `blob`. Blob — это заранее заготовленный набор байт с именем; у Zapret 2 есть три готовых, `fake_default_tls`, `fake_default_http` и `fake_default_quic` (что внутри, разобрано в разделе [[#Стандартные blob-ы|Стандартные blob-ы]]). Аргумент `blob` у `fake` обязателен: без него функция завершается ошибкой и молча шаг не пропускает ([[#5. blob обязателен — без optional будет Lua exception|нюанс 5]]).
Главное, что нужно понять о `fake`: фейк должен дойти до DPI, но не быть принят сервером. Если сервер примет его за настоящие данные, соединение сломается, как описано выше. Поэтому фейк намеренно портят: меняют его служебные поля (заголовки) так, чтобы сервер или промежуточные маршрутизаторы его отбросили, а DPI, по замыслу, всё равно прочитал. Такая порча называется fooling («одурачивание»); что это и зачем, разобрано в заметке [[ts-and-fooling]], в разделе [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]. Два самых частых способа. `tcp_md5` добавляет к TCP-заголовку фейка опцию с подписью MD5: серверы на Linux отбрасывают такие пакеты, если MD5-аутентификация у них не настроена. `ip_ttl=1` (для IPv6 — `ip6_ttl=1`) задаёт пакету TTL — предельное число промежуточных узлов, через которые он пройдёт; при значении 1 пакет умирает уже на первом хопе (хоп — один переход между узлами сети), поэтому до сервера не доходит, а DPI его увидит, если стоит ближе к клиенту, чем сервер. Есть и другие способы: испорченная контрольная сумма (`badsum`), неверный номер seq и так далее. Все они собраны в разделе [[#D) Standard fooling|D) Standard fooling]], а выбор под конкретную среду разобран в [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталоге способов]].
В итоге сервер получает правильные данные, а DPI сначала видит ложные. Фейк сервер должен отбросить из-за порчи, а принять только настоящий запрос и ответить как обычно; если порча не сработала и сервер принял фейк, соединение может сломаться (нюанс 2 ниже). DPI читает фейк первым и может принять решение по нему: пропустить соединение из-за безобидного SNI, потерять состояние потока из-за невалидных данных или не суметь заблокировать, потому что в фейке другое имя (подробно — в разделе [[#Зачем нужен fake|Зачем нужен fake]]). Гарантий нет: это лишь возможные исходы.
Команда из быстрого старта (`fake:blob=fake_default_tls:tcp_md5`) сначала отправляет фейковый ClientHello с именем `www.w3.org`, а следом уходит оригинал с `youtube.com`; оба занимают одни и те же позиции потока. ТСПУ (система DPI на стороне провайдера) читает фейк первым, и один из возможных исходов — решение по соединению принимается по нему. Сервер фейк отбрасывает: в пакете испорчена подпись TCP MD5. Как читать схему: строки — пакеты в порядке отправки, подписанные в тех же обозначениях, что и таблицы в этой статье; по горизонтали — место данных в TCP-потоке (номер последовательности, seq); в колонке справа — что сделал с пакетом сервер; внизу — буфер сервера, в который принятые куски встают по своим seq.
![[desync-anim-fake.svg|Анимация fake: фейковый ClientHello уходит перед оригиналом на те же позиции потока, сервер отбрасывает фейк]]
Минимальная команда для HTTPS — первая из [[#Быстрый старт|Быстрого старта]]. `--payload=tls_client_hello` значит «срабатывать только на TLS ClientHello», `blob=fake_default_tls` выбирает, какой фейк отправить, а `tcp_md5` портит его так, чтобы сервер отбросил:
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5
```
Для HTTP меняются только тип данных и blob: `--payload=http_req` и `fake_default_http`. `fake` работает и с UDP, а значит, с QUIC — протоколом на основе UDP, у которого тоже есть первый пакет (QUIC Initial), где DPI ищет сигнатуры, то есть заранее известные строки. У UDP нет сквозной нумерации байтов и TCP-опций (почему так, объяснено в нюансе [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|«Работает только с TCP»]] статьи про multisplit), поэтому TCP-fooling вроде `tcp_md5` для него бессмыслен. Вместо него берут IP-уровневые способы, в первую очередь `ip_ttl=1:ip6_ttl=1` ([[#6. Для UDP нет TCP-fooling|нюанс 6]]).
`fake` — «огонь по площадям»: он бросает в поток одну или несколько обманок и не трогает настоящий пакет. Поэтому его почти всегда ставят первым инстансом (одной записью `--lua-desync=…` в [[Zapret2/profile#Анатомия профиля|профиле]] — наборе правил Zapret 2 для определённого трафика), а следом — технику сегментации, например [[multisplit]]: фейк может сбить DPI, а нарезка — помешать ему найти имя в настоящем пакете ([[#8. Порядок инстансов: fake всегда первый|нюанс 8]]). Если одного фейка мало, может помочь `repeats=N`: некоторые DPI сбрасывают состояние после N-го пакета, поэтому серия фейков, например `repeats=11`, может оказаться эффективнее одного ([[#7. repeats может быть очень полезен|нюанс 7]]). Работает ли выбранная комбинация у вашего провайдера, проверяют по [[Zapret2/verify-strategy#Короткий чек-лист честной проверки|чек-листу честной проверки]].
У `fake` есть близкие соседи. [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] и [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] замешивают фейки прямо между кусками настоящего запроса; `fakedsplit` заменяет связку fake + [[multisplit]], но ограничен одной позицией разреза. [[Zapret2/desync/syndata#Зачем нужен syndata|syndata]] кладёт данные уже в SYN — первый пакет рукопожатия (handshake), с которого TCP-соединение устанавливается. А скрытый фейк внутри настоящего сегмента дают `seqovl`: в [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|multisplit]] для этого, по описанию в его статье, не приходится портить заголовки, а в [[Zapret2/desync/multidisorder#seqovl — перезапись буфера сокета через перекрытие|multidisorder]] тот же приём устроен иначе: фейк остаётся в буфере сокета сервера и затирается следующим сегментом. Все отличия сведены в таблицу в разделе [[#Сравнение с syndata и fakedsplit|Сравнение с syndata и fakedsplit]].
---
## Оглавление
- [Коротко и по-простому](#коротко-и-по-простому)
- [Справка: где функция в коде и родственники](#справка-где-функция-в-коде-и-родственники)
- [Зачем нужен fake](#зачем-нужен-fake)
- [Быстрый старт](#быстрый-старт)
- [Место в общем скелете](#место-в-общем-скелете)
- [blob — источник фейковых данных](#blob--источник-фейковых-данных)
- [Стандартные blob-ы](#стандартные-blob-ы)
- [Пользовательские blob-ы](#пользовательские-blob-ы)
- [tls_mod — модификации TLS в фейке](#tls_mod--модификации-tls-в-фейке)
- [Опции tls_mod](#опции-tls_mod)
- [Подстановка sni=%var](#подстановка-snivar)
- [Когда tls_mod применяется, а когда нет](#когда-tls_mod-применяется-а-когда-нет)
- [padencap — подробности](#padencap--подробности)
- [Полный список аргументов](#полный-список-аргументов)
- [A) Собственные аргументы fake](#a-собственные-аргументы-fake)
- [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)
- [Автосегментация по MSS](#автосегментация-по-mss)
- [Поведение при replay / reasm](#поведение-при-replay--reasm)
- [Псевдокод алгоритма](#псевдокод-алгоритма)
- [Сравнение с syndata и fakedsplit](#сравнение-с-syndata-и-fakedsplit)
- [Нюансы и подводные камни](#нюансы-и-подводные-камни)
- [Миграция с nfqws1](#миграция-с-nfqws1)
- [Практические примеры](#практические-примеры)
- [📚 См. также](#-см-также)
---
## Справка: где функция в коде и родственники
Паспорт функции для тех, кто сверяется с исходниками zapret2: в каком файле и на какой строке лежит код, какой ключ первой версии zapret она заменяет и как объявлена.
**Файл:** `lua/zapret-antidpi.lua:449`
**nfqws1 эквивалент:** `--dpi-desync=fake`
**Сигнатура:** `function fake(ctx, desync)`
`fake` — самая часто используемая функция в zapret. Она отправляет **отдельный** пакет (или группу пакетов) с фейковым payload из указанного blob. При этом функция **не выносит вердикт** и **не блокирует** отправку оригинала — оригинальный пакет уходит следом. DPI видит сначала фейк, затем настоящий трафик. Задача фейка — "отравить" состояние DPI ложными данными (неверный SNI, невалидный HTTP-запрос и т.д.).
Работает с **TCP и UDP** (в отличие от функций сегментации, которые только TCP).
> [!note] Проще говоря
> Payload — полезные данные пакета без сетевых заголовков: сам ClientHello, HTTP-запрос или первый пакет QUIC. Blob — заранее заготовленные данные, которые `fake` отправляет вместо настоящих. «Отравить состояние DPI» значит подсунуть ему ложные данные, по которым он сделает неверный вывод о соединении. Вердикт — решение «перехваченный пакет не отправлять»; у `fake` его нет, поэтому оригинал уходит следом, как описано в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]].
Родственные функции: [[syndata]] (payload в SYN), [[fakedsplit]] (фейки + сегментация), [[fakeddisorder]] (фейки + обратный порядок), [[multisplit]] (чистая сегментация), [[multidisorder]] (сегментация в обратном порядке).
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `fake` от этого скелета отличается.
---
## Зачем нужен fake
[[DPI/DPI|DPI]] анализирует первые пакеты TCP/UDP-потока, ища сигнатуры (SNI в TLS ClientHello, Host в HTTP, QUIC Initial). Сигнатура здесь — заранее известная строка, по которой DPI узнаёт сайт. Если **перед** настоящим пакетом отправить фейковый пакет с ложными данными, DPI может:
1. **Принять фейк за реальный трафик:** DPI обработает фейковый SNI/Host и примет решение на его основе — пропустит соединение
2. **Сбиться с состояния:** получив невалидные данные, DPI потеряет контекст потока и перестанет его анализировать
3. **Не суметь заблокировать:** если DPI блокирует по hostname, а в фейке другой hostname — реальный запрос может пройти
При этом **сервер должен отбросить фейк**. Для этого к фейку применяется [[Zapret2/ts-and-fooling#Что такое fooling|fooling]] — порча заголовков (TTL, badseq, md5sig, badsum), из-за которой сервер или промежуточные маршрутизаторы отбрасывают пакет, а DPI — нет.
**fake** — "огонь по площадям": он шлёт фейк отдельным пакетом, не трогая оригинал. Для более тонкой работы (замешивание фейков внутри сегментов) используйте [[fakedsplit]]/[[fakeddisorder]], для скрытых фейков через TCP window — [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|seqovl]] в [[multisplit]]/[[multidisorder]].
> [!note] Проще говоря
> `fake` заранее показывает DPI ложную картину: другое имя сайта или бессмысленные данные. Сервер эту картину не видит, потому что фейк специально испорчен, и получает только настоящий запрос. TCP window (окно приёма) — диапазон номеров байт, которые получатель готов принять; скрытые фейки, которые строятся на приёме и отбрасывании байт, `fake` не делает, это работа `seqovl` (в multisplit фейк оказывается левее окна приёма и сервер его выбрасывает, в multidisorder — иначе, как сказано в начале статьи). Как испортить фейк правильно, зависит от того, где стоит DPI и что стоит между вами и сервером: разбор — в заметке [[ts-and-fooling]].
---
## Быстрый старт
Ниже четыре готовые строки для [[Zapret2/profile#Анатомия профиля|профиля]] Zapret 2, от минимальных вариантов к боевой связке. Флаг `--payload=…` работает как фильтр для всех вызовов `--lua-desync`, которые идут после него (до следующего `--payload` или до конца профиля): они срабатывают только на данных указанного типа. Поэтому `--payload` пишут перед `--lua-desync` (подробно — в [[последовательность аргументов]]; типы данных описаны в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|о типах payload]]).
Минимальный TLS-фейк (обязателен `blob` + fooling):
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5
```
Сервер отбросит такой фейк из-за TCP MD5 option. `fake_default_tls` — один из стандартных blob-ов (что в нём, разобрано в разделе [[#Стандартные blob-ы|Стандартные blob-ы]]).
Минимальный HTTP-фейк:
```bash
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5
```
То же для HTTP: другой тип данных и другой стандартный blob.
QUIC-фейк (UDP):
```bash
--payload=quic_initial --lua-desync=fake:blob=fake_default_quic:ip_ttl=1:ip6_ttl=1
```
Для UDP TCP-fooling неприменим, поэтому фейк ограничен по TTL (подробно — в нюансе [[#6. Для UDP нет TCP-fooling|«Для UDP нет TCP-fooling»]]).
Типовая боевая связка fake + [[multisplit]]:
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid,padencap \
--lua-desync=multisplit:pos=1,midsld
```
Фейк уходит первым, а следом [[multisplit]] режет настоящий ClientHello после первого байта и посередине домена второго уровня (маркеры `pos` разобраны в разделе [[Zapret2/desync/multisplit#Маркеры позиций (pos)|Маркеры позиций (pos)]]). Набор `tls_mod=…` меняет содержимое самого фейка, см. раздел [[#tls_mod — модификации TLS в фейке|tls_mod]].
---
## Место в общем скелете
Тело `fake` построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в [[жизненный цикл desync-функции]] — здесь только отклонения, и их у `fake` больше, чем у прочих техник. Несколько слов из таблицы. Cutoff (отсечение) значит, что движок больше не будет вызывать функцию на пакетах этого потока или направления. Гвард — проверка «работать ли с этим пакетом». Replay (перепроигрывание) — повторный вызов функций на придержанных частях многопакетного запроса. Вердикт — решение движка, что сделать с перехваченным пакетом.
| Стадия общего скелета | Что делает `fake` |
|:----------------------|:------------------|
| 1. Отсев транспорта | **исключение из правила**: работает и с TCP, и с UDP, поэтому `instance_cutoff` не делает вовсе — просто условие `if (desync.dis.tcp or desync.dis.udp)` |
| 2. Направление | `dir=out` по умолчанию, отключается от входящего |
| 3. Аргументы | `blob` **обязателен** — без него `error()`; с флагом `optional` отсутствующий blob даёт тихий выход |
| 4. Данные | **не использует цепочку** `blob → reasm → payload`: всегда шлёт содержимое своего `blob`, а `reasm_data` нужен лишь как образец для `tls_mod` |
| 5. Гварды | штатные `direction_check` и `payload_check` (по умолчанию `known`) |
| 6. replay | штатный `replay_first`: фейк уходит один раз, на первой части многопакетного запроса |
| 7. **Своя техника** | загрузить blob, при необходимости применить `tls_mod`, отправить `rawsend_payload_segmented` |
| 8. Вердикт | **не выносит никакого** — оригинал уходит следом за фейком как ни в чём не бывало |
Две последние строки таблицы и объясняют, почему `fake` комбинируется с чем угодно: он ничего не дропает и ничего не модифицирует, а просто добавляет в поток лишний пакет перед настоящим. Поэтому в профиле его почти всегда ставят первым инстансом, а следом — технику сегментации вроде [[multisplit]].
> [!note] Проще говоря
> Почти каждая функция дурения проходит одни и те же восемь шагов, а `fake` от этого шаблона отличается тем, что берёт данные из своего `blob`, не выносит вердикт и умеет работать и с TCP, и с UDP.
Каждую стадию по отдельности разбирает жизненный цикл: [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|стадия 1]] (транспорт), [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадия 2]] (направление), [[Zapret2/жизненный цикл desync-функции#Стадия 3. Ранняя проверка аргументов|стадия 3]] (аргументы), [[Zapret2/жизненный цикл desync-функции#Стадия 4. Выбор данных: blob → reasm → payload|стадия 4]] (данные), [[Zapret2/жизненный цикл desync-функции#Стадия 5. Три гварда|стадия 5]] (гварды), [[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)|стадия 6]] (replay), [[Zapret2/жизненный цикл desync-функции#Стадия 7. Собственно техника|стадия 7]] (своя техника) и [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадия 8]] (вердикт).
---
## blob — источник фейковых данных
`blob` — обязательный аргумент `fake`. Он указывает, **что** именно отправить в качестве фейкового payload. Blob — заранее заготовленный набор байт с именем; как их объявлять и что в них можно положить, разобрано в заметке [[blob]], а здесь описано только то, что нужно для `fake`.
### Стандартные blob-ы
Zapret автоматически создаёт три стандартных blob-а при инициализации. Таблица показывает, какой из них к какому типу данных подходит; тип данных задаёт `--payload=…` в [[Zapret2/profile#Анатомия профиля|профиле]]:
| Blob | Описание | Типичное использование |
|:-----|:---------|:-----------------------|
| `fake_default_tls` | Валидный TLS ClientHello с SNI `www.w3.org` | `--payload=tls_client_hello` |
| `fake_default_http` | Валидный HTTP GET запрос | `--payload=http_req` |
| `fake_default_quic` | QUIC Initial пакет | `--payload=quic_initial` |
Эти blob-ы содержат полноценные протокольные структуры, которые DPI может распознать и обработать. Именно поэтому `fake_default_tls` — самый частый выбор для TLS-фейков: DPI парсит его как настоящий ClientHello, видит `www.w3.org` вместо заблокированного домена.
> [!note] Проще говоря
> Стандартный blob — это готовый «пример запроса» с безобидным содержимым. `fake` отправляет его как есть, а DPI разбирает его так же, как настоящий.
### Пользовательские blob-ы
Свой blob можно задать несколько способами: прямо в команде шестнадцатеричной строкой (inline hex), загрузить из файла, взять из Lua-переменной, в том числе созданной другой функцией. Примеры:
```bash
# Inline hex (произвольные байты)
--lua-desync=fake:blob=0xDEADBEEF:tcp_md5
# Нулевые байты (4 байта нулей)
--lua-desync=fake:blob=0x00000000:ip_ttl=6:ip6_ttl=6
# Из файла (предзагруженный)
--blob=my_fake_ch:@/path/to/custom_clienthello.bin
--lua-desync=fake:blob=my_fake_ch:tcp_md5
# Из Lua-переменной (например, клонированный ClientHello)
--lua-desync=tls_client_hello_clone:blob=cloned_ch:sni_del:sni_add=www.google.com
--lua-desync=fake:blob=cloned_ch:tcp_md5
```
Blob разрешается функцией `blob_exist()` / `blob()` в следующем порядке:
1. Если имя начинается с `0x` — inline hex, создаётся на лету
2. `desync[name]` — поле в текущем контексте desync (например, сгенерированное `tls_client_hello_clone`)
3. `_G[name]` — глобальная Lua-переменная (например, `fake_default_tls`)
> [!note] Проще говоря
> Сначала смотрится, не hex ли записан прямо в аргументе, потом — не создал ли имя предыдущий инстанс в текущем контексте `desync` (устройство таблицы — в [[структура desync и диссекта]]), и в последнюю очередь — среди глобальных переменных Lua. Если имя не нашлось нигде, `fake` завершится ошибкой, а с флагом `optional` тихо пропустит шаг ([[#5. blob обязателен — без optional будет Lua exception|нюанс 5]]).
---
## tls_mod — модификации TLS в фейке
`tls_mod` позволяет модифицировать содержимое blob-а перед отправкой, подстраивая фейковый ClientHello под текущее соединение. Это делает фейк более убедительным для DPI. Среди полей ClientHello есть случайные 32 байта (`Random`), идентификатор сессии (`Session ID`) и набор расширений (extensions), среди которых SNI с именем сайта и padding — заполнитель. Опции ниже меняют эти поля blob-а перед отправкой; остальные модификации TLS-блобов описаны в заметке [[blob]].
![[desync-anim-hub-tlsmod.svg|Анимация: что меняют опции tls_mod rnd, rndsni, sni=, dupsid и padencap в фейковом ClientHello]]
### Опции tls_mod
| Опция | Описание | Требования |
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------ |
| `none` | Ничего не делать | — |
| `rnd` | Заполнить поля `Random` (32 байта) и `Session ID` случайными данными | blob должен содержать валидный TLS ClientHello |
| `rndsni` | Заменить SNI на случайный домен. Если длина оригинального SNI >= 7 символов — случайный поддомен из известных 3-буквенных TLD. Иначе — случайные символы `[a-z][a-z0-9]*` | blob должен содержать SNI extension |
| `sni=<domain>` | Заменить SNI на конкретный домен (изменяет длины внутри TLS-структур) | blob должен содержать SNI extension |
| `dupsid` | Скопировать Session ID из **оригинального** payload (из `desync.reasm_data`) в фейк. Выполняется после `rnd`. Требует совпадения длин session id | валидный TLS в `reasm_data` |
| `padencap` | Подкорректировать blob так, чтобы оригинальный payload стал частью padding extension. Увеличивает поля длины TLS record/handshake/extensions/padding на `len(original_payload)` | blob должен содержать padding extension (type 21) |
**Порядок применения модов:** они применяются в порядке перечисления, но `dupsid` всегда выполняется после `rnd` (чтобы рандомизированный Session ID был перезаписан реальным).
> [!note] Проще говоря
> `rnd` делает случайными служебные поля фейка, `rndsni` и `sni=` подменяют имя сайта в нём, `dupsid` копирует идентификатор сессии из настоящего запроса, а `padencap` готовит фейк так, чтобы настоящий запрос выглядел его продолжением (подробности — [[#padencap — подробности|ниже]]). Образцом для `dupsid` и `padencap` служит настоящий запрос, собранный движком, — `desync.reasm_data`; что такое reasm, объяснено в разделе [[Zapret2/структура desync и диссекта#Приём многопакетных пейлоадов (reasm / decrypt / replay)|Приём многопакетных пейлоадов]].
### Подстановка sni=%var
Внутри `tls_mod` поддерживается специальная запись `sni=%variable`: вместо готового домена в SNI подставляется значение переменной. Значение `desync.target` устанавливается механизмом hostlist (списки доменов профиля, см. [[Zapret2/filter#Списки IP и доменов|Списки IP и доменов]]).
```bash
# Подстановка из desync.target (устанавливается механизмом hostlist)
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=sni=%target
# Подстановка из глобальной Lua-переменной
--lua-init="my_domain='www.google.com'" \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=sni=%my_domain
```
Порядок поиска переменной:
1. `desync[var]` — поле в текущем контексте desync
2. `_G[var]` — глобальная Lua-переменная
Если переменная не найдена — вызывается `error("tls_mod_shim: non-existent var 'varname'")`.
### Когда tls_mod применяется, а когда нет
В `fake` tls_mod вызывается **только если одновременно выполнены оба условия:**
1. `desync.reasm_data` существует (обычно есть при TLS ClientHello, который был реассемблирован)
2. Аргумент `tls_mod` задан
Reasm (reasm_data) — запрос, собранный движком из нескольких пакетов; для `tls_mod` он служит образцом настоящего ClientHello.
Если `reasm_data` отсутствует (например, маленький пакет, не потребовавший реассемблирования, или UDP) — `tls_mod` **молча пропускается**, без ошибок. Это отличие от [[syndata]], где `tls_mod` вызывается всегда (с `payload=nil`), потому что syndata работает на SYN-пакете, где никакого реального payload ещё нет.
> [!note] Проще говоря
> Нет образца настоящего запроса — модификации молча не применяются, и фейк уходит таким, каким лежит в blob-е.
**Обходной путь**, если tls_mod нужен без reasm_data:
```bash
# Заранее модифицировать blob через lua-init
--lua-init="fake_default_tls=tls_mod(fake_default_tls,'rnd,rndsni')"
```
### padencap — подробности
`padencap` — техника, при которой фейковый blob подготавливается так, чтобы **реальный payload**, отправленный следом, воспринимался DPI как продолжение padding extension в фейковом ClientHello. DPI, парсящий TLS, может объединить фейк и оригинал в один record, и реальный SNI окажется "внутри" padding — невидим для анализа.
Для этого в blob должна присутствовать padding extension (type 0x0015), а `padencap` увеличивает все поля длины (TLS record length, Handshake length, Extensions length, Padding extension length) на `len(original_payload)`.
> [!note] Проще говоря
> Фейк заранее объявляет: «моё поле-заполнитель длиннее, чем кажется». Настоящий ClientHello, который придёт следом, DPI может принять за продолжение этого заполнителя, и тогда настоящий SNI останется незамеченным. Ограничения этого трюка — в нюансе [[#10. padencap + реальный payload — тонкий трюк|10]].
---
## Полный список аргументов
Аргументы делятся на собственные аргументы `fake` (раздел A) и стандартные (разделы B–H): их понимают и другие функции дурения, а общий обзор есть в [[desync]]. Формат вызова:
```
--lua-desync=fake[:arg1[=val1][:arg2[=val2]]...]
```
Все `val` приходят в Lua как строки. Если `=val` не указан, значение = пустая строка `""` (в Lua это truthy), поэтому флаги пишутся просто как `:optional`, `:tcp_md5`, `:badsum`.
### A) Собственные аргументы fake
#### `blob` (обязательный)
- **Формат:** `blob=<blobName>`
- **Тип:** имя blob-переменной (загружается через `--blob=<name>:@file|0xHEX`, через `desync[name]` или как глобальная Lua-переменная)
- **По умолчанию:** нет (обязательный аргумент)
- **Описание:** Blob, содержащий фейковый payload. Может быть любой длины — для TCP сегментация по MSS выполняется автоматически. Для UDP отправляется как есть
- **Ошибка:** если `blob` не указан, вызывается `error("fake: 'blob' arg required")` — Lua exception, останавливающий обработку пакета
- **Примеры:**
- `blob=fake_default_tls` — стандартный TLS-фейк
- `blob=fake_default_http` — стандартный HTTP-фейк
- `blob=fake_default_quic` — стандартный QUIC-фейк
- `blob=0xDEADBEEF` — inline hex
- `blob=0x00000000` — четыре нулевых байта
- `blob=my_custom_blob` — предзагруженный или сгенерированный blob
Проще говоря: `blob` — единственный аргумент, без которого `fake` не работает. Как он ищется, описано в разделе [[#blob — источник фейковых данных|blob — источник фейковых данных]].
#### `optional`
- **Формат:** `optional` (флаг, без значения)
- **Описание:** Если blob отсутствует (не найден ни как inline hex, ни как `desync[name]`, ни как `_G[name]`) — тихий пропуск без ошибки. Без `optional` отсутствие blob-а вызывает `error()`
- **Использование:** защита от ошибок при использовании blob-ов, которые могут отсутствовать (например, генерируемых `tls_client_hello_clone` — если payload не TLS, clone не создаст blob)
Проще говоря: без `optional` отсутствие blob-а останавливает обработку пакета ошибкой, с `optional` `fake` просто ничего не делает ([[#5. blob обязателен — без optional будет Lua exception|нюанс 5]]).
#### `tls_mod`
- **Формат:** `tls_mod=<commaSeparatedList>`
- **Тип:** строка вида `opt1,opt2,...`
- **По умолчанию:** не задан (модификации не применяются)
- **Описание:** Список TLS-модификаций, применяемых к blob перед отправкой. Работает только при наличии `desync.reasm_data`
- **Опции:** `none`, `rnd`, `rndsni`, `sni=<domain>`, `dupsid`, `padencap`
- **Подстановка:** `sni=%variable` — подстановка из `desync[var]` или `_G[var]`
- **Примеры:**
- `tls_mod=rnd` — рандомизировать Random и Session ID
- `tls_mod=rnd,rndsni,dupsid` — рандомизировать + случайный SNI + скопировать реальный Session ID
- `tls_mod=rnd,rndsni,dupsid,padencap` — полный набор модификаций
- `tls_mod=sni=www.google.com` — конкретный домен в SNI
- `tls_mod=sni=%target` — подстановка из переменной target
Каждая опция разобрана в разделе [[#tls_mod — модификации TLS в фейке|tls_mod — модификации TLS в фейке]].
---
### B) Standard direction
Направление говорит, в какую сторону идёт пакет: от вашего компьютера к серверу (исходящий) или обратно (входящий).
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |
Фильтр по направлению пакета. `fake` по умолчанию работает только с исходящими (`out`).
- `dir=out` — только исходящие (от клиента к серверу)
- `dir=in` — только входящие (от сервера к клиенту)
- `dir=any` — оба направления
При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` — отсекает себя от противоположного направления. Cutoff (отсечение) значит, что движок больше не будет вызывать функцию на пакетах этого направления в текущем соединении; подробно — в [[Zapret2/жизненный цикл desync-функции#Стадия 2. Отсечение противоположного направления|стадии 2 жизненного цикла]].
---
### C) Standard payload
Здесь фильтр — по [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|типу payload]], то есть по названию распознанного протокола (`tls_client_hello`, `http_req`, `quic_initial`).
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `payload` | список типов через запятую | `known` |
Фильтр по типу payload на уровне Lua. Это **дополнительный** фильтр к `--payload=...` на уровне профиля.
- `payload=known` — только распознанные протоколы (`http_req`, `tls_client_hello`, `quic_initial` и т.д.)
- `payload=all` — любой payload, включая `unknown`
- `payload=tls_client_hello,http_req` — конкретные типы
- `payload=~unknown` — инверсия: всё кроме unknown
**Важно:** лучше ставить `--payload=...` на уровне профиля (C-код, быстрее), а не полагаться только на Lua-фильтр.
Проще говоря: фильтр `--payload=` на уровне профиля работает в быстром ядре программы, написанном на C, а аргумент `payload=` проверяется уже в Lua, то есть медленнее. Поэтому основной отбор лучше делать на уровне профиля.
---
### D) Standard fooling
Fooling — намеренная порча заголовков пакета, обычно для того, чтобы сервер отбросил фейк, а DPI его прочитал (подробно — [[Zapret2/ts-and-fooling#Что такое fooling|Что такое fooling]]). Здесь это модификации L3/L4 заголовков, то есть заголовков IP (сетевой уровень) и TCP (транспортный уровень). В `fake` fooling применяется **только к фейковым пакетам** (а не к оригиналу — тот уходит без изменений). Это ключевое отличие от [[multisplit]], где fooling идёт на все сегменты.
| Параметр | Описание | Пример |
|:---------|:---------|:-------|
| `ip_ttl=N` | Установить IPv4 TTL | `ip_ttl=6` |
| `ip6_ttl=N` | Установить IPv6 Hop Limit | `ip6_ttl=6` |
| `ip_autottl=delta,min-max` | Автоматический TTL (delta от серверного TTL) | `ip_autottl=-2,40-64` |
| `ip6_autottl=delta,min-max` | Аналогично для IPv6 | `ip6_autottl=-2,40-64` |
| `ip6_hopbyhop[=HEX]` | Вставить extension header hop-by-hop (по умолчанию 6 нулей) | `ip6_hopbyhop` |
| `ip6_hopbyhop2[=HEX]` | Второй hop-by-hop header | `ip6_hopbyhop2` |
| `ip6_destopt[=HEX]` | Destination options header | `ip6_destopt` |
| `ip6_destopt2[=HEX]` | Второй destination options | `ip6_destopt2` |
| `ip6_routing[=HEX]` | Routing header | `ip6_routing` |
| `ip6_ah[=HEX]` | Authentication header | `ip6_ah` |
| `tcp_seq=N` | Сместить TCP sequence (+ или -) | `tcp_seq=-10000` |
| `tcp_ack=N` | Сместить TCP ack (+ или -) | `tcp_ack=-66000` |
| `tcp_ts=N` | Сместить TCP timestamp | `tcp_ts=-100` |
| `tcp_md5[=HEX]` | Добавить TCP MD5 option (16 байт; по умолчанию случайные) | `tcp_md5` |
| `tcp_flags_set=LIST` | Установить TCP-флаги | `tcp_flags_set=FIN,PUSH` |
| `tcp_flags_unset=LIST` | Снять TCP-флаги | `tcp_flags_unset=ACK` |
| `tcp_ts_up` | Поднять TCP timestamp option в начало заголовка | `tcp_ts_up` |
| `tcp_nop_del` | Удалить все TCP NOP опции | `tcp_nop_del` |
| `fool=<func>` | Кастомная Lua-функция fooling | `fool=my_fooler` |
**Типичные комбинации fooling для fake:**
- `tcp_md5` — самый популярный. Серверы (Linux) отбрасывают пакеты с TCP MD5 option, если MD5-аутентификация не настроена
- `ip_ttl=1:ip6_ttl=1` — TTL=1: фейк не дойдёт до сервера, но DPI на пути его увидит
- `badsum` — испорченная контрольная сумма: сервер отбросит, DPI может пропустить
- `tcp_flags_unset=ACK` — datanoack: убрать ACK-флаг, сервер отбросит пакет без ACK в установленном соединении
- `tcp_seq=-10000` — badseq: невалидный sequence number, сервер отбросит
> [!note] Проще говоря
> Все опции таблицы — способы сделать фейк «неправильным» в одном из мест: время жизни пакета (`ip_ttl`), номера seq и ack, TCP-опции (`tcp_md5`, `tcp_ts`), флаги, контрольная сумма (`badsum`, раздел G). Какая опция сработает, зависит от среды: почему у каждой свой «слой смерти» и где она ломается, описано в [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|каталоге способов]].
---
### 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-сегментации для TCP, см. [[#Автосегментация по MSS|Автосегментация по MSS]]).
---
### F) Standard ipfrag
IP-фрагментация фейковых пакетов. Каждый пакет дополнительно фрагментируется на уровне IP.
Проще говоря: пакет-фейк при отправке ещё раз делится на части, но уже средствами протокола IP, а собирает их обратно получатель на уровне IP.
| Параметр | Описание | По умолчанию |
|:---------|:---------|:-------------|
| `ipfrag[=func]` | Включить IP-фрагментацию. Если без значения — `ipfrag2` | — |
| `ipfrag_disorder` | Отправить IP-фрагменты в обратном порядке | — |
| `ipfrag_pos_tcp=N` | Позиция фрагментации TCP (кратно 8) | `32` |
| `ipfrag_pos_udp=N` | Позиция фрагментации UDP (кратно 8). Актуально для QUIC-фейков | `8` |
| `ipfrag_next=N` | IPv6: next protocol во 2-м фрагменте (penetration атака на фаерволы) | — |
---
### G) Standard reconstruct
Реконструкция — сборка готового raw-пакета («сырого», со всеми заголовками) перед отправкой.
| Параметр | Описание |
|:---------|:---------|
| `badsum` | Испортить L4 (TCP/UDP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет |
---
### H) Standard rawsend
Rawsend — отправка готовых пакетов в сеть самой функцией; в `fake` её выполняет `rawsend_payload_segmented` (см. [[#Автосегментация по MSS|Автосегментация по MSS]]). Эти параметры управляют отправкой.
| Параметр | Описание |
|:---------|:---------|
| `repeats=N` | Отправить каждый пакет/сегмент N раз (идентичные повторы) |
| `ifout=<iface>` | Интерфейс для отправки (по умолчанию определяется автоматически) |
| `fwmark=N` | Firewall mark (только Linux, nftables/iptables) |
---
## Автосегментация по MSS
![[desync-anim-hub-mss.svg|Анимация: кусок данных больше MSS автоматически режется на под-сегменты с растущим seq]]
MSS (Maximum Segment Size) — наибольший объём данных, который помещается в один TCP-сегмент данного соединения. Для TCP о размерах пакетов думать **не нужно**. Функция `rawsend_payload_segmented` из `zapret-lib.lua` автоматически:
1. Отслеживает MSS для каждого TCP-соединения
2. Если blob превышает MSS — автоматически режет по MSS
3. Каждый под-сегмент отправляется с корректным TCP sequence
**Пример:** blob из 10000 байт при MSS=1460 будет отправлен как 7 TCP-сегментов (6 x 1460 + 1 x 240).
> [!note] Проще говоря
> Даже длинный фейк можно не готовить заранее: функция сама нарежет его на пакеты допустимого размера и проставит каждому правильный seq.
Для UDP сегментации нет — blob отправляется как один UDP-пакет. Если blob больше MTU, пакет будет фрагментирован на уровне IP (или отброшен, если DF-бит установлен). MTU — наибольший размер пакета, который проходит по каналу без разбиения на части, а DF-бит (Don't Fragment) — флаг «не фрагментировать».
---
## Поведение при replay / reasm
Многопакетный запрос (тот, что не влез в один пакет) `nfqws2` придерживает, собирает в буфер `reasm_data` (reasm — повторная сборка) и перепроигрывает через desync-функции: replay (перепроигрывание) означает, что движок по очереди вызывает функции на каждой придержанной части. Механика описана в [[Zapret2/жизненный цикл desync-функции#Стадия 6. Развилка перепроигрывания (replay)|жизненный цикл desync-функции, стадия 6]]. `fake` использует из неё только половину: срабатывает на первой части (`replay_first`) и молчит на остальных, логируя «not acting on further replay pieces».
Вторую половину развилки — дроп последующих частей — `fake` не применяет, потому что ему нечего дропать: он не отправляет реальные данные, а лишь добавляет фейк. Поэтому все части replay проходят дальше по цепочке инстансов, и стоящая следом [[multisplit]] или [[multidisorder]] нарежет собранный реасм как обычно.
> [!note] Проще говоря
> Фейку хватает одного срабатывания на первой части запроса, а остальные части идут дальше нетронутыми: их обработает следующая функция в цепочке, например [[Zapret2/desync/multisplit#Поведение при replay / reasm|multisplit, который на первой части режет и отправляет весь собранный буфер]].
---
## Псевдокод алгоритма
Ниже — запись тела `fake` на Lua, чтобы видно было, в каком порядке функция проверяет условия и что отправляет. Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл desync-функции]]:
```lua
function fake(ctx, desync)
-- 1. Cutoff противоположного направления
direction_cutoff_opposite(ctx, desync)
-- 2. Проверка: только TCP или UDP
if not (desync.dis.tcp or desync.dis.udp) then return end
-- 3. Проверки: направление OK, payload OK
if not direction_check(desync) then return end
if not payload_check(desync) then return end
-- 4. Только первый replay
if replay_first(desync) then
-- 5. blob обязателен
if not desync.arg.blob then
error("fake: 'blob' arg required")
end
-- 6. optional: тихий пропуск если blob нет
if optional and not blob_exist(desync, desync.arg.blob) then
DLOG("fake: blob not found. skipped")
return -- НЕ error, просто return
end
-- 7. Загрузка blob
fake_payload = blob(desync, desync.arg.blob)
-- 8. tls_mod (только если есть reasm_data)
if desync.reasm_data and desync.arg.tls_mod then
fake_payload = tls_mod_shim(desync, fake_payload,
desync.arg.tls_mod,
desync.reasm_data)
end
-- 9. Отправка (с fooling, ipid, reconstruct, ipfrag, rawsend)
rawsend_payload_segmented(desync, fake_payload)
-- НЕ возвращает вердикт!
else
DLOG("fake: not acting on further replay pieces")
end
-- 10. Нет return VERDICT_DROP — оригинал уходит
end
```
**Ключевое отличие от [[multisplit]]**: multisplit возвращает `VERDICT_DROP` после успешной отправки (оригинал блокируется), а fake **ничего не возвращает** (оригинал проходит). Псевдокод самого `multisplit` — в разделе [[Zapret2/desync/multisplit#Псевдокод алгоритма|Псевдокод алгоритма]] его статьи.
> [!note] Проще говоря
> Функция проверяет транспорт, направление и тип данных, на первой части запроса берёт blob, при необходимости применяет `tls_mod`, отправляет результат и выходит, ничего не решая насчёт оригинала.
---
## Сравнение с syndata и fakedsplit
Таблица ниже сопоставляет `fake` с двумя ближайшими соседями. [[Zapret2/desync/syndata#Отличия от fake|syndata]] работает на SYN-пакете (это самый первый пакет соединения, с него начинается рукопожатие TCP), а [[Zapret2/desync/fakedsplit#Зачем нужен fakedsplit|fakedsplit]] сам режет payload и вставляет фейки между частями.
| Аспект | `fake` | `syndata` | `fakedsplit` |
|:-------|:-------|:----------|:-------------|
| Когда работает | На payload (после TCP handshake) | На SYN-пакете (до handshake) | На payload (после handshake) |
| Протоколы | TCP **и** UDP | Только TCP | Только TCP |
| Что делает | Шлёт отдельный фейковый пакет | Вкладывает payload в SYN | Режет payload + вставляет фейки между частями |
| Вердикт | **Нет** (оригинал проходит) | `VERDICT_DROP` (заменяет SYN) | `VERDICT_DROP` (заменяет оригинал) |
| blob обязателен | **Да** (error если нет) | Нет (дефолт: 16 нулевых байт) | Нет (фейки из `pattern`) |
| tls_mod условие | Нужен `reasm_data` | Всегда (payload=nil) | Нет tls_mod |
| fooling к чему | К фейковым пакетам | К SYN-пакету | Только к фейковым сегментам |
| Автосегментация | Да (TCP по MSS) | **Нет** (должен влезть в 1 пакет) | Да |
| Типичная позиция в цепочке | **Первый** (перед split) | **Самый первый** (на SYN) | Единственный (заменяет split) |
**Когда что использовать:**
- **fake** — когда нужен простой фейк перед реальным трафиком. Самый универсальный вариант, работает с TCP и UDP
- **syndata** — когда DPI анализирует уже SYN-пакет. Очень ранняя стадия, но ограничен размером одного пакета
- **fakedsplit** — когда нужно одновременно и нарезать, и подмешать фейки. Заменяет связку fake + [[multisplit]], но ограничен одной позицией разреза
Сравнение с сегментирующими функциями — в статьях [[Zapret2/desync/fakeddisorder#Зачем нужен fakeddisorder|fakeddisorder]] (фейки + обратный порядок) и [[multisplit]] (чистая нарезка без фейков).
---
## Нюансы и подводные камни
### 1. fake не делает DROP
Оригинальный пакет уйдёт следом (если другие инстансы не дропнут его). Это **ожидаемое поведение** — fake лишь добавляет фейковые пакеты перед оригиналом. DROP — вердикт «перехваченный пакет не отправлять»; как движок складывает вердикты разных инстансов, описано в [[Zapret2/жизненный цикл desync-функции#Стадия 8. Вердикт и его агрегация|стадии 8 жизненного цикла]].
### 2. Без fooling фейк бесполезен или вреден
Если не задать fooling, сервер примет фейковый payload как настоящий. Для TCP это приведёт к рассинхронизации потока (данные с неверным seq будут интерпретированы неправильно). Для UDP сервер просто обработает фейковый пакет. **Всегда используйте fooling** (`tcp_md5`, `badsum`, `ip_ttl=1`, `tcp_flags_unset=ACK`, `tcp_seq=-10000` и т.д.). Рассинхронизация здесь означает, что данные с неверным seq сервер интерпретирует неправильно. Пример ниже показывает, как выглядит такая ошибка в конфиге; каталог способов испортить фейк — в [[Zapret2/ts-and-fooling#Как заставить fake умереть: каталог способов|заметке про fooling]].
> [!danger] Так делать НЕЛЬЗЯ — fake на TCP без ограничителей
> ```
> --lua-desync=fake:blob=fake_tls_vk:repeats=2:strategy=4
> ```
> Здесь нет ни одного fooling-параметра (`tcp_md5`, `ip_ttl=`, `badsum`, `tcp_flags_unset=ack`, `tcp_seq=`). Фейковый пакет уходит с **валидными TCP seq, checksum и нормальным TTL** → сервер принимает его как легитимные данные потока → приходит настоящий ClientHello с пересекающимся seq → **рассинхронизация → гарантированный разрыв соединения**.
>
> Цитата bolvan (Zapret anti-DPI staff) по этому конфигу:
> > fake без ограничителей на tcp = гарантированный слом соединения. https скорее всего работает, потому что через warp, но без работать не будет.
>
> То есть если кажется, что «работает» — это заслуга туннеля (WARP заворачивает реальный трафик в WireGuard/UDP, и битый fake-на-TCP по нему не бьёт), а **не** конфига. Убери WARP — голый HTTPS пойдёт по обычному TCP, и битый fake его сломает.
>
> **Примечание:** параметра `strategy=` у `fake` нет (см. [полный список аргументов](#a-собственные-аргументы-fake)) — он игнорируется и fooling не добавляет.
>
> **Как чинить** — добавить любой ограничитель:
> ```
> --lua-desync=fake:blob=fake_tls_vk:tcp_md5:repeats=2
> ```
> Выбор fooling зависит от того, где относительно тебя стоит DPI: `ip_ttl=` работает, если DPI ближе клиента, чем сервер; `tcp_md5`/`badsum`/`tcp_seq=` — сервер отбросит фейк сам.
### 3. fake работает только на первом replay-куске
При многопакетных payload (реассемблированных) fake срабатывает ровно один раз — на первом куске. На последующих частях он ничего не делает. Это правильно: фейк нужен один раз, перед началом реальных данных. Что такое replay и почему `fake` срабатывает лишь на первой части, описано в разделе [[#Поведение при replay / reasm|Поведение при replay / reasm]].
### 4. tls_mod молча пропускается без reasm_data
Если вы задали `tls_mod=rnd,rndsni`, но `reasm_data` отсутствует (например, payload влез в один пакет и реассемблирование не потребовалось для некоторых конфигураций) — tls_mod просто не применится. Фейк будет отправлен с немодифицированным blob. Для гарантированных модификаций используйте `--lua-init` для подготовки blob заранее (пример — в разделе [[#Когда tls_mod применяется, а когда нет|Когда tls_mod применяется, а когда нет]]).
### 5. blob обязателен — без optional будет Lua exception
Без `blob=...` функция вызывает `error()`. Это не тихий пропуск, а exception, который прервёт обработку всех инстансов для данного пакета. Если blob может отсутствовать — **всегда указывайте `optional`**. Lua exception — аварийная остановка выполнения Lua-кода с сообщением об ошибке; здесь её вызывает функция `error()`.
### 6. Для UDP нет TCP-fooling
UDP не нумерует байты и не имеет TCP-заголовка с опциями; почему TCP-приёмы к нему неприменимы, разобрано в нюансе [[Zapret2/desync/multisplit#1. Работает только с TCP — и почему|«Работает только с TCP»]] статьи про multisplit.
При использовании с UDP (QUIC) параметры `tcp_md5`, `tcp_seq`, `tcp_ack`, `tcp_flags_unset` и другие TCP-специфичные fooling не имеют смысла. Для UDP используйте IP-уровневые fooling: `ip_ttl`, `ip6_ttl`, `badsum`, `ipfrag`, IPv6 extension headers.
### 7. repeats может быть очень полезен
`repeats=N` отправляет каждый пакет/сегмент N раз. Для fake это означает N фейков подряд. Некоторые DPI сбрасывают состояние после N-го пакета, поэтому "заваливание" фейками (`repeats=11` или `repeats=20`) может быть эффективнее одного.
### 8. Порядок инстансов: fake всегда первый
В типичной конфигурации `fake` стоит **перед** [[multisplit]]/[[multidisorder]]. Порядок важен: сначала уходит фейк, затем (следующий инстанс) нарезает и отправляет реальный payload. Если поставить fake после split — фейк уйдёт после реальных данных, что может быть неэффективно. Инстанс — одна запись `--lua-desync=…` в профиле; как инстансы выстраиваются в цепочку, описано в [[последовательность аргументов]], а общий разбор порядка — в нюансе [[Zapret2/desync/multisplit#7. Порядок инстансов важен|«Порядок инстансов важен»]] статьи про multisplit.
### 9. fake не отсекается на не-TCP/не-UDP
В отличие от [[multisplit]] (который делает `instance_cutoff` на не-TCP), fake просто ничего не делает для пакетов, не являющихся TCP или UDP. Он **не отсекает** себя — будет продолжать проверять следующие пакеты в потоке. Что такое `instance_cutoff` и почему он экономит ресурсы, описано в [[Zapret2/жизненный цикл desync-функции#Стадия 1. Отсев чужого транспорта и instance cutoff|стадии 1 жизненного цикла]].
### 10. padencap + реальный payload — тонкий трюк
`padencap` увеличивает длины в TLS-структуре фейка на размер реального payload. Идея: DPI увидит фейковый ClientHello с padding extension, длина которой "обещает" ещё N байт. Реальный ClientHello (следующий пакет) может быть воспринят DPI как продолжение padding, а не как отдельный ClientHello. Это работает только с DPI, которые реассемблируют TCP и парсят TLS record по полям длины. Реассемблирование — склейка данных из нескольких TCP-пакетов обратно в единый поток; см. также [[#padencap — подробности|padencap — подробности]].
---
## Миграция с nfqws1
nfqws1 — движок первой версии zapret, у которого `fake` включался ключом `--dpi-desync=fake`. Ниже — как перенести такую настройку в nfqws2. Общая таблица соответствия флагов nfqws1 и nfqws2 лежит в [[Zapret2/desync#Соответствие флагов nfqws1 → nfqws2|обзоре desync]].
### Соответствие параметров
Слева ключи nfqws1, справа их запись в аргументах `fake`; тип payload в nfqws2 задаётся отдельным флагом `--payload=…`.
| nfqws1 | nfqws2 |
|:-------|:-------|
| `--dpi-desync=fake` | `--lua-desync=fake:blob=<blob>` |
| `--dpi-desync-fake-http=<hex>` | `--payload=http_req --lua-desync=fake:blob=<hex>` |
| `--dpi-desync-fake-tls=<hex\|!>` | `--payload=tls_client_hello --lua-desync=fake:blob=<hex\|fake_default_tls>` |
| `--dpi-desync-fake-quic=<hex>` | `--payload=quic_initial --lua-desync=fake:blob=<hex>` |
| `--dpi-desync-fake-tls-mod=<list>` | `:tls_mod=<list>` |
| `--dpi-desync-fooling=md5sig` | `:tcp_md5` |
| `--dpi-desync-fooling=badseq` | `:tcp_seq=-10000` |
| `--dpi-desync-fooling=badack` | `:tcp_ack=-66000` |
| `--dpi-desync-fooling=datanoack` | `:tcp_flags_unset=ack` |
| `--dpi-desync-fooling=hopbyhop` | `:ip6_hopbyhop` |
| `--dpi-desync-fooling=hopbyhop2` | `:ip6_hopbyhop2` |
| `--dpi-desync-fooling=destopt` | `:ip6_destopt` |
| `--dpi-desync-fooling=ipfrag1` | `:ipfrag` |
| `--dpi-desync-ttl=N` | `:ip_ttl=N:ip6_ttl=N` |
| `--dpi-desync-autottl=...` | `:ip_autottl=...:ip6_autottl=...` |
| `--dpi-desync-repeats=N` | `:repeats=N` |
| `--dpi-desync-badseq-increment=N` | `:tcp_seq=N` |
| `--dpi-desync-badack-increment=N` | `:tcp_ack=N` |
| `--dpi-desync-any-protocol` | Не нужно; или `payload=all` в инстансе |
### Ключевое отличие: в nfqws1 fake был "глобальный"
В nfqws1 `--dpi-desync=fake` автоматически выбирал blob по типу payload: для TLS — `--dpi-desync-fake-tls`, для HTTP — `--dpi-desync-fake-http`, для QUIC — `--dpi-desync-fake-quic`. В nfqws2 blob задаётся вручную, и для разных payload нужны **отдельные инстансы**.
> [!note] Проще говоря
> В nfqws1 достаточно было включить `--dpi-desync=fake`: движок сам выбирал blob по типу данных, по ключам `--dpi-desync-fake-tls`, `--dpi-desync-fake-http` и `--dpi-desync-fake-quic`. В nfqws2 вы сами говорите, какой blob для какого типа данных, и для каждого типа пишете свой инстанс.
### Пример полной миграции: fake с TTL
Ключ `--dpi-desync-fake-http` превращается в `--payload=http_req` и `blob=…`, а `--dpi-desync-ttl` — в пару `ip_ttl` и `ip6_ttl`.
```bash
# nfqws1:
nfqws --dpi-desync=fake \
--dpi-desync-fake-http=0x00000000 \
--dpi-desync-ttl=6
# nfqws2 (эквивалент):
nfqws2 \
--payload=http_req \
--lua-desync=fake:blob=0x00000000:ip_ttl=6:ip6_ttl=6
```
### Пример полной миграции: fake с tls_mod + datanoack
`--dpi-desync-fooling=datanoack` становится `tcp_flags_unset=ack`, а `--dpi-desync-fake-tls=!` — стандартным blob-ом `fake_default_tls`; список `--dpi-desync-fake-tls-mod` переносится в `tls_mod=`; в эквиваленте к нему добавлен `padencap`, которого в записи nfqws1 не было.
```bash
# nfqws1:
nfqws --dpi-desync=fake \
--dpi-desync-fooling=datanoack \
--dpi-desync-fake-tls=! \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
# nfqws2 (эквивалент):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack:tls_mod=rnd,rndsni,dupsid,padencap
```
### Пример полной миграции: fake + multisplit
Одна команда nfqws1 с `fake,multisplit` разделяется на отдельные инстансы: для TLS и HTTP нужны разные blob-ы, поэтому `fake` пишется дважды, а [[multisplit]] получает свои аргументы, включая `seqovl` и [[Zapret2/desync/multisplit#seqovl_pattern|seqovl_pattern]].
```bash
# nfqws1:
nfqws --dpi-desync=fake,multisplit \
--dpi-desync-fooling=md5sig \
--dpi-desync-split-pos=1,midsld \
--dpi-desync-split-seqovl=5 \
--dpi-desync-split-seqovl-pattern=0x1603030000 \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
# nfqws2 (эквивалент — отдельные инстансы):
nfqws2 \
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--payload=http_req \
--lua-desync=fake:blob=fake_default_http:tcp_md5 \
--payload=tls_client_hello,http_req \
--lua-desync=multisplit:pos=1,midsld:seqovl=5:seqovl_pattern=0x1603030000
```
---
## Практические примеры
Двадцать готовых вариантов: от минимального фейка до связок с другими функциями. К большинству добавлено короткое пояснение, что произойдёт с пакетом.
### 1. Минимальный TLS-фейк (обязателен blob + fooling)
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5
```
Отправляет стандартный фейковый ClientHello с `www.w3.org`, сервер отбросит из-за TCP MD5 option.
### 2. Минимальный HTTP-фейк
```bash
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5
```
То же, что в первом примере, но для HTTP: стандартный blob `fake_default_http` (см. [[#Стандартные blob-ы|Стандартные blob-ы]]), а сервер отбросит фейк из-за TCP MD5 option.
### 3. QUIC (UDP) фейк с TTL
```bash
--payload=quic_initial --lua-desync=fake:blob=fake_default_quic:ip_ttl=1:ip6_ttl=1
```
Для UDP TCP-fooling невозможен, используем TTL=1 — фейк не дойдёт до сервера. См. нюанс [[#6. Для UDP нет TCP-fooling|«Для UDP нет TCP-fooling»]].
### 4. optional: тихо пропустить, если blob не загружен
```bash
--lua-desync=fake:blob=cloned_ch:optional:tcp_md5
```
Если blob `cloned_ch` не был создан предыдущим инстансом — пропуск без ошибки.
### 5. Множественные повторы фейка
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:repeats=11
```
11 одинаковых фейков подряд. Эффективно против DPI, который "считает" пакеты. См. нюанс [[#7. repeats может быть очень полезен|«repeats может быть очень полезен»]].
### 6. rnd: рандомизировать Random и Session ID
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd
```
Каждый фейк будет с уникальным Random и Session ID.
### 7. rndsni: случайный SNI в фейке
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rndsni
```
SNI в фейке будет случайным доменом (DPI не увидит ни реальный, ни `www.w3.org`).
### 8. sni=domain: конкретный SNI в фейке
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=sni=www.google.com
```
DPI увидит `www.google.com` в фейке. Полезно, если DPI блокирует по whitelist.
### 9. sni=%var: подстановка SNI из переменной
```bash
--lua-init="target='www.google.com'" \
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=sni=%target
```
Домен для SNI берётся из глобальной Lua-переменной `target`, которую задаёт `--lua-init`; порядок поиска переменной описан в разделе [[#Подстановка sni=%var|Подстановка sni=%var]].
### 10. dupsid: копирование Session ID из реального ClientHello
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,dupsid
```
Session ID фейка совпадёт с реальным — DPI может привязать фейк к текущей сессии.
### 11. Полный набор tls_mod (типовой пресет)
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid,padencap
```
Максимальная маскировка фейка: случайные Random/SID, случайный SNI, скопированный реальный SID, padencap.
### 12. TTL-fooling (жёсткий фейк)
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:ip_ttl=1:ip6_ttl=1
```
Фейк умрёт на первом хопе. Работает, если DPI стоит ближе к клиенту, чем сервер.
### 13. datanoack + badsum (комбинированный fooling)
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:badsum:tcp_flags_unset=ack:tls_mod=rnd,dupsid,padencap
```
Два уровня защиты: испорченная контрольная сумма + снятый ACK.
### 14. IP-фрагментация фейка (TCP)
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:ipfrag:ipfrag_pos_tcp=32
```
Фейк фрагментируется на IP-уровне. DPI может не уметь реассемблировать IP-фрагменты. Параметры разобраны в разделе [[#F) Standard ipfrag|F) Standard ipfrag]].
### 15. IP-фрагментация фейка (UDP/QUIC) в обратном порядке
```bash
--payload=quic_initial --lua-desync=fake:blob=fake_default_quic:ipfrag:ipfrag_pos_udp=8:ipfrag_disorder
```
QUIC-фейк фрагментируется на уровне IP: `ipfrag_pos_udp=8` задаёт позицию фрагментации для UDP (кратно 8, по умолчанию 8), а `ipfrag_disorder` отправляет IP-фрагменты в обратном порядке. Параметры описаны в разделе [[#F) Standard ipfrag|F) Standard ipfrag]].
### 16. Произвольный hex blob (нулевые байты)
```bash
--payload=http_req --lua-desync=fake:blob=0x00000000:ip_ttl=6:ip6_ttl=6
```
4 нулевых байта как фейк. DPI увидит "мусор" перед реальным HTTP.
### 17. Боевая связка: fake + multisplit для YouTube
```bash
--filter-tcp=443 --hostlist=youtube.txt \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:repeats=11 \
--lua-desync=multisplit:pos=1,midsld
```
11 фейков подряд (с MD5 fooling) + реальный payload разрезан на 3 части. Как [[multisplit]] режет по маркерам, описано в разделе [[Zapret2/desync/multisplit#Маркеры позиций (pos)|Маркеры позиций (pos)]].
### 18. Боевая связка: fake + multisplit + seqovl
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--lua-desync=multisplit:pos=1,midsld:seqovl=5:seqovl_pattern=0x1603030000
```
Фейк с полным tls_mod, затем реальный payload нарезан с seqovl. Приём `seqovl` разобран в разделе [[Zapret2/desync/multisplit#seqovl — скрытый фейк внутри сегмента|seqovl — скрытый фейк внутри сегмента]].
### 19. fake с клонированным ClientHello (двухинстансная схема)
```bash
--payload=tls_client_hello \
--lua-desync=tls_client_hello_clone:blob=cloned_ch:sni_del:sni_add=www.google.com \
--lua-desync=fake:blob=cloned_ch:optional:tcp_md5
```
Первый инстанс клонирует реальный ClientHello и подменяет SNI. Второй — отправляет клон как фейк. Флаг `optional` нужен на случай, если payload не TLS и клон не создан (см. нюанс [[#5. blob обязателен — без optional будет Lua exception|5]]).
### 20. fake для нескольких протоколов (отдельные профили)
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid,padencap \
--lua-desync=multisplit:pos=1,midsld \
--payload=http_req \
--lua-desync=fake:blob=fake_default_http:tcp_md5 \
--lua-desync=multisplit:pos=host,midsld \
--payload=quic_initial \
--lua-desync=fake:blob=fake_default_quic:ip_ttl=1:ip6_ttl=1
```
Для каждого протокола — свой blob и свой fooling. Как `--payload=` разделяет профиль на такие блоки, описано в заметке [[Zapret2/payload#Что такое тип payload (и чем он отличается от протокола потока)|о типах payload]] и в [[Zapret2/profile#Анатомия профиля|анатомии профиля]].
---
## 📚 См. также
- [[desync]] — обзор всех функций `--lua-desync` и общий контракт: кто вызывает функцию, что ей передаёт и как складываются вердикты
- [[жизненный цикл desync-функции]] — восемь стадий, общих для всех техник дурения, и отличия `fake` от общего скелета
- [[структура desync и диссекта]] — устройство таблицы `desync` и диссекта пакета, в том числе `reasm_data`
- [[ts-and-fooling]] — зачем фейк обязан умереть до сервера и какие бывают способы его испортить
- [[blob]] — как объявлять и передавать данные для аргумента `blob`
- [[payload]] — типы payload и фильтр `--payload`
- [[multisplit]] · [[multidisorder]] — сегментация, с которой `fake` чаще всего комбинируют
- [[fakedsplit]] · [[fakeddisorder]] — разрез с подмешиванием фейковых сегментов
- [[syndata]] — payload в SYN-пакете
- [[verify-strategy]] — как честно проверить, работает ли подобранная комбинация
- [[последовательность аргументов]] — как выстраивается цепочка инстансов в профиле
- [[profile]] · [[preset]] — где `--lua-desync` живёт среди остальных настроек
- [[DPI/DPI|DPI и ТСПУ]] — как устроена инспекция трафика, против которой работает `fake`
---
> **Источники:** `lua/zapret-antidpi.lua:438-461`, `lua/zapret-lib.lua:625-636` (`tls_mod_shim`), `lua/zapret-lib.lua:1194-1203` (`rawsend_payload_segmented`), `docs/manual.md:3991-4009`, `docs/manual.md:2425-2442` из репозитория zapret2.
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git2.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/desync/fake.md) · [скачать весь репозиторий одним zip-архивом](https://git2.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).