Обновить правила репозитория и подписи исходников в 99 заметках, не затрагивая пользовательские незакоммиченные файлы. Сделать Forgejo Actions содержательным: проверять опубликованный commit, а не пустое рабочее дерево после checkout.
52 KiB
| date | tags | aliases | link | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-20 |
|
|
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):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 это выглядит так:
--dpi-desync-fooling=ts --dpi-desync-ts-increment=-600000В zapret2 прямого режима
tsнет — он пишется как параметр fooling:--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 уходит сообщение:
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:
--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:
--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-параметры:
--lua-desync=fake:tcp_ts=-600000
или, например, связка с плохим ACK:
--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 не получит порчу и может дойти до сервера живым.
Проверка состояния:
netsh interface tcp show global
Включение:
netsh interface tcp set global timestamps=enabled
[!warning] Три практических нюанса для Windows
- Настройку надо включать на каждом клиентском устройстве — нельзя молча рассчитывать, что у всех пользователей preset timestamps уже включены.
- После включения проверяй новые соединения. Уже открытый TCP-сеанс не станет timestamp-сеансом задним числом.
- 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 печатает предупреждение:
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-сообщение:
apply_fooling: timestamp tcp option not present or invalid
Но это именно debug: без --debug обычный DLOG не виден.
[!example] Debug в zapret2 На консоль:
winws2 --debug ...В файл:
winws2 --debug=@ts-debug.log ...
[!warning] Отсутствие строки не доказывает успех Нет строки про timestamp — это не значит, что
tsсработал. Возможно, debug не включён, пакет не попал в нужный profile или Lua-функция не вызывалась на этом payload.
Как проверять ts правильно
Сначала — настройка Windows:
netsh interface tcp show global
Если timestamps выключены:
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: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)
--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)
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5
То же для HTTP: подставной запрос отравляет DPI, MD5-опция убивает fake на сервере.
3. Сегментация + seqovl, вообще без порчи заголовков (readme.md:109)
--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)
--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)
--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)
--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)
--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 перестаёт быть плохим для сервера и может его сломать.
Мини-шпаргалка
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 kind8;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 (обновляет RFC 1323) — формат TSopt (Kind 8, Length 10,
TSval/TSecrпо 4 байта), правило согласования (TSopt вSYNиSYN,ACK) и механизм PAWS (отбрасывание сегмента приSEG.TSval < TS.Recent). - IANA TCP Option Kind Numbers — официальный реестр номеров 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 — TCP Timestamps, согласование и PAWS (обновляет RFC 1323)
- 🔗 Официальный manual zapret2 — первоисточник по параметрам
[!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: исходник этой заметки · весь репозиторий.