
Decoder/Encoder für Ubiquiti-AirMAX-Funkprotokoll-Frames; pcap-/Live-Captures parsen, Geräte erkennen, nach verwundbarer Firmware scannen, fehlerhafte Pakete erzeugen und AirMAX-AC/M-Ziele emulieren.
Decoder und Encoder für Ubiquiti-AirMAX-Wire-Protokoll-Frames, die sowohl die AC-Serie (WA-Firmware) als auch die M-Serie (XW/XM-Firmware) abdecken.
ksy/. Die AC-Nutzlast
(versionsabhängig, umgeschaltet über msg_type, mit Deauth-XOR-Entmaskierung)
ist manuell in ac.py geschrieben — diese Logik passt nicht in ein
reines Parse-Kaitai.| Variante | Dekodierung | Kodierung | pcap-Durchlauf |
|---|---|---|---|
| AC | ✅ alle 5 msg-Typen (Beacon, Assoc-Req/Resp, Probe-Req, Deauth) | ✅ byteexakte Umkehrung + Builder + seal | ✅ |
| M | teilweise (9 dokumentierte Bytes; Rest als unknown_rest) | ✅ Round-Trip für den dokumentierten Kopf + unknown_rest | ✅ |
| Routerboard.com-IE (Begleiter zu M) | ✅ Gerätename + Sub-IE-Liste | n/a | ✅ |
Das AC-Wire-Format folgt
docs/ac_wire_format.md
(mit [P] gegen ubnt_poll_host.ko bestätigt). Alle AC-Multibyte-Ganzzahlen
sind Big-Endian.
pyrmax.ac.encode(AcPacket) -> bytes — manuell geschriebene
byteexakte Umkehrung von decode() (+ seal(), to_ie() und
build_*-Konstruktoren).pyrmax.m.encode(MPacket) -> bytes — dasselbe für M (+ build_m,
seal, to_ie).[open]-AC-Felder exakt bestimmen — cap_flags-Bitmap,
mixed_mode, field_14/field_9c (assoc_req), sta_field_68/ic_6b8
(assoc_resp) sowie das Deauth-enc_token (extrahierbar, aber sein Schlüssel
ist noch nicht reverse-engineered, daher nicht verifizierbar). Siehe
docs/ac_wire_format.md §11-§12.version < 9-AC-Dekoder anhand echter Captures verifizieren —
der TX-Builder erzeugt immer nur Version 9, daher sind die Pfade der
niedrigeren Versionen (ungetaggter Name, fehlende Tail-Felder) gemäß
Spezifikation implementiert, aber nicht auf der Leitung verifiziert.unknown_rest) — derzeit undurchsichtig.pcap.FrameMeta verfügbar machen: Kanal,
RSSI, Rate. Derzeit sind nur Zeitstempel/MACs/BSSID befüllt.tests/samples/ — derzeit:
airmax_ac_beacon.pcap (1 Frame, Beacon) und
airmax_m_probe_response.pcap (1 Frame, Probe-Response). Weitere Varianten
(Assoc-Req/Resp, Multi-Frame-Captures) sind weiterhin willkommen.scan (aktives Force-Assoc) und
emulate injizieren über Scapy (die [scan]-/[emulate]-Extras). Siehe
scan.py / emulate.py.Das Paket enthält eine CLI mit Unterbefehlen. Führe sie wie folgt aus:
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` und `discover` akzeptieren **entweder** eine pcap/pcapng-Datei (als Positionsargument)
**oder** eine Live-Wireless-Schnittstelle über `-i / --iface IFACE`. Diese beiden
schließen sich gegenseitig aus. `scan` akzeptiert dieselben Quelloptionen (plus einen
`--active`-Modus, der nur auf eine Live-Schnittstelle anwendbar ist); `emulate` ist
ausschließlich für Live-Schnittstellen.```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
Live-Erfassung setzt voraus, dass sich die Schnittstelle bereits im Monitor-Modus auf dem gewünschten Kanal befindet – pyrmax konfiguriert keines von beiden. Es erfordert das optionale [live]-Extra (pip install pyrmax[live]), das pcapy-ng nachzieht. Drücken Sie Strg-C zum Beenden: parse meldet, wie viele Frames gestreamt wurden; discover gibt beim Beenden die aggregierte Geräteübersicht aus.
Exit-Codes (gelten für beide Befehle und beide Quellenmodi): 0 bei Erfolg (einschließlich „keine AirMAX-Frames“ – ein gültiges Ergebnis), 1 bei Fehlern im Erfassungsformat (falscher Link-Typ, beschädigte Datei, Schnittstelle kann nicht geöffnet werden), 2 bei fehlender Datei oder ungültigen Quellenargumenten.
parse — Dump pro Frame```shpython -m pyrmax parse capture.pcap
Jeder 802.11-Management-Frame, der eine AirMAX-Vendor-IE trägt, erzeugt einen
Block: AC-Paket, M-Paket und jede Routerboard.com-Begleit-IE, die
im selben Frame gefunden wird.```
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
The same single-pass walker is exposed programmatically as
pyrmax.pcap.iter_airmax(path) — it yields one
AirmaxRecord(meta, ac, m, routerboard) per AirMAX-bearing frame, so
you don't need to correlate M and Routerboard IEs by hand.
discover — Geräteübersicht```shpython -m pyrmax discover capture.pcap
Frames werden nach 802.11-Quell-MAC gruppiert, Peers werden akkumuliert, und AC-Payload- / M-Payload- / Routerboard-Gerätename-Beobachtungen werden zu einem einzigen Block pro Gerät zusammengefasst.```
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
Dieselbe Aggregation ist auch eine öffentliche Funktion:```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)
## Verwendung
### Eine einzelne AirMAX-AC-Nachricht dekodieren
Der Dekoder erwartet die Bytes der 802.11-Vendor-Specific-IE **ab der OUI** –
der IE-Wrapper (Element-ID `0xDD` + Länge) muss bereits entfernt worden sein.
`src_mac` / `dst_mac` werden aus der SA / DA des äußeren 802.11-Frames übernommen.```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
)