# 💀 `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).