# 💀 `ts` и fooling: почему фейк должен умереть
> [!summary] Главная мысль
> Fake-пакет в [[desync|десинхронизации]] должен дойти до DPI, но **умереть** до того, как его примет настоящий сервер. Способ "правильно убить fake" называется fooling. Самый капризный из них — `ts`: он не добавляет TCP timestamps с нуля, а работает только если настоящий TCP-сеанс уже идёт с timestamp option. На Windows timestamps обычно выключены, поэтому `ts` как единственный TCP fooling там опасен — fake может перестать быть "плохим" и сломать соединение.
Эта заметка отвечает на три вопроса, которые чаще всего возникают про fake-пакеты: **зачем** они нужны, **почему** они обязаны умирать, и **как** заставить их умирать правильно на разных платформах. Параметр `ts` разобран как главный пример, потому что именно он ломается тише и обиднее всего.
Полный справочник параметров `--lua-desync` (включая `tcp_ts`, `tcp_md5`, `tcp_seq` и прочие) живёт в [[desync]] — здесь без дублирования, упор на смысл и подводные камни.
Связанные заметки: [[desync]], [[fake]], [[fakedsplit]], [[fakeddisorder]], [[основные флаги]], [[оркестратор]], [[windivert]].
---
## TL;DR
- **Fake обязан умереть до сервера.** Иначе сервер получит мусорный ClientHello/HTTP-запрос и нормальное соединение сломается.
- **fooling — это контролируемая порча fake**, чтобы серверный TCP/IP стек отбросил его, а DPI успел разобрать и сделать неверный вывод.
- **У каждого fooling свой "слой смерти"** (checksum, seq, ack, timestamp, TTL, IPv6-заголовки) и своё место, где он не проходит (NAT, роутер, сервер).
- **`ts` смещает `TSval`** настоящего timestamp назад, чтобы сервер счёл fake "слишком старым". Работает только если timestamps согласованы в начале TCP-сеанса.
- **Платформа решает всё.** Windows по умолчанию без timestamps; за домашним NAT отмирают `badsum`/`datanoack`; на самом роутере наоборот могут ожить. Один и тот же fooling бывает отличным в одном месте и бесполезным в другом.
---
## Зачем нужен [[fake]] и почему он обязан умереть
Начнём с того, что вообще делает fake, потому что без этого `ts` и fooling выглядят набором магических флагов.
Когда zapret отправляет fake, у этого пакета **две разные аудитории**, и им нужно прямо противоположное.
Для DPI fake должен выглядеть достаточно настоящим: DPI должен успеть разобрать его как TLS, HTTP, QUIC или другой payload и сделать неправильный вывод о соединении (например, увидеть в SNI безобидный сайт).
Для сервера этот же fake должен быть плохим: серверный TCP/IP стек должен отбросить его ещё до прикладного уровня. Тогда настоящий пакет придёт следом и будет обработан нормально, как будто никакого fake и не было.
> [!example] На пальцах: разговор под прослушкой
> Представь, что между тобой и собеседником (сервером) сидит прослушка (DPI). Ты хочешь, чтобы прослушка решила, будто разговор безобидный. Для этого ты вслух произносишь подставную фразу — её **должна** услышать прослушка (она ближе), но **не должен** услышать собеседник, иначе он переспросит "что?" и собьётся.
> Значит, подставную фразу надо сказать так, чтобы она "не долетела" до собеседника: либо слишком тихо (это TTL), либо на ломаном языке, который его телефон отбракует как помеху (это `badsum`, `badseq`, `ts`). Вот эта "управляемая порча" и есть **fooling** — способ заставить fake умереть, не дойдя до сервера.
> [!info] Формула fooling
> Fake должен быть **«достаточно настоящим»** для DPI и **«достаточно неправильным»** для сервера. fooling добавляет ровно ту неправильность, которая убивает fake на пути к серверу, но не мешает DPI его разобрать.
> [!danger] Что будет, если fake НЕ умрёт
> Самая неприятная поломка — это не "обход не сработал". Хуже, когда fake доходит до сервера живым: сервер примет его как реальную часть TCP-потока, поверх ляжет настоящий пакет, и нормальный TLS/HTTP-поток развалится. Поэтому "fake не дошёл до сервера" — это не побочный эффект, а **обязательное условие** работы стратегии.
---
## Что такое fooling
fooling — это и есть та самая контролируемая "неправильность", которой портят fake, чтобы он гарантированно умер на пути к серверу.
В zapret1 fooling задаётся **готовыми режимами**: `ts`, `md5sig`, `badseq`, `badsum`, `datanoack` и так далее через флаг `--dpi-desync-fooling=`.
В zapret2 то же самое разложено на **низкоуровневые параметры** Lua-функций: `tcp_ts=N`, `tcp_md5`, `tcp_seq=N`, `tcp_ack=N`, `badsum`, `tcp_flags_unset=ACK` и другие. Это даёт больше гибкости, но требует понимать, что именно делает каждый параметр — потому что "магического" поведения по умолчанию у них меньше (см. [[desync]] для полного списка).
---
## Что такое TCP options (и зачем fooling с ними играет)
Чтобы понять `ts`, `md5sig` и `tcp_ts_up`, надо сначала понять, **куда** они вообще пишут. У TCP-пакета есть две разные зоны, которые fooling может портить.
**Фиксированные поля** заголовка есть всегда: sequence number (порядковый номер байта в потоке), acknowledgment number (что подтверждено), флаги (`SYN`, `ACK`, `RST`…), checksum (контрольная сумма), window (размер окна). По ним бьют `badseq` (портит seq), `badack`/`tcp_ack` (портит ack), `badsum` (портит checksum), `datanoack` (снимает флаг ACK).
**TCP options** — это переменная часть в конце TCP-заголовка, куда складываются «дополнительные возможности», о которых стороны договариваются. Timestamp — одна из таких опций. По опциям бьют `ts`/`tcp_ts` (меняет timestamp-опцию), `md5sig`/`tcp_md5` (добавляет MD5-опцию), `tcp_ts_up` (переставляет timestamp-опцию вперёд), `tcp_nop_del` (удаляет опции-заполнители NOP).
> [!example] Как устроена одна TCP option
> Почти каждая опция — это три части подряд: **kind** (1 байт — что за опция), **length** (1 байт — полная длина опции вместе с этими двумя байтами) и **data** (содержимое). Исключение — две однобайтовые опции без length и data: End of Option List (kind 0) и No-Operation / NOP (kind 1).
> Пример timestamp-опции в байтах (kind 8, длина 10, дальше 4 байта `TSval` и 4 байта `TSecr`):
> ```text
> 08 0A | 11 22 33 44 | 00 00 00 00
> ▲ ▲ ▲ ▲
> │ │ │ └ TSecr (4 байта) — эхо timestamp той стороны
> │ │ └ TSval (4 байта) — timestamp отправителя
> │ └ length = 10 (2 байта заголовка + 8 байт данных)
> └ kind = 8 (Timestamps)
> ```
> На практике перед timestamp часто идут два NOP (`01 01`) — просто чтобы выровнять опцию по 4 байта. Поэтому порядок опций вообще можно «двигать» — это и делает `tcp_ts_up`.
Распространённые TCP options (номера kind — по реестру IANA):
| kind | Опция | Зачем нужна | Стандарт |
| --- | --- | --- | --- |
| 0 | End of Option List | Отметка конца списка опций | RFC 9293 |
| 1 | No-Operation (NOP) | Заполнитель для выравнивания | RFC 9293 |
| 2 | Maximum Segment Size (MSS) | Максимальный размер сегмента | RFC 9293 |
| 3 | Window Scale | Множитель размера окна | RFC 7323 |
| 4 | SACK Permitted | Согласие на выборочные подтверждения | RFC 2018 |
| 5 | SACK | Сами выборочные подтверждения | RFC 2018 |
| 8 | **Timestamps (`TSval`/`TSecr`)** | Замер RTT и защита от старых пакетов (PAWS) | RFC 7323 |
| 19 | MD5 Signature | Подпись сегмента (используется в BGP) | RFC 2385 |
| 29 | Authentication Option (AO) | Современная замена MD5 | RFC 5925 |
| 34 | TCP Fast Open | Данные уже в SYN | RFC 7413 |
> [!check] Исходники zapret = стандарт
> Номера kind в zapret заданы в `nfq2/darkmagic.h` (`TCP_KIND_TS=8`, `TCP_KIND_MD5=19`, `TCP_KIND_SCALE=3`, `TCP_KIND_MSS=2`, `TCP_KIND_SACK_PERM=4`, `TCP_KIND_NOOP=1`, `TCP_KIND_END=0`) и совпадают с реестром IANA и RFC. zapret1 в `nfq/darkmagic.c` использует те же числа напрямую (`tcp_find_option(tcp,8)` — поиск timestamp по kind 8). То есть это не «выдумка zapret», а ровно те значения, что лежат в спецификации.
> [!important] Главное свойство: возможность согласуется один раз — в начале
> Ключевой момент для fooling по опциям: **поддержка timestamp (как и window scale, SACK) согласуется один раз — в первом обмене (`SYN` и `SYN,ACK`), и её нельзя «включить» в середине соединения**. Если timestamp не предложили в SYN — дальше в этом сеансе его не будет. (Это не значит, что опции вообще не появляются по ходу: например, SACK-блоки и MD5-подпись идут в обычных сегментах. Но именно timestamp-сеанс задаётся на старте.) Поэтому нельзя просто «дописать timestamp в один пакет»: для сервера это не сделает сеанс timestamp-сеансом. Отсюда и растёт вся капризность `ts` — подробно ниже.
---
## Как заставить fake умереть: каталог способов
Универсального fooling нет. Каждый способ убивает fake **на своём слое** (контрольная сумма, sequence, ack, timestamp, TTL, IPv6-заголовки) и **ломается в своём месте** (NAT, роутер, конкретный сервер, конкретный DPI). Выбор зависит не от названия, а от среды.
| Способ (zapret1 / zapret2) | Как убивает fake | Где ломается |
| --- | --- | --- |
| `ts` / `tcp_ts=N` | Смещает `TSval` так, что сервер считает пакет слишком старым (PAWS) | Нужны согласованные TCP timestamps. На Windows по умолчанию выключены |
| `md5sig` / `tcp_md5` | Добавляет TCP MD5 option со случайными байтами — Linux-сервер обычно отбрасывает | Не универсально. Увеличивает TCP header → на больших TLS ClientHello (Kyber, малая split-позиция) возможен `message too long` (переполнение MTU) |
| `badsum` / `badsum` | Портит контрольную сумму — пакет битый для любого узла | NAT/conntrack/роутер часто отбрасывает fake раньше DPI. На самом роутере живёт лучше, чем за ним |
| `badseq` / `tcp_seq=N` | Уводит sequence за TCP-окно — сервер отбрасывает | Внимательный DPI тоже следит за окном и может не поверить fake. В zapret1 дефолт смещения `-10000` |
| (bad ack) / `tcp_ack=N` | Портит ACK при валидном seq — сервер отбрасывает | На Linux надёжно дропается **только** если timestamp option идёт первой → нужен `tcp_ts_up` (см. ниже) |
| `datanoack` / `tcp_flags_unset=ACK` | Снимает ACK с data-пакета — сервер такое не принимает | NAT с masquerade (типично на домашних роутерах) часто режет. Зависит от места запуска |
| `tcp_flags_set=SYN` | Делает невалидную комбинацию флагов — сервер не примет, DPI примет | Работает не на всех серверах; через NAT может отбрасываться |
| TTL / `ip_ttl=N`, `ip_autottl` | Fake "не долетает": умирает по дороге, не дойдя до сервера | Надо угадать число хопов до DPI. Маршрут меняется → fake умрёт рано или пробьётся до сервера. Стоковые прошивки роутеров фиксируют исходящий TTL |
| `hopbyhop` / `ip6_hopbyhop` | IPv6 extension header: DPI разберёт, сервер отбросит (или провайдер отфильтрует) | Только IPv6. `hopbyhop2` (два заголовка) дропается всеми ОС гарантированно, но может резаться сетью |
> [!tip] Как выбирать fooling
> Не по названию, а по **месту запуска, протоколу, NAT, серверу и pcap**. Один и тот же режим бывает отличным на роутере и бесполезным за роутером. Подбор делается тестами (`blockcheck`/pcap), а не на слух.
---
## `ts` подробно: что делает и почему капризный
`ts` стоит особняком, потому что ломается тише всех: он не падает с ошибкой, а просто молча перестаёт портить fake.
### Что именно делает `ts`
`ts` работает с **TCP timestamp option** (kind 8 — см. раздел про TCP options выше). Внутри неё два 32-битных значения: `TSval` (timestamp отправителя) и `TSecr` (timestamp второй стороны, который отправитель возвращает обратно). Для `ts` важен именно `TSval`.
Смысл техники: взять timestamp из настоящего пакета и **сместить `TSval` назад**. Это запускает на сервере механизм **PAWS** (Protection Against Wrapped Sequences — защита от «обернувшихся» порядковых номеров, RFC 7323): сервер отбрасывает сегменты со «слишком старым» timestamp. "Состаренный" fake умирает на сервере по PAWS, а DPI обычно не проверяет timestamp так строго и спокойно разбирает payload.
> [!quote] RFC 7323, правило PAWS (R1)
> «If there is a Timestamps option in the arriving segment, `SEG.TSval < TS.Recent`, TS.Recent is valid … then treat the arriving segment as not acceptable … and drop the segment.»
> Суть: если в пришедшем сегменте timestamp **меньше** последнего запомненного сервером (`TS.Recent`), сегмент считается старым и отбрасывается. Сдвиг `TSval` на `-600000` искусственно делает fake «старым» — и сервер дропает его по PAWS.
> [!note] Смещение — со знаком, и диапазон имеет значение
> Смещение `ts` может быть и отрицательным, и положительным, но на практике используют отрицательное ("назад"). По документации zapret1 рабочий инкремент — примерно **от `-100` до `-0x80000000`**. Поэтому и `-1000`, и `-600000` попадают в рабочий диапазон. В zapret1 дефолт `ts` — `-600000`.
> [!example] zapret1 и zapret2
> В zapret1 это выглядит так:
> ```bash
> --dpi-desync-fooling=ts
> --dpi-desync-ts-increment=-600000
> ```
>
> В zapret2 прямого режима `ts` нет — он пишется как параметр fooling:
> ```bash
> --lua-desync=fake:tcp_ts=-600000
> ```
> [!warning] В zapret2 нужно явное значение
> `tcp_ts` требует число. Нельзя просто написать `tcp_ts` и ждать "дефолта как в zapret1". При портировании стратегии из zapret1 смещение надо выбрать руками (часто `-600000`, чтобы повторить старое поведение).
### Почему `ts` не работает без согласованных timestamps
Это главный подвох. TCP timestamps — **не** произвольная добавка к любому пакету. Они согласуются в начале соединения.
Клиент должен предложить timestamp option в SYN. Сервер должен ответить timestamp option в SYN,ACK. Только после этого весь TCP-сеанс считается сеансом с timestamps, и у data-пакетов появляется timestamp option.
Если клиент не предложил timestamps или сервер их не поддержал, дальше соединение идёт **без них** — и портить `ts`-ом будет попросту нечего.
> [!quote] RFC 7323, согласование timestamp
> «A TCP MAY send the TSopt in an initial `<SYN>` segment … and MAY send a TSopt in `<SYN,ACK>` only if it received a TSopt in the initial `<SYN>` segment.» И далее: «Once TSopt has been successfully negotiated, that is both `<SYN>` and `<SYN,ACK>` contain TSopt, the TSopt MUST be sent in every non-`<RST>` segment for the duration of the connection.»
> Перевод сути: сервер вставит timestamp в ответ, **только** если увидел его в SYN клиента. Если согласовали — timestamp обязан быть в каждом дальнейшем пакете. Если не согласовали — его не будет нигде, и `ts` нечего портить. RFC 7323 обновляет более старый RFC 1323.
> [!danger] Главное ограничение `ts`
> Если в настоящем TCP-пакете нет timestamp option, `ts` не на чем работать. "Вставить timestamp в fake руками" недостаточно: TCP-сеанс не был согласован как timestamp-сеанс, и сервер не обязан применять к такому fake логику PAWS. Fake перестаёт быть "плохим" — и может дойти до сервера живым.
Поэтому zapret2 **не создаёт** timestamp option с нуля. В `lua/zapret-lib.lua` он ищет `TCP_KIND_TS`, проверяет длину данных (8 байт) и меняет `TSval`, только если option уже есть. Если её нет — в debug уходит сообщение:
```text
apply_fooling: timestamp tcp option not present or invalid
```
Это не ошибка запуска и не падение стратегии, а сигнал: к этому конкретному пакету `tcp_ts=N` по сути не применился.
> [!note] Как читать это сообщение
> Оно не говорит "Windows виновата". Оно сообщает только факт: в данном TCP-пакете не было валидной timestamp option. Причина может быть в Windows, в настройках клиента, в сервере или просто в том, что [[profile|Профиль]] применился не к тому пакету.
### `cond_tcp_has_ts` — не стрелять вхолостую
Вместо того чтобы вручную гадать, есть ли в пакете timestamp, zapret2 даёт встроенную проверку времени выполнения.
Функция `cond_tcp_has_ts(desync)` возвращает true, только если диссект — это TCP и в нём присутствует timestamp option (в `lua/zapret-auto.lua`). В документации (`docs/manual.md`) она же фигурирует под именем `cond_tcp_ts`.
> [!tip] Зачем это нужно
> Через [[оркестратор|условный оркестратор]] можно повесить `ts`-зависимый fake на условие `cond_tcp_has_ts` — тогда этот вариант стратегии сработает **только** на пакетах, реально несущих timestamp option, а не будет молча "пустеть" на всех остальных. Это честнее, чем надеяться на ручную проверку pcap, и полезно для стратегий "поставил и забыл".
---
## `tcp_ts_up` — это не `ts`
`tcp_ts_up` легко спутать с `tcp_ts`, но это разные вещи. `tcp_ts=N` меняет значение `TSval`. А `tcp_ts_up` только **переносит уже существующую** timestamp option в начало списка TCP options.
Автор zapret2 описывает это как странный, но практический workaround: Linux стабильно отбрасывает пакеты с валидным `seq` и плохим `ack` **только если timestamp option идёт первой**. Старый nfqws1 случайно всегда ставил timestamp первым, потому что не сохранял порядок TCP options — и потому "старое работало, а новое нет". `tcp_ts_up` повторяет старое поведение.
> [!note] Главное про `tcp_ts_up`
> Он не включает timestamps и не создаёт timestamp option. Если в пакете её нет — поднимать нечего.
Типичная zapret2-связка для HTTP:
```bash
--lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
> [!info] Это порт "badseq без badseq" из zapret1
> Связка `tcp_ack=-66000:tcp_ts_up` — это документированный эквивалент zapret1-конструкции `--dpi-desync=fakedsplit --dpi-desync-fooling=badseq --dpi-desync-badseq-increment=0`, то есть **плохой ACK при валидном seq**. `tcp_ts_up` здесь нужен, чтобы воспроизвести старый порядок опций (timestamp первым), на котором Linux надёжно дропает такие сегменты.
> [!note] Куда именно вешается `tcp_ts_up` в split-функциях
> Плохой ACK (`tcp_ack`) применяется к **fake** — именно fake должен умереть на сервере. К **оригинальным** (настоящим) сегментам применяется только `tcp_ts_up` (плюс `ip_id`), их ACK остаётся валидным, поэтому они нормально доходят до сервера. Сам `tcp_ts_up` ставит timestamp option первой: без этого Linux ненадёжно отбрасывает пакет с валидным seq и плохим ACK.
---
## Отличие zapret1 и zapret2
В zapret1 `ts` — готовый режим fooling:
```bash
--dpi-desync-fooling=ts
--dpi-desync-ts-increment=-600000
```
Внутри код ищет TCP option kind `8`. Если timestamps есть — option копируется в fake, `TSval` смещается, `TSecr` остаётся без изменения. Если timestamps нет — option в fake не добавляется. То есть zapret1 тоже **не превращает** обычный TCP-сеанс в timestamp-сеанс.
В zapret2 готовое слово `ts` разложено на явные Lua-параметры:
```bash
--lua-desync=fake:tcp_ts=-600000
```
или, например, связка с плохим ACK:
```bash
--lua-desync=fakedsplit:tcp_ack=-66000:tcp_ts_up
```
> [!important] При портировании из zapret1
> zapret1 `ts` по умолчанию ближе к `tcp_ts=-600000`. А в `blockcheck2` стандартный тест использует `tcp_ts=-1000`. Оба смещения рабочие, но это **разные** значения, и нельзя молча считать их одним и тем же поведением.
---
## Как правильно атаковать сервер на разных платформах
Это самая практическая часть. Один и тот же [[profile|Профиль]] ведёт себя по-разному в зависимости от того, **где** запущен zapret и **чья ОС** открывает исходящий TCP-сеанс. Именно платформа определяет, какой fooling вообще имеет шанс сработать.
> [!example] Почему место запуска решает всё
> fooling убивает fake на пути к серверу. Но на этом пути стоят промежуточные узлы — NAT домашнего роутера, провайдерский NAT, conntrack. Они могут убить fake **раньше**, чем тот дойдёт до DPI (тогда обход не сработает), или, наоборот, пропустить то, что должно было умереть. Поэтому "рабочий" fooling — всегда привязан к конкретной точке запуска.
### Windows-клиент напрямую
TCP-сеанс открывает сама Windows, а она по умолчанию держит timestamps **выключенными**. Значит, `ts` "из коробки" работать не будет — fake не получит порчу и может дойти до сервера живым.
Проверка состояния:
```bat
netsh interface tcp show global
```
Включение:
```bat
netsh interface tcp set global timestamps=enabled
```
> [!warning] Три практических нюанса для Windows
> 1. Настройку надо включать на **каждом** клиентском устройстве — нельзя молча рассчитывать, что у всех пользователей [[preset|пресета]] timestamps уже включены.
> 2. После включения проверяй **новые** соединения. Уже открытый TCP-сеанс не станет timestamp-сеансом задним числом.
> 3. [[profile|Профиль]], который держится **только** на `ts`, без этой настройки — ненадёжный Windows-профиль.
### Linux-клиент напрямую
Linux обычно включает TCP timestamps по умолчанию. Поэтому `--dpi-desync-fooling=ts` на Linux часто работает сразу, без дополнительных настроек — это ровно та ситуация, на которую `ts` и рассчитан.
### За домашним роутером (через NAT)
Если трафик клиента проходит через NAT домашнего роутера (masquerade), часть fooling **умирает в NAT раньше DPI**:
- `badsum` — битую контрольную сумму роутер/conntrack часто режет на месте.
- `datanoack` / `tcp_flags_unset=ACK` — пакеты с битыми/снятыми флагами Linux-NAT с masquerade обычно не пропускает.
То, что зависит от заголовков самого TCP, а не от валидности пакета для NAT (`ts`, `badseq`, разумный `tcp_ack`, сегментация), переживает NAT лучше. Но финальное слово — за pcap.
### На самом роутере
Если zapret запущен **на роутере** (исходящий сеанс открывает роутер), картина меняется: `badsum` и `datanoack` отсюда могут работать, потому что fake уже не проходит через "свой" NAT по дороге наружу.
> [!warning] Transparent proxy (`tpws`, `squid`) — отдельный случай
> Пакетный фильтр (`nfqws`/`winws`) **не открывает** TCP-сеанс сам — его открывает конечный клиент, и timestamps согласует ОС клиента (Windows). А вот transparent proxy (`tpws`, `squid`) **терминирует** соединение клиента и открывает **новый** наружу — тогда timestamps для внешнего плеча согласует ОС роутера (Linux), и настройка Windows-клиента на это плечо уже не влияет. Поэтому при proxy проверять timestamps надо на устройстве, которое реально открывает соединение к серверу.
| Платформа / место | TCP timestamps | Что обычно живёт | Что чаще ломается |
| --- | --- | --- | --- |
| Windows-клиент напрямую | Выкл. по умолчанию (включается `netsh`) | После включения timestamps — `ts`; сегментация | `ts` без `netsh`; зависит от устройства |
| Linux-клиент напрямую | Вкл. по умолчанию | `ts` из коробки; большинство fooling | — |
| За домашним роутером (NAT/masquerade) | Зависит от клиента | `ts`, `badseq`, сегментация | `badsum`, `datanoack` (режет NAT) |
| На самом роутере | Зависит от ОС роутера | `badsum`, `datanoack`, TTL | TTL требует подбора хопа |
| Через transparent proxy (`tpws`/`squid`) | Зависит от **ОС роутера**, не клиента | fooling по ОС роутера (Linux) | настройка Windows-клиента на внешнее плечо не влияет |
---
## Почему `blockcheck` может вводить в заблуждение
`blockcheck.sh` (zapret1) и `blockcheck2.sh` (zapret2) на Windows **сами** пытаются включить timestamps через `netsh`. Это удобно для теста, но искажает интерпретацию результата.
Если blockcheck показал, что `ts` сработал — это может означать не "у пользователя всё было готово", а "blockcheck сначала включил системную настройку, а потом тестировал".
> [!tip] Как правильно читать результат
> Нашёл blockcheck рабочий `ts` — отдельно помни, что на Windows он мог предварительно выполнить `netsh interface tcp set global timestamps=enabled`. Для пользовательского [[preset|пресета]] эту настройку всё равно надо документировать.
В zapret1 blockcheck после успешного `ts` печатает предупреждение:
```text
WARNING ! although ts fooling worked it will not work without timestamps being enabled in the client OS. In windows timestamps are DISABLED by default.
```
Предупреждение есть **в blockcheck**, но обычный runtime `winws.exe` такую фразу при каждом запуске писать не обязан.
---
## Что видно в логах
В zapret1 `winws.exe` с `--dpi-desync-fooling=ts` может молча собрать fake без timestamp-порчи, если в исходном пакете timestamps нет. В коде сначала вызывается `tcp_find_timestamps()`; если option нет, указатель `timestamps` равен `NULL`, и `fill_tcphdr()` не добавляет timestamp option в fake. Никакого крика в лог.
В zapret2 чуть прозрачнее — Lua-функция пишет debug-сообщение:
```text
apply_fooling: timestamp tcp option not present or invalid
```
Но это именно debug: без `--debug` обычный `DLOG` не виден.
> [!example] Debug в zapret2
> На консоль:
> ```bash
> winws2 --debug ...
> ```
> В файл:
> ```bash
> winws2
[email protected] ...
> ```
> [!warning] Отсутствие строки не доказывает успех
> Нет строки про timestamp — это не значит, что `ts` сработал. Возможно, debug не включён, пакет не попал в нужный [[profile|Профиль]] или Lua-функция не вызывалась на этом payload.
---
## Как проверять `ts` правильно
Сначала — настройка Windows:
```bat
netsh interface tcp show global
```
Если timestamps выключены:
```bat
netsh interface tcp set global timestamps=enabled
```
После этого проверяй **новые** соединения.
Лучший способ проверки — pcap. Смотреть надо не только fake, а **весь TCP handshake**, потому что timestamps согласуются именно там.
> [!check] Признаки, что `ts` действительно применим
> - В SYN от клиента есть TCP timestamp option.
> - В SYN,ACK от сервера тоже есть timestamp option.
> - В data-пакете, к которому применяется `fake`/`fakedsplit`, timestamp option на месте.
> - В fake виден смещённый `TSval`, и сервер не отвечает на fake как на валидный payload.
Если timestamp option нет уже в SYN — дальше искать `ts` бессмысленно: такой TCP-сеанс не timestamp-сеанс.
---
## Как писать это в [[preset|пресетах]] и UI
Если в пресете есть TCP-[[profile|Профиль]], который держится только на `ts`, для Windows рядом нужен явный текст. Хорошая формулировка:
> [!warning] Windows и `ts`
> Для `ts`-fooling на Windows нужны включённые TCP timestamps:
> ```bat
> netsh interface tcp set global timestamps=enabled
> ```
> Без этого TCP-фейки могут доходить до сервера и ломать соединение.
Если нужен пресет "поставил и забыл" — не держи весь TCP только на `ts`. Тестируй альтернативы и комбинации: `md5sig`, `badseq`, `datanoack`, TTL/autottl, `seqovl`. Либо гейти `ts`-вариант через `cond_tcp_has_ts`, чтобы он не работал вхолостую. Но каждую комбинацию проверяй pcap/debug, а не выбирай на ощущение.
---
## Где здесь `seqovl`
`seqovl` лучше не воспринимать как ещё один обычный fooling — это другой приём.
В обычном fake мы стараемся, чтобы сервер **полностью отбросил** поддельный сегмент. В `seqovl` идея тоньше: сервер принимает только нужную часть потока из-за **пересечения TCP sequence numbers** (подробный разбор `seqovl` — в [[multisplit]]).
Поэтому `seqovl` интересен там, где header-fooling плохо проходит или слишком зависит от NAT. Но это не "автоматическая замена", а отдельная логика, требующая аккуратной настройки позиции и понимания payload.
---
## 🎣 Готовые обманки: как fooling собирают на практике
Вся теория выше — про то, **почему** fake умирает. А вот как этих «убийц fake» совмещают в реальных стратегиях. Примеры взяты из официального `zapret2/docs/readme.md` — это типовые комбинации, показанные автором как «примерно так строятся стратегии».
> [!warning] Это примеры, а не магия
> Показаны только desync-части (без обвязки `--filter`/`--qnum`). Что именно сработает у тебя — зависит от конкретного DPI и провайдера, поэтому любую комбинацию гоняй через `blockcheck2` и проверяй pcap, а не копируй вслепую. Ценность ниже — увидеть, **как** складывают fooling и **что в каждой обманке убивает fake**.
**1. TLS-фейк, который сервер отбросит по MD5-опции** *(readme.md:107)*
```bash
--payload=tls_client_hello \
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid
```
DPI видит поддельный ClientHello (со случайным SNI) и решает «сайт безобидный». Сервер дропает fake из-за лишней TCP MD5-опции (`tcp_md5`). Настоящий ClientHello проходит следом.
**2. HTTP-фейк + MD5** *(readme.md:108)*
```bash
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5
```
То же для HTTP: подставной запрос отравляет DPI, MD5-опция убивает fake на сервере.
**3. Сегментация + `seqovl`, вообще без порчи заголовков** *(readme.md:109)*
```bash
--payload=tls_client_hello,http_req \
--lua-desync=multisplit:pos=1:seqovl=5:seqovl_pattern=0x1603030000
```
Здесь отдельный fake не нужен: ClientHello режется, а перекрытие sequence (`seqovl`) подсовывает DPI ложное начало потока. Хорошо там, где header-fooling не проходит через NAT (см. [[multisplit]]).
**4. Fake, который «не долетает» до сервера по TTL** *(readme.md:155)*
```bash
--lua-desync=fake:blob=0x00000000:ip_ttl=5:ip6_ttl=3 \
--lua-desync=multidisorder:pos=5,endhost-1
```
fake с маленьким TTL умирает по дороге, не дойдя до сервера, — а DPI его уже увидел. Следом идёт реальная сегментация с обратным порядком.
**5. Плохой ACK + timestamp сверху (= zapret1 `badseq` с `increment=0`)** *(readme.md:237)*
```bash
--payload=http_req \
--lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
Наша главная связка из разбора `tcp_ts_up`: fake с валидным seq и плохим ACK сервер надёжно отбрасывает, потому что timestamp-опция поднята вперёд.
**6. Автоподбор TTL + MD5 как страховка** *(readme.md:355)*
```bash
--lua-desync=fake:blob=fake_default_http:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5 \
--lua-desync=fakedsplit:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5
```
`ip_autottl` сам подбирает TTL по hop-расстоянию, а `tcp_md5` — подстраховка: если fake всё-таки долетит до сервера, тот отбросит его по MD5-опции.
**7. Fake + MD5 + сдвиг seq + повторы** *(readme.md:367)*
```bash
--lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000:repeats=6 \
--lua-desync=multidisorder:pos=midsld
```
Несколько «убийц fake» сразу (MD5-опция + увод `seq` за окно), а `repeats=6` шлёт fake шесть раз, чтобы наверняка попасть в окно анализа DPI.
> [!tip] «Обманка на `ts`» для Windows — отдельно
> Самостоятельная `ts`-обманка вроде `--lua-desync=fake:tcp_ts=-600000` имеет смысл только при включённых TCP timestamps (Linux — да, Windows — после `netsh`). Чтобы она не работала вхолостую, повесь её на условие `cond_tcp_has_ts` (см. [[оркестратор]]) или держи запасной вариант на `md5sig`/TTL.
---
## Частые ошибки
> [!failure] "В fake можно просто добавить timestamp option"
> Нет. Timestamp option согласуется в начале TCP-сеанса. Если сеанс без timestamps, добавленная руками option не делает его timestamp-сеансом — и сервер не отбросит fake по PAWS.
> [!failure] "Если в логах нет warning, значит всё хорошо"
> Нет. В zapret1 runtime может молчать. В zapret2 сообщение про отсутствие timestamp идёт через `DLOG`, то есть видно только с `--debug`.
> [!failure] "blockcheck показал `ts`, значит у пользователей Windows всё готово"
> Нет. blockcheck на Windows сам пытается включить timestamps через `netsh`.
> [!failure] "`tcp_ts_up` включит timestamps"
> Нет. `tcp_ts_up` только переставляет **уже существующую** timestamp option в начало списка TCP options.
> [!failure] "`ts` самый безопасный fooling"
> Он аккуратный, когда timestamps есть. Если timestamps нет — fake перестаёт быть плохим для сервера и может его сломать.
---
## Мини-шпаргалка
```text
zapret1:
--dpi-desync-fooling=ts
--dpi-desync-ts-increment=-600000 (диапазон ~ -100 .. -0x80000000)
zapret2:
--lua-desync=fake:tcp_ts=-600000 (значение обязательно)
--lua-desync=fakedsplit:tcp_ack=-66000:tcp_ts_up (= badseq increment=0)
Проверить Windows:
netsh interface tcp show global
Включить Windows timestamps:
netsh interface tcp set global timestamps=enabled
zapret2 debug-признак, что ts не применился:
apply_fooling: timestamp tcp option not present or invalid
Гейт "только если есть timestamp" (zapret2):
cond_tcp_has_ts (в мануале: cond_tcp_ts)
```
---
## Источники в коде
> [!note] Проверено по локальным исходникам
> Заметка не основана на догадках про DPI. Основные утверждения сверены с локальными исходниками zapret1 и zapret2.
**zapret1:**
- `docs/readme.md` — `ts` прибавляет к `TSval` инкремент (дефолт `-600000`, рабочий диапазон `-100 .. -0x80000000`); сервер отбрасывает пакеты с `TSval` в определённых пределах; Windows timestamps выключены по умолчанию (`netsh ... timestamps=enabled`); там же слабые места `md5sig`, `badsum`, `badseq` (дефолт `-10000`), TTL, `datanoack` (NAT/masquerade).
- `nfq/darkmagic.c` — `tcp_find_timestamps()` ищет TCP option kind `8`; `fill_tcphdr()` добавляет timestamp option только если `timestamps != NULL`; при `FOOL_TS` меняется `TSval`, `TSecr` копируется без изменения.
- `blockcheck.sh` — на Windows включает timestamps через `netsh`; после успешного `ts` печатает warning про Windows.
**zapret2:**
- `lua/zapret-lib.lua` — `tcp_ts=N` ищет существующий `TCP_KIND_TS` (длина данных 8 байт); если option нет, пишет `apply_fooling: timestamp tcp option not present or invalid`; `tcp_ts_up` только двигает существующую option в начало; `tcp_md5` по умолчанию добавляет 16 случайных байт.
- `lua/zapret-antidpi.lua` — в `fakedsplit`/`fakeddisorder`/`hostfakesplit` к оригинальным сегментам применяется только `tcp_ts_up` (+ `ip_id`), остальной fooling — на fake.
- `lua/zapret-auto.lua` — `cond_tcp_has_ts(desync)` возвращает true, если диссект TCP и присутствует timestamp option.
- `docs/readme.md` — связка `tcp_ack=-66000:tcp_ts_up` — эквивалент zapret1 `--dpi-desync-fooling=badseq --dpi-desync-badseq-increment=0`; объяснение, почему Linux дропает плохой ack только при timestamp-опции сверху.
- `docs/manual.md` — `tcp_ts` работает только при наличии timestamp option; `--debug` включает `DLOG`; conditional описан как `cond_tcp_ts`.
- `blockcheck2.sh` — на Windows тоже включает TCP timestamps через `netsh`; стандартный тест использует `tcp_ts=-1000`.
- `nfq2/darkmagic.h` — числовые kind TCP options (`TCP_KIND_TS=8`, `TCP_KIND_MD5=19`, `TCP_KIND_SCALE=3`, `TCP_KIND_MSS=2`, `TCP_KIND_SACK_PERM=4`, `TCP_KIND_NOOP=1`, `TCP_KIND_END=0`) — совпадают с IANA/RFC.
**Стандарты:**
- [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) (обновляет [RFC 1323](https://datatracker.ietf.org/doc/html/rfc1323)) — формат TSopt (Kind 8, Length 10, `TSval`/`TSecr` по 4 байта), правило согласования (TSopt в `SYN` и `SYN,ACK`) и механизм PAWS (отбрасывание сегмента при `SEG.TSval < TS.Recent`).
- [IANA TCP Option Kind Numbers](https://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-parameters-1) — официальный реестр номеров kind; значения в `darkmagic.h` совпадают с ним.
---
## 📚 См. также
- [[desync]] — общий справочник по `--lua-desync` и полный список fooling-параметров (`tcp_ts`, `tcp_md5`, `tcp_seq`, `ip_ttl`…)
- [[fake]] — что такое fake-пакет и как он отравляет состояние DPI
- [[fakedsplit]], [[fakeddisorder]] — стратегии, где fake совмещён с сегментацией (там и применяется `tcp_ack`/`tcp_ts_up`)
- [[оркестратор]] — условная логика, через которую вешается `cond_tcp_has_ts`
- [[multisplit]] — разбор `seqovl` (скрытый фейк внутри сегмента) и сегментации
- [[основные флаги]] — где fooling стоит в общей картине аргументов
- 🔗 [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) — TCP Timestamps, согласование и PAWS (обновляет RFC 1323)
- 🔗 [Официальный manual zapret2](https://github.com/bol-van/zapret2/blob/master/docs/manual.md) — первоисточник по параметрам
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны на GitHub: [исходник этой заметки](https://github.com/youtubediscord/todo/blob/main/Zapret2/ts-and-fooling.md) · [весь репозиторий](https://github.com/youtubediscord/todo/tree/main).