Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
pyrmax — Decoder/encoder per frame del protocollo wireless Ubiquiti AirMAX; analizzare catture pcap/live, scoprire dispositivi, cercare firmware vulnerabili, creare pacchetti malformati ed emulare target AirMAX AC/M. | Kitploit
Strumenti/GitHubGitHub/infobyte/pyrmax
Scanner di VulnerabilitàReverse EngineeringFuzzingSicurezza di ReteSicurezza Wireless
GitHubinfobyte/pyrmax

pyrmax

Decoder/encoder per frame del protocollo wireless Ubiquiti AirMAX; analizzare catture pcap/live, scoprire dispositivi, cercare firmware vulnerabili, creare pacchetti malformati ed emulare target AirMAX AC/M.

Vedi Repository
313 giorni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

pyrmax

Decoder ed encoder per i frame wire-protocol Ubiquiti AirMAX, che coprono sia la serie AC (firmware WA) che la serie M (firmware XW/XM).

  • Decodifica degli header esterni in chiaro è guidata da Kaitai Struct schemi in ksy/. Il payload AC (condizionato dalla versione, selezionato in base a msg_type, con l'unmask XOR del deauth) è scritto a mano in ac.py — quella logica non si adatta a un Kaitai di solo parsing.
  • Codifica è scritta a mano per rispecchiare il percorso di decodifica campo per campo.

Stato

VarianteDecodificaCodificaiterazione su pcap
AC✅ tutti e 5 i tipi di msg (beacon, assoc req/resp, probe req, deauth)✅ inverso byte-esatto + builder + seal✅
Mparziale (9 byte documentati; il resto come unknown_rest)✅ fa il round-trip della testa documentata + unknown_rest✅
Routerboard.com IE (complementare a M)✅ nome dispositivo + elenco sub-IEn/a✅

AC wire format segue docs/ac_wire_format.md (confermato [P] contro ubnt_poll_host.ko). Tutti gli interi multi-byte AC sono big-endian.

TODO

  • pyrmax.ac.encode(AcPacket) -> bytes — inverso byte-esatto scritto a mano di decode() (+ seal(), to_ie() e costruttori build_*).
  • pyrmax.m.encode(MPacket) -> bytes — lo stesso per M (+ build_m, seal, to_ie).
  • Definire con precisione i campi AC [open] — mappa di bit esatta di cap_flags, mixed_mode, / (assoc_req), / (assoc_resp) e l' del deauth (estraibile ma la sua chiave non è ancora stata ricavata tramite reverse engineering, quindi non può essere verificato). Vedi §11-§12.

Strumento a riga di comando

Il pacchetto include una CLI basata su sottocomandi. Eseguilo come 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).

root@kitploit:~
`parse` e `discover` accettano **o** un file pcap/pcapng (posizionale) **o** un'interfaccia wireless attiva tramite `-i / --iface IFACE`. Le due modalità si escludono a vicenda. `scan` accetta le stesse opzioni di origine (più una modalità `--active` che si applica solo a un'interfaccia attiva); `emulate` funziona solo su interfaccia attiva.```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

La cattura live presuppone che l'interfaccia sia già in modalità monitor sul canale di interesse — pyrmax non configura nessuno dei due. Richiede l'extra opzionale [live] (pip install pyrmax[live]) che include pcapy-ng. Premere Ctrl-C per fermarsi: parse riporta quanti frame ha elaborato; discover stampa il riepilogo aggregato dei dispositivi al termine.

Codici di uscita (condivisi da entrambi i comandi e da entrambe le modalità di sorgente): 0 in caso di successo (incluso "no AirMAX frames" — un risultato valido), 1 per errori di formato della cattura (tipo di link errato, file malformato, impossibilità di aprire l'interfaccia), 2 per file mancante o argomenti di sorgente non validi.

parse — dump per frame```sh

python -m pyrmax parse capture.pcap

root@kitploit:~
Ogni frame di gestione 802.11 che trasporta un vendor IE AirMAX produce un
blocco: pacchetto AC, pacchetto M e qualsiasi IE companion Routerboard.com trovato
nello stesso frame.```
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

Lo stesso walker a passaggio singolo è esposto programmaticamente come pyrmax.pcap.iter_airmax(path) — produce un AirmaxRecord(meta, ac, m, routerboard) per ogni frame che contiene AirMAX, così non devi correlare manualmente gli IE M e Routerboard.

discover — riepilogo dispositivo```sh

python -m pyrmax discover capture.pcap

root@kitploit:~
I frame sono raggruppati per MAC sorgente 802.11, i peer accumulati e
le osservazioni AC payload / M payload / nome dispositivo Routerboard confluiscono in
un singolo blocco per dispositivo.```
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

La stessa aggregazione è anche una funzione pubblica:```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)

root@kitploit:~
## Utilizzo

### Decodificare un singolo messaggio AirMAX AC

Il decoder si aspetta i byte dell'IE 802.11 Vendor Specific **a partire dall'OUI** — il wrapper dell'IE (Element ID `0xDD` + Length) deve essere già stato rimosso. `src_mac` / `dst_mac` sono ricavati dai campi SA / DA del frame 802.11 esterno.```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
)

packet.msg_type          # <MsgType.BEACON: 1>
packet.version           # 9 (the wire-format epoch / version gate)
packet.src_mac           # b'\xaa\xbb\xcc\xdd\xee\xff'  (integrity-checked)
packet.radioname         # "lab-rx-1"   (convenience prop, delegates to body)
packet.ssid              # "NetA"
packet.cap_flags         # 0x3e  (None for msg types that have no cap_flags)

# The per-message-type fields live on packet.body, one of:
#   BeaconBody | AssocReqBody | AssocRespBody | ProbeReqBody | DeauthBody
body = packet.body
if isinstance(body, ac.BeaconBody):
    body.mac_0c          # the radio's own MAC / BSSID
    body.cap_flags       # u32 capability bitfield (§11)
    body.mixed_mode      # u32 [open]

msg_type è uno tra BEACON / ASSOC_REQ / ASSOC_RESP / PROBE_REQ / DEAUTH. I campi di coda controllati dalla versione (field_9c, rssi, fwname, txpower, …) sono None quando la version del frame è inferiore alla loro soglia. Il deauth esegue l'un-XOR di src_mac con il nonce jiffies prima del controllo di integrità e espone il enc_token di 16 byte come byte opachi (la sua chiave non è ancora invertita, quindi non può essere verificato).

Scorri la lista TLV AC

I body che portano TLV di nome (beacon, assoc_req) preservano lo stream — inclusa la voce Padding finale — così com'è su body.tlvs. radioname, ssid e fwname sono esposti come proprietà di comodo sul pacchetto; tutto il resto rimane grezzo.```python body = packet.body for tlv in getattr(body, "tlvs", ()): if tlv.tag == ac.TlvTag.PADDING: continue print(f"{tlv.tag.name:<10} ({len(tlv.data)} bytes): {tlv.data!r}")

root@kitploit:~
### Decodifica di un singolo messaggio AirMAX M

Stessa forma, superficie più piccola — solo 9 byte del payload M sono documentati;
il resto viene preservato fedelmente su `unknown_rest`. **Nota:** il payload M
di per sé non ha un campo SSID — per quello, guarda l'IE SSID standard 802.11
nel beacon o nella probe response che lo contiene (esposto come
`FrameMeta.ssid` quando si itera tramite `pyrmax.pcap`).```python
from pyrmax import m

packet = m.decode(data, src_mac="aa:bb:cc:dd:ee:ff")

packet.version           # 1
packet.msg_type          # <MsgType.BEACON: 1>
packet.src_mac           # b'\xaa\xbb\xcc\xdd\xee\xff'
packet.enable            # 1
packet.unknown_rest      # b'\xde\xad\xbe\xef...'   # opaque, RE pending

Costruire messaggi (encode)

encode(packet, dst_mac=…) è l'inverso byte-esatto di decode — encode(decode(x)) == x per una trama ben formata. Serializza il corpo (riapplicando i controlli di versione e, per deauth, la maschera XOR di src_mac), aggiunge il padding fino al blocco AES, cifra con la chiave derivata da packet.src_mac ed emette i byte successivi all'OUI. Avvolgilo con to_ie() per una vendor IE 0xDD completa.

I costruttori build_* ti evitano di assemblare a mano i corpi annidati:```python from pyrmax import ac

A beacon (mac_0c defaults to src_mac; broadcast key, as beacons use)

pkt = ac.build_beacon(src_mac="24:5a:4c:44:57:fd", radioname="LB1", ssid="labalUBI2", cap_flags=0x3e) ie = ac.to_ie(ac.encode(pkt)) # full 802.11 vendor IE, ready to embed

An authenticated deauth — the encoder XOR-masks the src with the jiffies nonce

deauth = ac.build_deauth(src_mac="24:5a:4c:44:57:fd", jiffies_nonce=0xdeadbeef) raw = ac.encode(deauth, dst_mac="24:a4:3c:88:d8:22")

Others: ac.build_assoc_req / build_assoc_resp / build_probe_req, and m.build_m

root@kitploit:~
Per fuzzing / PoC, `seal()` cifra **qualsiasi** plaintext e costruisce l'header esterno — così puoi creare payload deliberatamente malformati (`msg_type` fittizio, lunghezze errate, corpi sub-block) che l'encoder strutturato non produrrebbe mai:```python
frame = ac.seal(b"\xde\xad\xbe\xef", src_mac="aa:bb:cc:dd:ee:ff",
                dst_mac="ff:ff:ff:ff:ff:ff", msg_type=0xEE)  # zero-padded to 16

Structured encode solleva EncodeError su qualsiasi cosa che non possa andare sul cavo (un valore TLV > 255 byte, testo cifrato ≥ 0x101); seal è permissivo per progettazione.

Flusso di pacchetti da un pcap o pcapng

pyrmax.pcap trova gli IE vendor AirMAX all'interno dei frame di gestione 802.11, estrae i MAC dall'header 802.11 esterno e invia tutto al decoder corretto. Sia .pcap che .pcapng vengono rilevati automaticamente. I frame che non riescono a essere decodificati (chiave errata, danneggiati, IE vendor non correlati) vengono saltati silenziosamente — l'iterazione si ferma solo all'EOF.```python from pyrmax import pcap

for meta, packet in pcap.iter_ac("capture.pcap"): print( f"{meta.timestamp:.3f} " f"{meta.src_mac.hex(':')} → {meta.dst_mac.hex(':')} " f"{packet.msg_type.name:<10} " f"radio={packet.radioname!r} ssid={packet.ssid!r}" )

for meta, packet in pcap.iter_m("capture.pcapng"): print( f"{meta.timestamp:.3f} " f"{packet.msg_type.name:<10} " f"src={packet.src_mac.hex(':')} enable={packet.enable}" )

root@kitploit:~
`meta` è un `FrameMeta(timestamp, src_mac, dst_mac, bssid)`. L'estrazione di canale/RSSI
dal radiotap è nella lista TODO.

### Recupera il nome del dispositivo da una IE Routerboard.com

I frame AirMAX M sono quasi sempre accompagnati da una vendor IE Mikrotik /
Routerboard.com (OUI `00:0C:42`) nello stesso frame di gestione 802.11.
La sua sub-IE di sottotipo-1 trasporta il nome del dispositivo.```python
from pyrmax import pcap

for meta, packet in pcap.iter_routerboard("capture.pcap"):
    print(f"{meta.src_mac.hex(':')}  →  {packet.device_name!r}")
    # "AP Sur HY1315"

Il decoder Routerboard è anche disponibile standalone — passa i dati IE a partire dall'OUI:```python from pyrmax import routerboard

packet = routerboard.decode(ie_data) packet.device_name # "AP Sur HY1315" packet.sub_ies # tuple of SubIe(subtype, data)

root@kitploit:~
Correla i pacchetti Routerboard con i pacchetti M nella stessa cattura
abbinando `meta.timestamp` + `meta.src_mac`.

### `scan` — trova dispositivi vulnerabili

Cattura da un'interfaccia in modalità monitor (o da un pcap) i dispositivi AirMAX e segnala
quali sono vulnerabili. Richiede l'extra `[scan]` (`scapy`) e, per la cattura live,
i privilegi di root.```sh
# offline — scan a capture (no root)
python -m pyrmax scan capture.pcap

# live, passive — read versions only from traffic that happens to fly
sudo python -m pyrmax scan -i wlan0mon --channel 36

# live, active — force AirMAX AC APs to disclose their firmware
sudo python -m pyrmax scan -i wlan0mon --channel 36 --active

La versione determina la vulnerabilità in modo diverso per ogni variante. Sono coinvolti due numeri di versione: la versione del protocollo wire-format trasportata in ogni IE AirMAX (inclusi i beacon), e la versione del firmware AC (fwname) che viaggia solo nello scambio di associazione.``` AirMAX AC ──> proto < 9 ? ──yes──────────────┐ │no ├──> VULNERABLE ▼ │ fw <= 8.7.20 ? ──yes───────────┘ ├──no───────> PATCHED └──unknown──> UNDETERMINED

AirMAX M ───> proto < 15 ? ──yes──> VULNERABLE └──no───────────> UNDETERMINED

root@kitploit:~
Poiché `proto` è nel beacon, i dispositivi **vecchi** (AC epoch < 9, M
versione < 15) vengono rilevati **passivamente**. Per ottenere un verdetto `PATCHED` per
AC richiede la versione del firmware, quindi serve o un'associazione catturata
o `--active` (l'handshake attivo — auth → assoc → leggere il campo assoc-resp
`fwname` — è uno scambio completo e byte-fedele di stazione Ubiquiti).

Flag: `--channel N` (blocca un canale, altrimenti salta), `--seconds N`,
`--cutoff X.Y.Z` (vulnerabile ad AC se `fw <= cutoff`, predefinito `8.7.20`),
`--src MAC` (sorgente per la sonda attiva, es. il peer PTP), `--vuln-only`,
`--no-set-channel`. Il codice di uscita è **3** quando viene trovato qualsiasi dispositivo vulnerabile
(utile per gli script), altrimenti `0`.```
AirMAX: 5 device(s) (3 AC, 2 M), 2 vulnerable (AC fw <= 8.7.20 or protocol version below the fixed epoch).

  1c:6a:1b:00:00:01  AC  VulnAC            ch36   v8.7.19    VULNERABLE     rssi=-40dBm  peers=0
  1c:6a:1b:00:00:04  M   VulnM             ch36   v14        VULNERABLE     rssi=-42dBm  peers=0
  1c:6a:1b:00:00:03  AC  PatchedAC         ch36   v8.7.24    patched        rssi=-41dBm  peers=0
  1c:6a:1b:00:00:02  AC  UndetAC           ch36   epoch9     undetermined   rssi=-41dBm  peers=0
  1c:6a:1b:00:00:05  M   UndetM            ch36   v15        undetermined   rssi=-43dBm  peers=0

emulate — target AirMAX fittizi

Emetti beacon come uno o più dispositivi AirMAX AC/M fittizi e, per AC, rispondi all' handshake di discovery così che uno scanner attivo legga la versione del firmware emulato. Richiede l'extra [emulate] (scapy), un'interfaccia in modalità monitor, e root. Utile per testare scan senza hardware reale.```sh sudo python -m pyrmax emulate -i wlan1mon --channel 36
-d ac/8.7.19/VulnAC -d ac/9/UndetAC -d ac/8.7.24/PatchedAC
-d m/14/VulnM -d m/15/UndetM

root@kitploit:~
Ogni `-d` (ripetibile) è `TYPE/VERSION[/SSID[/MAC]]`, separato da `/` così i
due punti del MAC sono sicuri:

- `ac/8.7.19` — AC moderno, firmware `8.7.19` (epoch 9, rivela `fwname`)
- `ac/9` — epoch AC moderno, **nessuna** stringa firmware → lo scanner vede
  `undetermined`
- `ac/7` — AC **vecchio**, epoch 7 wire-format (< 9) → vulnerabile, rilevato dal
  beacon
- `m/14` — AirMAX M, versione 14 (< 15) → vulnerabile
- `m/15` — AirMAX M all'epoch corretto → undetermined

Senza `-d`, viene emulata una flotta demo. `--no-respond` solo beacon (il
firmware AC quindi non lo rivelerà). La versione del firmware AC vive solo nella
assoc-resp, ed è per questo che esiste il responder.

### Testa entrambi insieme su una sola macchina

`scripts/hwsim_testbed.sh` crea due radio virtuali tramite `mac80211_hwsim`
così puoi eseguire `emulate` su una e `scan` sull'altra senza hardware:```sh
sudo ./scripts/hwsim_testbed.sh up 36    # prints EMU_IFACE / SCAN_IFACE
# ...run emulate on EMU_IFACE and scan on SCAN_IFACE (two terminals)...
sudo ./scripts/hwsim_testbed.sh down

scripts/demo_5_devices.sh fa tutto il processo end-to-end — avvia le radio, emula la flotta di 5 dispositivi sopra menzionata, esegue scan --active e smantella tutto:```sh sudo ./scripts/demo_5_devices.sh 36

root@kitploit:~
### Gestione degli errori

Le mancate corrispondenze di schema e le violazioni di integrità sollevano `pyrmax.DecodeError`. La
causa più comune è una chiave errata (`src_mac` / `dst_mac` errati forniti a
`decode()` per il frame in questione).```python
from pyrmax import ac, DecodeError

try:
    packet = ac.decode(data, src_mac=src, dst_mac=dst)
except DecodeError as exc:
    print(f"skipping frame: {exc}")

Extra opzionali

  • pip install pyrmax[pcap] (or uv sync --extra pcap) — include dpkt, così pyrmax.pcap.iter_ac(path) / iter_m(path) possono trasmettere i pacchetti da catture .pcap o .pcapng (link type DLT_IEEE802_11_RADIO). Il formato viene rilevato automaticamente dal magic number del file.
  • pip install pyrmax[live] — aggiunge pcapy-ng per la cattura live da un'interfaccia wireless in modalità monitor. Usato dal flag -i / --iface della CLI e dal generatore programmatico pyrmax.pcap.iter_airmax_live(iface).
  • pip install pyrmax[scan] — aggiunge scapy per il comando scan (sniff/decode live + l'handshake attivo force-assoc).

Installa più extra in una volta sola, ad es. uv sync --extra scan --extra emulate.

Struttura```

pyrmax/ ├── ksy/ # Kaitai Struct source schemas │ ├── airmax_ac.ksy # AC cleartext outer header (payload decode is hand-written in ac.py) │ ├── airmax_m.ksy # M outer (OUI marker + encrypted blob) │ ├── airmax_m_payload.ksy # M decrypted payload (9 documented bytes) │ └── routerboard.ksy # Mikrotik / Routerboard.com vendor IE ├── src/pyrmax/ │ ├── init.py │ ├── ac.py # AC decode/encode API + AcPacket dataclass │ ├── m.py # M decode/encode API + MPacket dataclass │ ├── routerboard.py # Routerboard IE decoder + RouterboardPacket │ ├── pcap.py # iter_ac / iter_m / iter_routerboard / iter_airmax / iter_airmax_live │ ├── devices.py # summarize() — per-device aggregation │ ├── vuln.py # firmware-version parse + is_vulnerable() │ ├── scan.py # Scanner — live/pcap discovery + active handshake + vuln verdict │ ├── emulate.py # Emulator — fake AC/M targets (scapy) │ ├── main.py # python -m pyrmax CLI │ ├── exceptions.py │ ├── _crypto.py # AES-128-ECB + HMAC-SHA1 KDF (internal) │ └── _generated/ # kaitai-struct-compiler output (committed) ├── scripts/ │ ├── hwsim_testbed.sh # two virtual radios (mac80211_hwsim) for scan<->emulate │ └── demo_5_devices.sh # end-to-end 5-device emulate + scan demo └── tests/ ├── samples/ # raw frame captures (currently empty) ├── test_ac.py ├── test_m.py ├── test_crypto.py ├── test_pcap.py ├── test_routerboard.py ├── test_devices.py ├── test_cli.py └── test_integration.py # real-capture round-trips

root@kitploit:~
## Sviluppo```sh
uv sync                    # create .venv and install runtime + dev deps
uv run pytest              # run tests
uv run ruff check          # lint
uv run pyright             # static type check

La configurazione vive in pyproject.toml ([tool.pyright]):

  • typeCheckingMode = "basic" — individua i problemi strutturali senza scontrarsi con il confine dpkt / kaitaistruct / pycryptodome (questi pacchetti non includono stub di tipo).
  • src/pyrmax/_generated/ è esclusa — i file generati da Kaitai già contengono # type: ignore e vengono sovrascritti a ogni esecuzione di kaitai-struct-compiler.
  • I confini verso dpkt che toccano attributi dinamici (es. MGMT_Frame.src) vengono attraversati con un'annotazione Any sul binding locale anziché con commenti ignore sparsi.

Rigenerazione dei parser Kaitai

I file Python generati sotto src/pyrmax/_generated/ sono versionati, così il pacchetto si installa senza una toolchain Kaitai. Per rigenerarli dopo aver modificato un .ksy:```sh kaitai-struct-compiler -t python --outdir src/pyrmax/_generated/ ksy/*.ksy

root@kitploit:~
Scarica lo strumento
field_14
field_9c
sta_field_68
ic_6b8
enc_token
docs/ac_wire_format.md
  • Verificare i decoder AC version < 9 contro catture reali — il TX builder emette solo versione 9, quindi i percorsi delle versioni inferiori (nome non taggato, campi di coda mancanti) sono implementati dalla spec ma non verificati sul wire.
  • Fare reverse engineering del resto del payload M (byte 9+ in unknown_rest) — attualmente opaco.
  • Esporre i metadati radiotap su pcap.FrameMeta: channel, RSSI, rate. Attualmente sono popolati solo timestamp/MAC/BSSID.
  • Fixture di cattura reali in tests/samples/ — attualmente: airmax_ac_beacon.pcap (1 frame, beacon) e airmax_m_probe_response.pcap (1 frame, probe response). Altre varianti (assoc req/resp, catture multi-frame) ancora benvenute.
  • Invio di pacchetti — i comandi scan (force-assoc attivo) ed emulate iniettano tramite scapy (gli extra [scan] / [emulate]). Vedi scan.py / emulate.py.
  • pip install pyrmax[emulate] — aggiunge scapy per il comando emulate (inietta beacon + risponde all'handshake di discovery come dispositivi fittizi).