Zapret2: start/cutoff и почему n2<n3 “вдруг” попадает на ClientHello
Эта заметка — ответ на типичную ситуацию:
--filter-tcp=443 --payload=all --out-range="n1<n2" --lua-desync=send:ip_ttl=1→ дюпается SYN
--filter-tcp=443 --payload=all --out-range="n2<n3" --lua-desync=send:ip_ttl=1→ дюпается ClientHello, хотя “по идее должен ACK”
1) Ключевой факт: n — это номер ПЕРЕХВАЧЕННОГО пакета, а не “всех пакетов TCP”
--out-range работает по счётчикам conntrack внутри движка.
Счётчик n увеличивается только на те исходящие пакеты, которые:
- реально перехвачены (NFQUEUE/WinDivert), и
- реально дошли до обработки профилем (фильтры не выкинули их раньше).
Если какой-то пакет “не попал” — он не существует для счётчика n.
2) Почему именно ACK часто “пропадает”
Windows / winws2
В winws2 по умолчанию часто включено:
--wf-tcp-empty=0
Это не перехватывает пустые TCP ACK (ACK без payload).
Сильный профит по CPU, но n перестаёт совпадать с тем, что вы ожидаете как “номер пакета в соединении”.
Итог: исходящий порядок “в движке” становится таким:
n1= SYN (перехвачен)n2= ClientHello (первый перехваченный packet с данными)
А “реальный” handshake ACK существует, но не перехвачен.
Linux / nfqws2
Если ваши правила NFQUEUE (nft/iptables) перехватывают только “пакеты с данными” или только часть фаз — ситуация аналогичная:
ACK просто не попадёт в userspace, и n сдвинется.
3) Вторая причина (реже): ACK может быть “склеен” с ClientHello
Иногда стек может отправить ACK на SYN-ACK вместе с первым сегментом данных (ACK+payload). Тогда “второй исходящий пакет” реально будет ClientHello (и это нормально). Проверяется только захватом трафика (Wireshark).
4) Эквивалент nfqws1 --dup-start=n2 --dup-cutoff=n3 в zapret2
Если вы хотите строго применить действие к “2-му исходящему пакету” по счётчику n:
--out-range="n2<n3" --lua-desync=send:ip_ttl=1Но это будет работать “как ожидаете” только если этот пакет реально перехвачен.
5) Как применить “дурение” именно ко 2-му исходящему ACK (если он пустой)
Вариант A (Windows / winws2): включить перехват пустых ACK
- включить перехват пустых TCP пакетов:
--wf-tcp-empty=1- сузить по типу payload (очень полезно, чтобы убедиться что вы бьёте именно по пустому):
--payload=empty --out-range="n2<n3" --lua-desync=send:ip_ttl=1Если после этого “ничего не дюпается” — значит либо ACK не перехвачен фильтрами, либо ACK не пустой.
Вариант B: если цель — “первый пакет с данными” (ClientHello), используйте d
Это устойчиво даже если пустые ACK не перехватываются:
--payload=tls_client_hello --out-range="d1<d2" --lua-desync=send:ip_ttl=16) Самый быстрый способ понять, что реально происходит
Добавьте на время дебага:
--lua-desync=posdebug
--lua-desync=luaexec:code="DLOG('flags '..tostring(desync.dis.tcp and desync.dis.tcp.th_flags)..' payload '..#desync.dis.payload..' l7payload '..tostring(desync.l7payload))"И смотрите:
- какая длина payload у “n2”
- какой
l7payload(empty/tls_client_hello/unknown) - увеличивается ли
nна пустых ACK