
Декодер/кодировщик кадров беспроводного протокола Ubiquiti AirMAX; разбор pcap-файлов и живых захватов, обнаружение устройств, сканирование на уязвимые прошивки, формирование повреждённых пакетов и эмуляция целей AirMAX AC/M.
Декодер и энкодер для кадров wire-протокола Ubiquiti AirMAX, покрывающий как серию AC (прошивка WA), так и серию M (прошивки XW/XM).
ksy/. Полезная нагрузка AC payload
(зависящая от версии, переключается по msg_type, с XOR-снятием маски для deauth)
написана вручную в ac.py — эта логика не помещается в parse-only Kaitai.| Вариант | Декодирование | Кодирование | Итерация по pcap |
|---|---|---|---|
| AC | ✅ все 5 типов сообщений (beacon, assoc req/resp, probe req, deauth) | ✅ байт-в-байт обратная операция + билдеры + seal | ✅ |
| M | частично (9 документированных байт; остальное как unknown_rest) | ✅ round-trip документированной головы + unknown_rest | ✅ |
| IE Routerboard.com (компаньон для M) | ✅ имя устройства + список sub-IE | n/a | ✅ |
Формат wire AC соответствует
docs/ac_wire_format.md
([P]-подтверждено против ubnt_poll_host.ko). Все многобайтовые целые AC —
big-endian.
pyrmax.ac.encode(AcPacket) -> bytes — написанная вручную байт-в-байт
обратная операция к decode() (+ seal(), to_ie() и конструкторы build_*).pyrmax.m.encode(MPacket) -> bytes — то же для M (+ build_m,
seal, to_ie).[open] — точную битовую карту cap_flags,
mixed_mode, field_14/field_9c (assoc_req), sta_field_68/ic_6b8
(assoc_resp), а также deauth enc_token (извлекается, но его ключ ещё
не ревёрс-инженерен, поэтому проверить невозможно). См.
docs/ac_wire_format.md §11-§12.version < 9 на реальных захватах —
билдер TX всегда выдаёт только версию 9, поэтому пути для младших версий
(нетегированное имя, отсутствующие хвостовые поля) реализованы по спецификации,
но не проверены на проводе.unknown_rest) — пока непрозрачно.pcap.FrameMeta: канал, RSSI,
скорость. Сейчас заполняются только метка времени/MAC/BSSID.tests/samples/ — сейчас:
airmax_ac_beacon.pcap (1 кадр, beacon) и
airmax_m_probe_response.pcap (1 кадр, probe response). Другие варианты
(assoc req/resp, многофреймовые захваты) по-прежнему приветствуются.scan (активная force-assoc) и emulate
внедряются через scapy (экстры [scan] / [emulate]). См.
scan.py / emulate.py.Пакет поставляется с CLI, управляемым через подкоманды. Запуск:
python -m pyrmax COMMAND ....```
usage: python -m pyrmax [-h] [--version] COMMAND ...
COMMAND parse Print each AirMAX frame in detail. discover Summarize devices observed in the capture. scan Live-scan for AirMAX devices and flag vulnerable firmware. emulate Emulate AirMAX AC/M devices (fake targets for scanners).
`parse` и `discover` принимают **либо** файл pcap/pcapng (позиционный аргумент)
**либо** живой беспроводной интерфейс через `-i / --iface IFACE`. Эти два
варианта взаимоисключающие. `scan` принимает те же опции источника (плюс
режим `--active`, который применяется только к живому интерфейсу); `emulate`
работает только с живым интерфейсом.```sh
python -m pyrmax parse capture.pcap # offline
python -m pyrmax discover capture.pcap
sudo python -m pyrmax parse -i wlan0mon # live (needs root)
sudo python -m pyrmax discover -i wlan0mon
Захват в реальном времени предполагает, что интерфейс уже находится в режиме монитора на
нужном канале — pyrmax не настраивает ни то, ни другое. Для этого требуется
дополнительная опция [live] (pip install pyrmax[live]), которая подтягивает
pcapy-ng. Нажмите Ctrl-C для остановки: parse сообщает, сколько кадров он
получил; discover выводит агрегированную сводку устройств при выходе.
Коды выхода (общие для обеих команд и обоих режимов источников): 0 при
успехе (включая «нет кадров AirMAX» — допустимый результат), 1 для
ошибок формата захвата (неверный тип канального уровня, повреждённый файл, невозможно
открыть интерфейс), 2 для отсутствующего файла или недопустимых аргументов источника.
parse — покадровый дамп```shpython -m pyrmax parse capture.pcap
Каждый управляющий кадр 802.11, содержащий вендорный IE AirMAX, формирует один
блок: пакет AC, пакет M и любой сопутствующий IE Routerboard.com, найденный
в том же кадре.```
Found 1 AirMAX frame(s) in capture.pcap: 0 AC, 1 M (1 with Routerboard companion).
=== Frame #0 [M] ts=1765494974.955358 ===
802.11 src=04:18:d6:0e:0c:42 dst=24:a4:3c:88:d8:22 bssid=04:18:d6:0e:0c:42
AirMAX M
version 15
msg_type BEACON (raw=1)
src_mac 04:18:d6:0e:0c:42
enable 1
unknown_rest b700000000000000000000040418d60e0c420000000000 (23B)
Routerboard.com IE
oui_type 0
unknown 0000
device_name 'AP Sur HY1315'
sub_ie subtype=1 (30B) 040000001f660902ff0f4150205375722048593133313500000000000000
Тот же однопроходный обходчик предоставляется программно как
pyrmax.pcap.iter_airmax(path) — он возвращает по одному
AirmaxRecord(meta, ac, m, routerboard) для каждого кадра с AirMAX, так что
вам не нужно сопоставлять IE M и Routerboard вручную.
discover — сводка по устройствам```shpython -m pyrmax discover capture.pcap
Кадры группируются по MAC-адресу источника 802.11, пиры накапливаются, а
наблюдения AC payload / M payload / имени устройства Routerboard сводятся в
единый блок для каждого устройства.```
2 device(s) observed in capture.pcap across 12 AirMAX frame(s).
24:5a:4c:44:57:fd (AC)
radioname 'LB1'
ssid 'labalUBI2'
ac_msg_types BEACON
ac_version 9
cap_flags 0x0000003e
mixed_mode 0
frames 8
first seen 1767046123.708745
last seen 1767046129.012448
peers (broadcast only)
04:18:d6:0e:0c:42 (M)
device_name 'AP Sur HY1315'
msg_types BEACON
m_version 15
m_enable 1
frames 4
first seen 1765494974.955358
last seen 1765494980.341110
peers 24:a4:3c:88:d8:22
Та же агрегация также является публичной функцией:```python from pyrmax.devices import summarize from pyrmax.pcap import iter_airmax
devices = summarize(iter_airmax("capture.pcap")) for mac, dev in devices.items(): print(mac.hex(":"), dev.device_name or dev.radioname, dev.peers)
## Usage
### Decode a single AirMAX AC message
The decoder expects the bytes of the 802.11 Vendor Specific IE **starting at
the OUI** — the IE wrapper (Element ID `0xDD` + Length) must already be
stripped. `src_mac` / `dst_mac` are lifted from the outer 802.11 frame's
SA / DA.
## Использование
### Декодирование одного сообщения AirMAX AC
Декодер ожидает байты Vendor Specific IE стандарта 802.11, **начиная
с OUI** — обёртка IE (Element ID `0xDD` + Length) уже должна быть
удалена. `src_mac` / `dst_mac` берутся из SA / DA внешнего кадра 802.11.```python
from pyrmax import ac
packet = ac.decode(
data, # bytes starting at b"\x00\x27\x22"
src_mac="aa:bb:cc:dd:ee:ff", # accepts str ("aa:bb:..." or "aa-bb-..."
# or "aabbcc...") and raw 6-byte bytes
dst_mac="ff:ff:ff:ff:ff:ff", # optional — defaults to broadcast
)