todo/Zapret2/desync/fake.md

813 lines
49 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
---
# `fake` — прямой фейк (zapret2 / nfqws2)
**Файл:** `lua/zapret-antidpi.lua:449`
**nfqws1 эквивалент:** `--dpi-desync=fake`
**Сигнатура:** `function fake(ctx, desync)`
`fake` — самая часто используемая функция в zapret. Она отправляет **отдельный** пакет (или группу пакетов) с фейковым payload из указанного blob. При этом функция **не выносит вердикт** и **не блокирует** отправку оригинала — оригинальный пакет уходит следом. DPI видит сначала фейк, затем настоящий трафик. Задача фейка — "отравить" состояние DPI ложными данными (неверный SNI, невалидный HTTP-запрос и т.д.).
Работает с **TCP и UDP** (в отличие от функций сегментации, которые только TCP).
Родственные функции: [[syndata]] (payload в SYN), [[fakedsplit]] (фейки + сегментация), [[fakeddisorder]] (фейки + обратный порядок), [[multisplit]] (чистая сегментация), [[multidisorder]] (сегментация в обратном порядке).
Общий для всех техник дурения порядок работы — восемь стадий от отсева чужого транспорта до вердикта — разобран в [[жизненный цикл desync-функции]]; здесь описано только то, чем `fake` от этого скелета отличается.
---
## Оглавление
- [Зачем нужен 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)
- [Практические примеры](#практические-примеры)
---
## Зачем нужен fake
DPI анализирует первые пакеты TCP/UDP-потока, ища сигнатуры (SNI в TLS ClientHello, Host в HTTP, QUIC Initial). Если **перед** настоящим пакетом отправить фейковый пакет с ложными данными, DPI может:
1. **Принять фейк за реальный трафик:** DPI обработает фейковый SNI/Host и примет решение на его основе — пропустит соединение
2. **Сбиться с состояния:** получив невалидные данные, DPI потеряет контекст потока и перестанет его анализировать
3. **Не суметь заблокировать:** если DPI блокирует по hostname, а в фейке другой hostname — реальный запрос может пройти
При этом **сервер должен отбросить фейк**. Для этого к фейку применяется fooling — порча заголовков (TTL, badseq, md5sig, badsum), из-за которой сервер или промежуточные маршрутизаторы отбрасывают пакет, а DPI — нет.
**fake** — "огонь по площадям": он шлёт фейк отдельным пакетом, не трогая оригинал. Для более тонкой работы (замешивание фейков внутри сегментов) используйте [[fakedsplit]]/[[fakeddisorder]], для скрытых фейков через TCP window — seqovl в [[multisplit]]/[[multidisorder]].
---
## Быстрый старт
Минимальный TLS-фейк (обязателен `blob` + fooling):
```bash
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5
```
Минимальный HTTP-фейк:
```bash
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5
```
QUIC-фейк (UDP):
```bash
--payload=quic_initial --lua-desync=fake:blob=fake_default_quic:ip_ttl=1:ip6_ttl=1
```
Типовая боевая связка 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
```
---
## Место в общем скелете
Тело `fake` построено по тому же шаблону, что и все остальные функции дурения: восемь стадий от отсева чужого транспорта до вердикта. Разобраны они один раз в [[жизненный цикл desync-функции]] — здесь только отклонения, и их у `fake` больше, чем у прочих техник:
| Стадия общего скелета | Что делает `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]].
---
## blob — источник фейковых данных
`blob` — обязательный аргумент `fake`. Он указывает, **что** именно отправить в качестве фейкового payload.
### Стандартные blob-ы
Zapret автоматически создаёт три стандартных blob-а при инициализации:
| 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` вместо заблокированного домена.
### Пользовательские blob-ы
```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`)
---
## tls_mod — модификации TLS в фейке
`tls_mod` позволяет модифицировать содержимое blob-а перед отправкой, подстраивая фейковый ClientHello под текущее соединение. Это делает фейк более убедительным для DPI.
### Опции 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 был перезаписан реальным).
### Подстановка sni=%var
Внутри `tls_mod` поддерживается специальная запись `sni=%variable`:
```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_data` отсутствует (например, маленький пакет, не потребовавший реассемблирования, или UDP) — `tls_mod` **молча пропускается**, без ошибок. Это отличие от [[syndata]], где `tls_mod` вызывается всегда (с `payload=nil`), потому что syndata работает на SYN-пакете, где никакого реального payload ещё нет.
**Обходной путь**, если 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)`.
---
## Полный список аргументов
Формат вызова:
```
--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
#### `optional`
- **Формат:** `optional` (флаг, без значения)
- **Описание:** Если blob отсутствует (не найден ни как inline hex, ни как `desync[name]`, ни как `_G[name]`) — тихий пропуск без ошибки. Без `optional` отсутствие blob-а вызывает `error()`
- **Использование:** защита от ошибок при использовании blob-ов, которые могут отсутствовать (например, генерируемых `tls_client_hello_clone` — если payload не TLS, clone не создаст blob)
#### `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
---
### B) Standard direction
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `dir` | `in`, `out`, `any` | `out` |
Фильтр по направлению пакета. `fake` по умолчанию работает только с исходящими (`out`).
- `dir=out` — только исходящие (от клиента к серверу)
- `dir=in` — только входящие (от сервера к клиенту)
- `dir=any`оба направления
При первом вызове с указанным `dir` функция делает `direction_cutoff_opposite` — отсекает себя от противоположного направления.
---
### C) Standard payload
| Параметр | Значения | По умолчанию |
|:---------|:---------|:-------------|
| `payload` | список типов через запятую | `known` |
Фильтр по типу payload на уровне Lua. Это **дополнительный** фильтр к `--payload=...` на уровне профиля.
- `payload=known` — только распознанные протоколы (`http_req`, `tls_client_hello`, `quic_initial` и т.д.)
- `payload=all` — любой payload, включая `unknown`
- `payload=tls_client_hello,http_req` — конкретные типы
- `payload=~unknown` — инверсия: всё кроме unknown
**Важно:** лучше ставить `--payload=...` на уровне профиля (C-код, быстрее), а не полагаться только на Lua-фильтр.
---
### D) Standard fooling
Модификации L3/L4 заголовков. В `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, сервер отбросит
---
### E) Standard ipid
| Параметр | Описание | По умолчанию |
|:---------|:---------|:-------------|
| `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).
---
### F) Standard ipfrag
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
| Параметр | Описание |
|:---------|:---------|
| `badsum` | Испортить L4 (TCP/UDP) checksum при реконструкции raw-пакета. Сервер отбросит такой пакет |
---
### H) Standard rawsend
| Параметр | Описание |
|:---------|:---------|
| `repeats=N` | Отправить каждый пакет/сегмент N раз (идентичные повторы) |
| `ifout=<iface>` | Интерфейс для отправки (по умолчанию определяется автоматически) |
| `fwmark=N` | Firewall mark (только Linux, nftables/iptables) |
---
## Автосегментация по MSS
Для 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).
Для UDP сегментации нет — blob отправляется как один UDP-пакет. Если blob больше MTU, пакет будет фрагментирован на уровне IP (или отброшен, если DF-бит установлен).
---
## Поведение при replay / reasm
Многопакетный запрос `nfqws2` придерживает, собирает в буфер `reasm_data` и перепроигрывает через desync-функции — механика описана в [[жизненный цикл desync-функции]] (стадия 6). `fake` использует из неё только половину: срабатывает на первой части (`replay_first`) и молчит на остальных, логируя «not acting on further replay pieces».
Вторую половину развилки — дроп последующих частей — `fake` не применяет, потому что ему нечего дропать: он не отправляет реальные данные, а лишь добавляет фейк. Поэтому все части replay проходят дальше по цепочке инстансов, и стоящая следом [[multisplit]] или [[multidisorder]] нарежет собранный реасм как обычно.
---
## Псевдокод алгоритма
Тело функции целиком, включая общие для всех техник стадии — они пронумерованы так же, как в [[жизненный цикл 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 **ничего не возвращает** (оригинал проходит).
---
## Сравнение с syndata и fakedsplit
| Аспект | `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]], но ограничен одной позицией разреза
---
## Нюансы и подводные камни
### 1. fake не делает DROP
Оригинальный пакет уйдёт следом (если другие инстансы не дропнут его). Это **ожидаемое поведение** — fake лишь добавляет фейковые пакеты перед оригиналом.
### 2. Без fooling фейк бесполезен или вреден
Если не задать fooling, сервер примет фейковый payload как настоящий. Для TCP это приведёт к рассинхронизации потока (данные с неверным seq будут интерпретированы неправильно). Для UDP сервер просто обработает фейковый пакет. **Всегда используйте fooling** (`tcp_md5`, `badsum`, `ip_ttl=1`, `tcp_flags_unset=ACK`, `tcp_seq=-10000` и т.д.).
> [!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 срабатывает ровно один раз — на первом куске. На последующих частях он ничего не делает. Это правильно: фейк нужен один раз, перед началом реальных данных.
### 4. tls_mod молча пропускается без reasm_data
Если вы задали `tls_mod=rnd,rndsni`, но `reasm_data` отсутствует (например, payload влез в один пакет и реассемблирование не потребовалось для некоторых конфигураций) — tls_mod просто не применится. Фейк будет отправлен с немодифицированным blob. Для гарантированных модификаций используйте `--lua-init` для подготовки blob заранее.
### 5. blob обязателен — без optional будет Lua exception
Без `blob=...` функция вызывает `error()`. Это не тихий пропуск, а exception, который прервёт обработку всех инстансов для данного пакета. Если blob может отсутствовать — **всегда указывайте `optional`**.
### 6. Для UDP нет TCP-fooling
При использовании с 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 — фейк уйдёт после реальных данных, что может быть неэффективно.
### 9. fake не отсекается на не-TCP/не-UDP
В отличие от [[multisplit]] (который делает `instance_cutoff` на не-TCP), fake просто ничего не делает для пакетов, не являющихся TCP или UDP. Он **не отсекает** себя — будет продолжать проверять следующие пакеты в потоке.
### 10. padencap + реальный payload — тонкий трюк
`padencap` увеличивает длины в TLS-структуре фейка на размер реального payload. Идея: DPI увидит фейковый ClientHello с padding extension, длина которой "обещает" ещё N байт. Реальный ClientHello (следующий пакет) может быть воспринят DPI как продолжение padding, а не как отдельный ClientHello. Это работает только с DPI, которые реассемблируют TCP и парсят TLS record по полям длины.
---
## Миграция с nfqws1
### Соответствие параметров
| 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 нужны **отдельные инстансы**.
### Пример полной миграции: fake с 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
```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
```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
```
### 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 — фейк не дойдёт до сервера.
### 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, который "считает" пакеты.
### 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
```
### 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-фрагменты.
### 15. IP-фрагментация фейка (UDP/QUIC) в обратном порядке
```bash
--payload=quic_initial --lua-desync=fake:blob=fake_default_quic:ipfrag:ipfrag_pos_udp=8:ipfrag_disorder
```
### 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 части.
### 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.
### 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. Второй — отправляет клон как фейк.
### 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.
---
> **Источники:** `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.