Some checks failed
Published content check / validate (push) Failing after 3s
Обновить правила репозитория и подписи исходников в 99 заметках, не затрагивая пользовательские незакоммиченные файлы. Сделать Forgejo Actions содержательным: проверять опубликованный commit, а не пустое рабочее дерево после checkout.
210 lines
15 KiB
Markdown
210 lines
15 KiB
Markdown
---
|
||
date: 2026-07-17
|
||
tags:
|
||
- zapret
|
||
- zapret2
|
||
- nfqws2
|
||
- payload
|
||
- filter
|
||
- l7
|
||
- protocol-detection
|
||
link: https://github.com/bol-van/zapret2/blob/master/docs/manual.md
|
||
aliases:
|
||
- Тип payload
|
||
- --payload
|
||
- l7payload
|
||
- Распознавание протоколов
|
||
- Типы пейлоада
|
||
---
|
||
# 🧩 Тип payload и распознавание протоколов
|
||
|
||
> [!info] О чём заметка
|
||
> Что такое «тип payload» в [[Zapret2/Zapret2|Zapret 2]], чем он отличается от протокола потока, как `nfqws2` распознаёт протоколы по сигнатурам, какие типы бывают и как работает фильтр `--payload`. Также — как объявить **свой** детектор протокола на Lua. Фильтр `--payload` — одно из полей [[profile|профиля]] рядом с [[filter|`--filter-*`]]; где поля `l7payload`/`l7proto` лежат в данных пакета — см. [[структура desync и диссекта]].
|
||
|
||
## TL;DR
|
||
|
||
- **Тип payload** (`l7payload`) — это распознанное содержимое **одного пакета**: `tls_client_hello`, `http_req`, `quic_initial` и т. д. Пустой пакет — `empty`, нераспознанный — `unknown`.
|
||
- **Протокол потока** (`l7proto`) — ярлык на **всё соединение**; ставится по первому известному пейлоаду и держится до конца, даже если дальше идут `unknown`-пакеты.
|
||
- Фильтр **`--payload`** ограничивает, к каким пакетам применится последующий [[desync|`--lua-desync`]]. По умолчанию `known` (всё, кроме `empty` и `unknown`).
|
||
- Спецзначения фильтра: **`all`** — любой пейлоад, **`known`** — не `empty` и не `unknown`. Префикс **`~`** — инверсия.
|
||
- Распознавание **сигнатурное** и живёт в C-ядре. Свой тип можно назначить Lua-детектором (`detect_payload_str`), но такой тип **не виден** C-фильтру `--payload` — только payload-фильтрам самих desync-функций.
|
||
|
||
---
|
||
|
||
## Что такое тип payload (и чем он отличается от протокола потока)
|
||
|
||
Это два разных понятия, и их постоянно путают.
|
||
|
||
**Тип payload** (в данных пакета — поле `desync.l7payload`) — это распознанный тип содержимого **одного конкретного пакета**. Например, в TLS-соединении первый пакет от клиента имеет тип `tls_client_hello`, ответ сервера — `tls_server_hello`, а дальше идут уже зашифрованные пакеты, которые распознать нельзя, — их тип `unknown`.
|
||
|
||
**Протокол потока** (поле `desync.l7proto`, а в фильтрах профиля — [[filter|`--filter-l7`]]) — это ярлык на **всё соединение целиком**. Он присваивается, как только в потоке встретился первый известный пейлоад, и остаётся с потоком **до конца его существования** — даже если последующие пакеты имеют тип `unknown`.
|
||
|
||
> [!note] Проще говоря
|
||
> Протокол потока — как табличка на двери всего «разговора»: повесили один раз («это TLS») и не меняем. Тип payload — как ярлык на каждой отдельной реплике внутри разговора: у первой реплики `tls_client_hello`, у следующих может быть `unknown`. Одно соединение (`tls`) → много пакетов с разными типами payload.
|
||
|
||
Два особых типа payload есть всегда:
|
||
|
||
- **`empty`** — пакет без прикладных данных (например, «пустой» ACK).
|
||
- **`unknown`** — данные есть, но их тип не распознан (зашифрованная середина соединения, неизвестный протокол).
|
||
|
||
Полное описание, где именно поля `l7payload` и `l7proto` лежат в таблице пакета, — в [[структура desync и диссекта]].
|
||
|
||
---
|
||
|
||
## Как `nfqws2` распознаёт протоколы
|
||
|
||
Распознавание **сигнатурное**: C-ядро `nfqws2` смотрит на характерные байты в начале пейлоада (или группы пакетов) и по ним определяет тип. Это происходит на быстрой стороне C-кода ещё до вызова Lua (см. [[схема обработки трафика]]).
|
||
|
||
В фильтрах по типу payload и по протоколу потока доступны два спецзначения:
|
||
|
||
- **`all`** — совпадает с любым пейлоадом (в том числе `empty` и `unknown`);
|
||
- **`known`** — совпадает со всем, что **не** `empty` и **не** `unknown` (то есть только с реально распознанными типами).
|
||
|
||
### Таблица распознаваемых типов
|
||
|
||
Ниже — какие протоколы потока `nfqws2` умеет распознавать, на каком транспорте (L4) они работают и какие типы payload внутри них бывают.
|
||
|
||
| Протокол потока | L4 | Типы payload внутри |
|
||
|:----------------|:---|:--------------------|
|
||
| `http` | TCP | `http_req`, `http_reply` |
|
||
| `tls` | TCP | `tls_client_hello`, `tls_server_hello` |
|
||
| `xmpp` | TCP | `xmpp_stream`, `xmpp_starttls`, `xmpp_proceed`, `xmpp_features` |
|
||
| `mtproto` | TCP | `mtproto_initial` |
|
||
| `bt` | TCP | `bt_handshake` |
|
||
| `quic` | UDP | `quic_initial` |
|
||
| `wireguard` | UDP | `wireguard_initiation`, `wireguard_response`, `wireguard_cookie`, `wireguard_keepalive` |
|
||
| `dht` | UDP | `dht` |
|
||
| `utp_bt` | UDP | `utp_bt_handshake` |
|
||
| `discord` | UDP | `discord_ip_discovery` |
|
||
| `stun` | UDP | `stun` |
|
||
| `dns` | UDP | `dns_query`, `dns_response` |
|
||
| `dtls` | UDP | `dtls_client_hello`, `dtls_server_hello` |
|
||
| *(любой)* | ICMP | `ipv4`, `ipv6`, `icmp` |
|
||
|
||
> [!note] Особые типы `ipv4` / `ipv6` / `icmp`
|
||
> Это типы payload для ICMP-пакетов. `ipv4` и `ipv6` генерируются, когда ICMP несёт **прикреплённый исходный пакет** (например, ICMP «destination unreachable» с куском исходного соединения). Во всех остальных случаях ICMP получает тип payload `icmp`. Подробнее про обработку ICMP — [[структура desync и диссекта]].
|
||
|
||
---
|
||
|
||
## Фильтр `--payload`
|
||
|
||
`--payload` — поле [[profile|профиля]], которое ограничивает, к каким пакетам применятся **последующие** `--lua-desync`-инстансы. Пока `--payload` не переопределён, он действует на все идущие за ним инстансы (как и другие внутрипрофильные фильтры, см. [[схема обработки трафика]]).
|
||
|
||
### Синтаксис
|
||
|
||
```bash
|
||
# Один тип
|
||
--payload=tls_client_hello
|
||
|
||
# Несколько типов через запятую
|
||
--payload=http_req,http_reply
|
||
|
||
# Все известные типы (не empty и не unknown) — это же значение по умолчанию
|
||
--payload=known
|
||
|
||
# Вообще все пакеты, включая empty и unknown
|
||
--payload=all
|
||
|
||
# Инверсия: всё, КРОМЕ указанного
|
||
--payload=~empty
|
||
```
|
||
|
||
### Значение по умолчанию — `known`
|
||
|
||
Если `--payload` не указан, используется `known`. Вот реальный код фильтра (`lua/zapret-lib.lua`):
|
||
|
||
```lua
|
||
function payload_match_filter(l7payload, l7payload_filter, def)
|
||
local argpl = l7payload_filter or def or "known"
|
||
local neg = string.sub(argpl,1,1)=="~"
|
||
local pl = neg and string.sub(argpl,2) or argpl
|
||
return neg ~= (in_list(pl, "all") or in_list(pl, l7payload) or in_list(pl, "known") and l7payload~="unknown" and l7payload~="empty")
|
||
end
|
||
```
|
||
|
||
По умолчанию (`known`) фильтр:
|
||
|
||
- ✅ пропускает любой распознанный payload (`tls_client_hello`, `mtproto_initial`, `http_req`, …);
|
||
- ❌ **не** пропускает `unknown`;
|
||
- ❌ **не** пропускает `empty`.
|
||
|
||
Инверсия через `~` означает «всё, кроме»: `--payload=~empty` — любой непустой пакет, `--payload=~unknown` — всё, кроме нераспознанного.
|
||
|
||
> [!important] `--payload` (C-уровень) vs payload-фильтр инстанса (Lua)
|
||
> `--payload` на уровне профиля обрабатывается быстрым C-кодом. У многих desync-функций есть **свой** аргумент `payload=` — это дополнительный фильтр уже на стороне Lua. Практика: держите основной фильтр на `--payload` профиля (быстрее), а `payload=` инстанса используйте для тонкой донастройки. Разница станет важной ниже, в разделе про кастомные детекторы.
|
||
|
||
---
|
||
|
||
## Свой детектор протокола на Lua (`detect_payload_str`)
|
||
|
||
Иногда нужного протокола нет в списке распознаваемых, но у его пакетов есть характерная подстрока. Тогда тип payload можно назначить самому — простейшим Lua-детектором. В комплекте есть пример-функция `detect_payload_str`:
|
||
|
||
```lua
|
||
function detect_payload_str(ctx, desync)
|
||
-- arg: pattern — подстрока для поиска в desync.dis.payload
|
||
-- arg: payload — какое значение присвоить desync.l7payload, если подстрока найдена
|
||
-- arg: undetected — какое значение присвоить, если подстрока НЕ найдена (необязательно)
|
||
end
|
||
```
|
||
|
||
Логика простая: функция ищет подстроку `pattern` в пейлоаде текущего пакета (`desync.dis.payload`); если находит — присваивает `desync.l7payload = desync.arg.payload`; иначе, если задан `undetected`, присваивает `desync.l7payload = desync.arg.undetected`.
|
||
|
||
> [!warning] Кастомный тип не виден C-коду и фильтру `--payload`
|
||
> Такие Lua-детекторы **не имеют силы для C-кода**: ядро ничего не знает о вашем протоколе и вашем типе payload. Поэтому ваше значение **нельзя** указать в `--payload` (это C-уровневый фильтр). Но его **можно** использовать в payload-фильтрах (`payload=`) многих desync-функций — они работают уже на стороне Lua, после того как ваш детектор отработал. Схема такая: сначала инстанс `detect_payload_str` проставляет `l7payload`, а следующий инстанс с `payload=ваш_тип` на него реагирует.
|
||
|
||
---
|
||
|
||
## Практика: MTProto (Telegram)
|
||
|
||
Хороший пример, где важна разница «пакет vs поток». В соединении MTProto распознаётся только первый пакет:
|
||
|
||
| Пакет | `l7payload` | Пройдёт фильтр `known`? |
|
||
|:------|:------------|:------------------------|
|
||
| Первый пакет с данными | `mtproto_initial` | ✅ Да |
|
||
| Последующие пакеты | `unknown` | ❌ Нет |
|
||
|
||
Нужно ли добавлять `payload=unknown`? Обычно **нет**. Для обхода блокировки Telegram, как правило, достаточно обработать только `mtproto_initial`, потому что:
|
||
|
||
1. DPI анализирует только начало соединения (сигнатуру MTProto);
|
||
2. после initial данные зашифрованы — DPI их не парсит;
|
||
3. если initial прошёл — соединение установлено.
|
||
|
||
```bash
|
||
# Рекомендуемый вариант — обрабатываем только initial
|
||
nfqws2 --filter-l7=mtproto \
|
||
--payload=mtproto_initial \
|
||
--lua-desync=fake:blob=0x00000000:repeats=3
|
||
```
|
||
|
||
Если же нужны **все** пакеты MTProto (initial + последующие), укажите `unknown` явно или используйте `all`:
|
||
|
||
```bash
|
||
# Явно добавить unknown-пакеты отдельным фильтром
|
||
nfqws2 --filter-l7=mtproto \
|
||
--payload=mtproto_initial --lua-desync=fake:blob=... \
|
||
--payload=unknown --lua-desync=send:ip_ttl=3
|
||
|
||
# Или разом все пакеты
|
||
nfqws2 --filter-l7=mtproto \
|
||
--payload=all \
|
||
--lua-desync=fake:blob=...
|
||
```
|
||
|
||
Детальный разбор распознавания Telegram — в [[распознавание mtproto]].
|
||
|
||
---
|
||
|
||
## 📚 См. также
|
||
|
||
- [[filter|Фильтры профиля]] — `--filter-l7` (протокол потока) и другие фильтры рядом с `--payload`
|
||
- [[desync|Механизм --lua-desync]] — что за инстансы ограничивает `--payload`
|
||
- [[структура desync и диссекта|Структура desync]] — где лежат поля `l7payload`/`l7proto` и как приходит собранный многопакетный пейлоад
|
||
- [[схема обработки трафика|Схема обработки трафика]] — на какой стадии определяется тип payload и протокол потока
|
||
- [[распознавание mtproto|Распознавание MTProto]] — разбор на примере Telegram
|
||
- [[blob|Blob-данные]] — заготовки для `blob=…`, часто применяемые вместе с payload-фильтром
|
||
- 🔗 [Официальная документация (docs/manual.md)](https://github.com/bol-van/zapret2/blob/master/docs/manual.md) — первоисточник
|
||
|
||
---
|
||
|
||
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
|
||
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: [исходник этой заметки](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/payload.md) · [весь репозиторий](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main).
|