Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
pyrmax — Codificador/decodificador para tramas del protocolo inalámbrico AirMAX de Ubiquiti; analiza capturas pcap/en vivo, descubre dispositivos, busca firmware vulnerable, crea paquetes malformados y emula objetivos AirMAX AC/M. | Kitploit
Herramientas/GitHubGitHub/infobyte/pyrmax
Escáneres de VulnerabilidadesIngeniería InversaFuzzingSeguridad de RedesSeguridad Inalámbrica
GitHubinfobyte/pyrmax

pyrmax

Codificador/decodificador para tramas del protocolo inalámbrico AirMAX de Ubiquiti; analiza capturas pcap/en vivo, descubre dispositivos, busca firmware vulnerable, crea paquetes malformados y emula objetivos AirMAX AC/M.

Ver Repositorio
3hace 13 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

pyrmax

Decodificador y codificador de tramas del protocolo wire de Ubiquiti AirMAX, que cubre tanto la serie AC (firmware WA) como la serie M (firmware XW/XM).

  • Decodificación de las cabeceras externas en claro está impulsada por esquemas de Kaitai Struct bajo ksy/. La carga útil de AC (restringida por versión, conmutada por msg_type, con el desenmascarado XOR de deauth) está escrita a mano en ac.py — esa lógica no encaja en Kaitai solo-parse.
  • Codificación está escrita a mano para reflejar la ruta de decodificación campo por campo.

Estado

VarianteDecodificaciónCodificaciónIteración de pcap
AC✅ los 5 tipos de msg (beacon, assoc req/resp, probe req, deauth)✅ inverso byte-exacto + constructores + seal✅
Mparcial (9 bytes documentados; el resto como unknown_rest)✅ hace round-trip de la cabecera documentada + unknown_rest✅
IE de Routerboard.com (compañero de M)✅ nombre de dispositivo + lista de sub-IEn/a✅

El formato wire de AC sigue docs/ac_wire_format.md ([P]-confirmado contra ubnt_poll_host.ko). Todos los enteros multi-byte de AC son big-endian.

TODO

  • pyrmax.ac.encode(AcPacket) -> bytes — inverso byte-exacto escrito a mano de decode() (+ seal(), to_ie() y constructores build_*).
  • pyrmax.m.encode(MPacket) -> bytes — lo mismo para M (+ build_m, seal, to_ie).
  • Precisar los campos [open] de AC — mapa de bits exacto de cap_flags, mixed_mode, / (assoc_req), / (assoc_resp), y el de deauth (extraíble pero su clave aún no tiene ingeniería inversa, por lo que no puede verificarse). Ver §11-§12.

Herramienta de línea de comandos

El paquete incluye una CLI controlada por subcomandos. Ejecútalo como 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` y `discover` aceptan **ya sea** un archivo pcap/pcapng (posicional)
**o** una interfaz inalámbrica en vivo a través de `-i / --iface IFACE`. Ambas opciones son
mutuamente excluyentes. `scan` acepta las mismas opciones de origen (además de un
modo `--active` que solo se aplica a una interfaz en vivo); `emulate` es
exclusivo de interfaces en vivo.```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 captura en vivo asume que la interfaz ya está en modo monitor en el canal de interés — pyrmax no configura ninguno de los dos. Requiere el extra opcional [live] (pip install pyrmax[live]) que incorpora pcapy-ng. Presione Ctrl-C para detener: parse informa cuántas tramas transmitió; discover imprime el resumen agregado de dispositivos al salir.

Códigos de salida (compartidos por ambos comandos y ambos modos de origen): 0 en caso de éxito (incluyendo "sin tramas AirMAX" — un resultado válido), 1 para errores de formato de captura (tipo de enlace incorrecto, archivo malformado, no se puede abrir la interfaz), 2 para archivo faltante o argumentos de origen no válidos.

parse — volcado por trama```sh

python -m pyrmax parse capture.pcap

root@kitploit:~
Cada trama de gestión 802.11 que transporta un IE de proveedor AirMAX produce un bloque: paquete AC, paquete M y cualquier IE acompañante de Routerboard.com que se encuentre en la misma trama.```
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

El mismo walker de una sola pasada se expone programáticamente como pyrmax.pcap.iter_airmax(path) — produce un AirmaxRecord(meta, ac, m, routerboard) por trama que contenga AirMAX, por lo que no necesitas correlacionar manualmente los IEs de M y Routerboard.

discover — resumen del dispositivo```sh

python -m pyrmax discover capture.pcap

root@kitploit:~
Las tramas se agrupan por MAC de origen 802.11, los peers se acumulan, y las
observaciones de payload AC / payload M / nombre de dispositivo Routerboard se consolidan en
un solo bloque por 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 misma agregación también es una función pública:```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:~
## Uso

### Decodificar un único mensaje AirMAX AC

El decodificador espera los bytes de la IE específica del proveedor 802.11 **a partir
del OUI** — el envoltorio de la IE (Element ID `0xDD` + Length) ya debe haber sido
eliminado. `src_mac` / `dst_mac` se extraen de los SA / DA de la trama 802.11
exterior.```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 es uno de BEACON / ASSOC_REQ / ASSOC_RESP / PROBE_REQ / DEAUTH. Los campos de cola controlados por versión (field_9c, rssi, fwname, txpower, …) son None cuando la version de la trama está por debajo de su umbral. Deauth deshace el XOR de src_mac con el nonce de jiffies antes de la comprobación de integridad y expone el enc_token de 16 bytes como bytes opacos (su clave no está invertida aún, por lo que no se puede verificar).

Recorrer la lista AC TLV

Los cuerpos que llevan TLV de nombre (beacon, assoc_req) preservan el flujo — incluida la entrada Padding final — tal cual en body.tlvs. radioname, ssid y fwname se exponen como propiedades de conveniencia en el paquete; todo lo demás permanece sin procesar.```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:~
### Decodificar un único mensaje AirMAX M

Misma forma, superficie más pequeña — solo se documentan 9 bytes de la carga útil M; el resto se conserva literalmente en `unknown_rest`. **Nota:** la carga útil M en sí no tiene campo SSID — para eso, consulta la IE SSID estándar 802.11 en la baliza o respuesta de sonda circundante (expuesta como `FrameMeta.ssid` al iterar mediante `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

Crear mensajes (encode)

encode(packet, dst_mac=…) es la inversa exacta en bytes de decode — encode(decode(x)) == x para una trama bien formada. Serializa el cuerpo (reaplicando las compuertas de versión y, para deauth, la máscara XOR de src_mac), rellena hasta el bloque AES, cifra con la clave derivada de packet.src_mac y emite los bytes a partir del OUI. Envuelve con to_ie() para una IE de proveedor completa 0xDD.

Los constructores build_* te ahorran ensamblar a mano los cuerpos anidados:```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:~
Para fuzzing / PoCs, `seal()` cifra texto plano **arbitrario** y construye la
cabecera externa — para que puedas crear payloads deliberadamente malformados (`msg_type`
falso, longitudes incorrectas, cuerpos de sub-bloques) que el codificador estructurado
nunca produciría:```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

El encode estructurado lanza EncodeError ante cualquier cosa que no pueda enviarse por el cable (un valor TLV > 255 bytes, texto cifrado ≥ 0x101); seal es permisivo por diseño.

Procesar paquetes de un pcap o pcapng

pyrmax.pcap encuentra IEs de proveedor AirMAX dentro de tramas de gestión 802.11, extrae las MAC de la cabecera 802.11 externa y alimenta todo al decodificador adecuado. Tanto .pcap como .pcapng se detectan automáticamente. Las tramas que no se pueden decodificar (clave incorrecta, datos corruptos, IE de proveedor no relacionado) se omiten silenciosamente — la iteración solo se detiene al llegar a 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` es un `FrameMeta(timestamp, src_mac, dst_mac, bssid)`. La extracción de canal/RSSI
desde radiotap está en la lista TODO.

### Recuperar el nombre del dispositivo de una IE de Routerboard.com

Las tramas AirMAX M están casi siempre acompañadas de una IE de proveedor de Mikrotik /
Routerboard.com (OUI `00:0C:42`) en la misma trama de gestión
802.11. Su sub-IE de subtipo 1 contiene el nombre 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"

El decodificador de Routerboard también está disponible de forma independiente: pase los datos de IE a partir de la 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:~
Correlaciona los paquetes Routerboard con los paquetes M en la misma captura haciendo coincidir `meta.timestamp` + `meta.src_mac`.

### `scan` — encontrar dispositivos vulnerables

Captura el tráfico de una interfaz en modo monitor (o un pcap) para buscar dispositivos AirMAX y marca cuáles son vulnerables. Requiere el extra `[scan]` (`scapy`) y, para captura en vivo, 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 versión determina la vulnerabilidad de manera diferente según la variante. Dos números de versión están involucrados: la versión del protocolo de formato de cable transportada en cada IE de AirMAX (incluidas las balizas), y la versión de firmware de AC (fwname) que viaja solo en el intercambio de asociación.``` 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:~
Debido a que `proto` está en la baliza, los dispositivos **antiguos** (época AC < 9, M
versión < 15) se detectan **pasivamente**. Alcanzar un veredicto `PATCHED` para
AC requiere la versión del firmware, por lo que necesita una asociación capturada
o `--active` (el handshake activo — auth → assoc → leer el assoc-resp
`fwname` — es un intercambio de estación Ubiquiti completo y fiel a nivel de byte).

Flags: `--channel N` (bloquear un canal, si no saltar), `--seconds N`,
`--cutoff X.Y.Z` (vulnerable a AC si `fw <= cutoff`, por defecto `8.7.20`),
`--src MAC` (origen para la sonda activa, p. ej. el peer PTP), `--vuln-only`,
`--no-set-channel`. El código de salida es **3** cuando se encuentra algún dispositivo vulnerable
(útil para scripting), de lo contrario `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 — objetivos AirMAX falsos

Actúa como baliza de uno o más dispositivos AirMAX AC/M falsos y, para AC, responde al handshake de descubrimiento para que un escáner activo lea la versión de firmware emulada. Requiere el extra [emulate] (scapy), una interfaz en modo monitor y permisos de root. Útil para probar scan sin hardware real.```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:~
Cada `-d` (repetible) es `TYPE/VERSION[/SSID[/MAC]]`, separado por `/` para que los dos puntos de la MAC sean seguros:

- `ac/8.7.19` — AC moderno, firmware `8.7.19` (epoch 9, revela `fwname`)
- `ac/9` — epoch de AC moderno, **sin** cadena de firmware → el escáner ve `undetermined`
- `ac/7` — AC **antiguo**, epoch de formato de trama 7 (< 9) → vulnerable, detectado desde la baliza
- `m/14` — AirMAX M, versión 14 (< 15) → vulnerable
- `m/15` — AirMAX M en el epoch corregido → undetermined

Sin `-d`, se emula una flota de demostración. `--no-respond` emite solo balizas (el firmware AC entonces no revelará la versión). La versión de firmware AC solo reside en la assoc-resp, por eso existe el responder.

### Probar ambos juntos en una sola máquina

`scripts/hwsim_testbed.sh` crea dos radios virtuales mediante `mac80211_hwsim` para que puedas ejecutar `emulate` en una y `scan` en la otra sin 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 hace todo el proceso de principio a fin — levanta los radios, emula la flota de 5 dispositivos mencionada, ejecuta scan --active y finaliza:```sh sudo ./scripts/demo_5_devices.sh 36

root@kitploit:~
### Manejo de errores

Las discrepancias de esquema y los fallos de integridad lanzan `pyrmax.DecodeError`. La
causa más común es una clave incorrecta (`src_mac` / `dst_mac` incorrectos proporcionados a
`decode()` para la trama en cuestión).```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}")

Extras opcionales

  • pip install pyrmax[pcap] (o uv sync --extra pcap) — incorpora dpkt para que pyrmax.pcap.iter_ac(path) / iter_m(path) puedan transmitir paquetes desde capturas .pcap o .pcapng (tipo de enlace DLT_IEEE802_11_RADIO). El formato se detecta automáticamente a partir del número mágico del archivo.
  • pip install pyrmax[live] — añade pcapy-ng para la captura en vivo desde una interfaz inalámbrica en modo monitor. Se usa mediante la opción -i / --iface de la CLI y mediante el generador programático pyrmax.pcap.iter_airmax_live(iface).
  • pip install pyrmax[scan] — añade scapy para el comando scan (sniff/decodificación en vivo + el handshake activo de force-assoc).

Instala varios a la vez, p. ej. uv sync --extra scan --extra emulate.

Estructura```

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:~
## Desarrollo```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

Configuration lives in pyproject.toml ([tool.pyright]):

La configuración vive en pyproject.toml ([tool.pyright]):

  • typeCheckingMode = "basic" — catches structural issues without fighting the dpkt / kaitaistruct / pycryptodome boundary (those packages don't ship type stubs).

  • typeCheckingMode = "basic" — detecta problemas estructurales sin pelear con el límite de dpkt / kaitaistruct / pycryptodome (esos paquetes no incluyen stubs de tipos).

  • src/pyrmax/_generated/ is excluded — Kaitai-generated files already carry # type: ignore and are overwritten on each kaitai-struct-compiler run.

  • src/pyrmax/_generated/ está excluido — los archivos generados por Kaitai ya llevan # type: ignore y se sobrescriben en cada ejecución de kaitai-struct-compiler.

  • Boundaries to dpkt that touch dynamic attributes (e.g. MGMT_Frame.src) are crossed with an Any annotation on the local binding rather than scattered ignore comments.

  • Los límites con dpkt que tocan atributos dinámicos (p. ej. MGMT_Frame.src) se cruzan con una anotación en el enlace local en lugar de comentarios de ignore dispersos.

Regenerating Kaitai parsers

Regenerando los parsers de Kaitai

The generated Python files under src/pyrmax/_generated/ are committed so the package installs without a Kaitai toolchain. To regenerate after editing a .ksy:

Los archivos Python generados en src/pyrmax/_generated/ están versionados para que el paquete se instale sin una cadena de herramientas de Kaitai. Para regenerar después de editar un .ksy:```sh kaitai-struct-compiler -t python --outdir src/pyrmax/_generated/ ksy/*.ksy

root@kitploit:~
Descargar herramienta
field_14
field_9c
sta_field_68
ic_6b8
enc_token
docs/ac_wire_format.md
  • Verificar los decodificadores AC de version < 9 contra capturas reales — el constructor TX solo emite versión 9, por lo que las rutas de versiones inferiores (nombre sin etiqueta, campos de cola ausentes) están implementadas según la especificación pero sin verificar en el wire.
  • Hacer ingeniería inversa del resto de la carga útil de M (bytes 9+ en unknown_rest) — actualmente opaco.
  • Exponer metadatos de radiotap en pcap.FrameMeta: canal, RSSI, tasa. Actualmente solo se rellenan timestamp/MACs/BSSID.
  • Capturas reales de ejemplo en tests/samples/ — actualmente: airmax_ac_beacon.pcap (1 trama, beacon) y airmax_m_probe_response.pcap (1 trama, respuesta de probe). Más variantes (assoc req/resp, capturas multi-trama) aún son bienvenidas.
  • Envío de paquetes — los comandos scan (asociación forzada activa) y emulate inyectan vía scapy (los extras [scan] / [emulate]). Ver scan.py / emulate.py.
  • pip install pyrmax[emulate] — añade scapy para el comando emulate (inyectar balizas + responder al handshake de descubrimiento como dispositivos falsos).
  • Any