Some checks failed
Published content check / validate (push) Failing after 3s
Обновить правила репозитория и подписи исходников в 99 заметках, не затрагивая пользовательские незакоммиченные файлы. Сделать Forgejo Actions содержательным: проверять опубликованный commit, а не пустое рабочее дерево после checkout.
571 lines
52 KiB
Markdown
571 lines
52 KiB
Markdown
---
|
||
date: 2026-06-20
|
||
tags:
|
||
- zapret
|
||
- zapret1
|
||
- zapret2
|
||
- winws
|
||
- winws2
|
||
- tcp
|
||
- fooling
|
||
- timestamp
|
||
- tcp-options
|
||
- paws
|
||
- rfc7323
|
||
aliases:
|
||
- ts fooling
|
||
- tcp_ts
|
||
- ts и fooling
|
||
- fooling
|
||
- почему фейк должен умереть
|
||
- как убить фейк
|
||
- TCP options
|
||
- PAWS
|
||
link: https://datatracker.ietf.org/doc/html/rfc7323
|
||
---
|
||
|
||
# 💀 `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 --debug=@ts-debug.log ...
|
||
> ```
|
||
|
||
> [!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-архивом доступны в Forgejo: [исходник этой заметки](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/ts-and-fooling.md) · [весь репозиторий](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main).
|