
Ubiquiti AirMAX वायरलेस प्रोटोकॉल फ़्रेम के लिए डिकोडर/एन्कोडर; pcap/लाइव कैप्चर पार्स करें, डिवाइस खोजें, असुरक्षित फ़र्मवेयर के लिए स्कैन करें, विकृत पैकेट तैयार करें और AirMAX AC/M लक्ष्यों का अनुकरण करें।
Ubiquiti AirMAX वायर-प्रोटोकॉल फ्रेम के लिए डिकोडर और एन्कोडर, जो AC (WA फर्मवेयर) और M (XW/XM फर्मवेयर) दोनों श्रृंखलाओं को कवर करता है।
ksy/ के अंतर्गत संचालित होता है। AC पेलोड (संस्करण-गेटेड, msg_type पर स्विच्ड, डीऑथ XOR-अनमास्क के साथ) ac.py में हस्तलिखित है — वह तर्क पार्स-ओनली Kaitai में फिट नहीं होता।| वैरिएंट | डीकोड | एन्कोड | pcap पुनरावृत्ति |
|---|---|---|---|
| AC | ✅ सभी 5 msg प्रकार (beacon, assoc req/resp, probe req, deauth) | ✅ बाइट-सटीक व्युत्क्रम + बिल्डर + seal | ✅ |
| M | आंशिक (9 प्रलेखित बाइट्स; शेष unknown_rest के रूप में) | ✅ प्रलेखित हेड + unknown_rest का राउंड-ट्रिप | ✅ |
| Routerboard.com IE (M का सहयोगी) | ✅ डिवाइस नाम + उप-IE सूची | लागू नहीं | ✅ |
AC वायर प्रारूप
docs/ac_wire_format.md का अनुसरण करता है
([P]-ubnt_poll_host.ko के विरुद्ध पुष्टि)। सभी AC मल्टी-बाइट पूर्णांक
बड़े-एंडियन हैं।
pyrmax.ac.encode(AcPacket) -> bytes — decode() का हस्तलिखित बाइट-सटीक
व्युत्क्रम (+ seal(), to_ie(), और build_* कंस्ट्रक्टर)।pyrmax.m.encode(MPacket) -> bytes — M के लिए भी वही (+ build_m,
seal, to_ie)।[open] AC फ़ील्ड्स को पिन करें — cap_flags सटीक बिट मैप,
mixed_mode, / (assoc_req), /
(assoc_resp), और deauth (निकालने योग्य लेकिन इसकी कुंजी अभी तक
रिवर्स-इंजीनियर नहीं हुई है, इसलिए इसे सत्यापित नहीं किया जा सकता)। देखें
§11-§12।पैकेज एक उप-कमांड संचालित 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 फ़ाइल (positional) लेते हैं
**या** `-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 सफलता पर
(जिसमें "no AirMAX frames" — एक वैध परिणाम शामिल है), 1 कैप्चर-प्रारूप
त्रुटियों के लिए (गलत लिंक प्रकार, दूषित फ़ाइल, इंटरफ़ेस नहीं खोल सकता),
2 लापता फ़ाइल या अमान्य स्रोत तर्कों के लिए।
parse — प्रति-फ़्रेम डंप```shpython -m pyrmax parse capture.pcap
प्रत्येक 802.11 प्रबंधन फ्रेम जो AirMAX विक्रेता IE ले जाता है, एक
ब्लॉक उत्पन्न करता है: AC packet, M packet, और कोई भी Routerboard.com सहयोगी IE जो उसी फ्रेम में
पाया गया।```
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) के रूप में उपलब्ध है — यह प्रत्येक AirMAX-युक्त फ्रेम के लिए एक AirmaxRecord(meta, ac, m, routerboard) उत्पन्न करता है, इसलिए आपको M और Routerboard IEs को हाथ से मिलान करने की आवश्यकता नहीं है।
discover — डिवाइस सारांश```shpython -m pyrmax discover capture.pcap
फ्रेम्स को 802.11 सोर्स MAC द्वारा समूहीकृत किया जाता है, पीयर्स एकत्रित होते हैं, और AC
पेलोड / M पेलोड / 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)
## उपयोग
### एकल AirMAX AC संदेश डिकोड करें
डिकोडर 802.11 Vendor Specific IE के बाइट्स की अपेक्षा करता है **OUI से शुरू
होते हुए** — IE रैपर (Element ID `0xDD` + Length) पहले ही हटाया
जाना चाहिए। `src_mac` / `dst_mac` बाहरी 802.11 फ्रेम के
SA / DA से लिए जाते हैं।```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 इनमें से एक है: BEACON / ASSOC_REQ / ASSOC_RESP / PROBE_REQ /
DEAUTH. संस्करण-गेटेड (version-gated) टेल फ़ील्ड (field_9c, rssi, fwname, txpower,
…) तब None होते हैं जब फ़्रेम का version उनकी सीमा (threshold) से कम होता है। Deauth
इंटीग्रिटी जाँच से पहले src_mac का jiffies nonce के साथ XOR खोलता है और
16-बाइट enc_token को अपारदर्शी बाइट्स के रूप में सामने लाता है (इसकी कुंजी अभी उलटी
नहीं गई है, इसलिए इसे सत्यापित नहीं किया जा सकता)।
जो बॉडीज़ नाम TLV रखती हैं (beacon, assoc_req), वे स्ट्रीम को संरक्षित रखती हैं —
समापन Padding प्रविष्टि सहित — body.tlvs पर शब्दशः। radioname,
ssid, और fwname पैकेट पर सुविधाजनक प्रॉपर्टी (convenience properties) के रूप में सामने आते हैं;
बाकी सब कुछ कच्चा (raw) रहता है।```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}")
### एकल AirMAX M संदेश डिकोड करें
समान आकार, छोटी सतह — M पेलोड के केवल 9 बाइट्स दस्तावेज़ित हैं;
शेष भाग `unknown_rest` पर यथावत संरक्षित है। **नोट:** M पेलोड
में स्वयं SSID फ़ील्ड नहीं होता — उसके लिए, मानक 802.11 SSID IE
संलग्न बीकन या प्रोब रिस्पॉन्स पर देखें (इस रूप में सामने आता है
`FrameMeta.ssid` जब `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
encode(packet, dst_mac=…) decode का बाइट-सटीक व्युत्क्रम है —
encode(decode(x)) == x एक सुगठित फ्रेम के लिए। यह बॉडी को सीरियलाइज़ करता है
(version गेट्स और, deauth के लिए, src_mac XOR-mask को पुनः लागू करते हुए), पैड
AES ब्लॉक तक, packet.src_mac-व्युत्पन्न कुंजी से एन्क्रिप्ट करता है, और उत्सर्जित करता है
OUI-आगे के बाइट्स। पूर्ण 0xDD वेंडर IE के लिए to_ie() के साथ लपेटें।
build_* कंस्ट्रक्टर आपको नेस्टेड बॉडी को हाथ से जोड़ने से बचाते हैं:```python
from pyrmax import ac
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
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")
फ़ज़िंग / PoCs के लिए, `seal()` **मनमाना** प्लेनटेक्स्ट एन्क्रिप्ट करता है और बाहरी हेडर बनाता है — ताकि आप जानबूझकर विकृत पेलोड (गलत `msg_type`, गलत लंबाई, सब-ब्लॉक बॉडी) तैयार कर सकें, जिन्हें स्ट्रक्चर्ड एन्कोडर कभी उत्पन्न नहीं कर सकता:```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
संरचित encode ऐसी किसी भी चीज़ पर EncodeError उठाता है जो वायर पर नहीं जा सकती
(TLV मान > 255 बाइट्स, सिफरटेक्स्ट ≥ 0x101); seal डिज़ाइन से उदार है।
pyrmax.pcap 802.11 प्रबंधन फ्रेम्स के अंदर AirMAX वेंडर IE ढूंढता है,
बाहरी 802.11 हेडर से MAC निकालता है, और सब कुछ
सही डिकोडर में फीड करता है। .pcap और .pcapng दोनों स्वतः पहचाने जाते हैं। फ्रेम्स
जो डिकोड करने में विफल होते हैं (गलत कुंजी, गड़बड़, असंबंधित वेंडर IE) उन्हें छोड़ दिए जाते हैं
चुपचाप — पुनरावृत्ति केवल 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}" )
`meta` एक `FrameMeta(timestamp, src_mac, dst_mac, bssid)` है। radiotap से चैनल/RSSI
निष्कर्षण TODO सूची में है।
### Routerboard.com IE से डिवाइस का नाम प्राप्त करें
AirMAX M फ्रेम लगभग हमेशा उसी 802.11 प्रबंधन फ्रेम में Mikrotik /
Routerboard.com विक्रेता IE (OUI `00:0C:42`) के साथ होते हैं। इसका subtype-1 उप-IE डिवाइस का नाम रखता है।```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"
Routerboard डिकोडर भी स्टैंडअलोन रूप से उपलब्ध है — IE डेटा 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)
उसी कैप्चर में Routerboard पैकेटों को M पैकेटों के साथ सहसंबद्ध करें
`meta.timestamp` + `meta.src_mac` का मिलान करके।
### `scan` — कमजोर डिवाइस खोजें
AirMAX डिवाइसों के लिए मॉनिटर-मोड इंटरफ़ेस (या pcap) स्निफ करें और चिह्नित करें
कि कौन से कमजोर हैं। `[scan]` एक्स्ट्रा (`scapy`) की आवश्यकता है और, लाइव
कैप्चर के लिए, 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
संस्करण भेद्यता को प्रति वेरिएंट अलग-अलग निर्धारित करता है। दो संस्करण
संख्याएँ शामिल हैं: वायर-फ़ॉर्मेट प्रोटोकॉल संस्करण जो
हर AirMAX IE (बीकन सहित) में वहन किया जाता है, और AC फर्मवेयर संस्करण
(fwname) जो केवल एसोसिएशन एक्सचेंज में मौजूद रहता है।```
AirMAX AC ──> proto < 9 ? ──yes──────────────┐
│no ├──> VULNERABLE
▼ │
fw <= 8.7.20 ? ──yes───────────┘
├──no───────> PATCHED
└──unknown──> UNDETERMINED
AirMAX M ───> proto < 15 ? ──yes──> VULNERABLE └──no───────────> UNDETERMINED
क्योंकि `proto` बीकन में होता है, **पुराने** डिवाइस (AC epoch < 9, M
version < 15) **निष्क्रिय रूप से** पकड़े जाते हैं। `PATCHED` verdict तक पहुँचने के लिए
AC को फ़र्मवेयर संस्करण की आवश्यकता होती है, इसलिए इसे या तो कैप्चर किया गया association चाहिए
या `--active` (सक्रिय हैंडशेक — auth → assoc → assoc-resp
`fwname` पढ़ना — एक पूर्ण, बाइट-सटीक Ubiquiti station exchange है)।
फ़्लैग: `--channel N` (एक चैनल लॉक करें, अन्यथा hop करें), `--seconds N`,
`--cutoff X.Y.Z` (AC-असुरक्षित यदि `fw <= cutoff`, डिफ़ॉल्ट `8.7.20`),
`--src MAC` (सक्रिय प्रोब के लिए स्रोत, जैसे PTP peer), `--vuln-only`,
`--no-set-channel`. एग्ज़िट कोड **3** है जब कोई भी असुरक्षित डिवाइस मिलता है
(स्क्रिप्टिंग के लिए उपयोगी), अन्यथा `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 — नकली AirMAX लक्ष्यएक या अधिक नकली AirMAX AC/M डिवाइस के रूप में बीकन भेजें और, AC के लिए,
डिस्कवरी हैंडशेक का उत्तर दें ताकि एक सक्रिय स्कैनर अनुकरणित फर्मवेयर
संस्करण पढ़ सके। [emulate] एक्स्ट्रा (scapy), एक मॉनिटर-मोड इंटरफ़ेस,
और रूट की आवश्यकता है। वास्तविक हार्डवेयर के बिना scan का परीक्षण करने के लिए उपयोगी।```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
प्रत्येक `-d` (दोहराने योग्य) `TYPE/VERSION[/SSID[/MAC]]` है, जो `/`-से अलग किया गया है ताकि
MAC के कोलन सुरक्षित रहें:
- `ac/8.7.19` — आधुनिक AC, फर्मवेयर `8.7.19` (epoch 9, `fwname` प्रकट करता है)
- `ac/9` — आधुनिक AC epoch, **कोई** फर्मवेयर स्ट्रिंग नहीं → स्कैनर देखता है
`undetermined`
- `ac/7` — **पुराना** AC, वायर-फॉर्मेट epoch 7 (< 9) → असुरक्षित, पकड़ा गया
बीकन से
- `m/14` — AirMAX M, संस्करण 14 (< 15) → असुरक्षित
- `m/15` — AirMAX M निर्धारित epoch पर → undetermined
बिना `-d` के, एक डेमो फ्लीट अनुकरण किया जाता है। `--no-respond` केवल बीकन भेजता है (AC
फर्मवेयर तब प्रकट नहीं करेगा)। AC फर्मवेयर का संस्करण केवल
assoc-resp में रहता है, इसीलिए रिस्पॉन्डर मौजूद है।
### दोनों को एक मशीन पर एक साथ परीक्षण करें
`scripts/hwsim_testbed.sh` `mac80211_hwsim` के माध्यम से दो वर्चुअल रेडियो बनाता है,
ताकि आप बिना किसी हार्डवेयर के एक पर `emulate` और दूसरे पर `scan` चला सकें:```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 पूरी प्रक्रिया शुरू से अंत तक करता है — रेडियो को चालू करता है,
ऊपर दिए गए 5-डिवाइस फ्लीट का अनुकरण करता है, scan --active चलाता है, और सब कुछ
बंद कर देता है:```sh
sudo ./scripts/demo_5_devices.sh 36
### त्रुटि प्रबंधन
स्कीमा बेमेल और इंटीग्रिटी विफलताएँ `pyrmax.DecodeError` उत्पन्न करती हैं। सबसे
आम कारण गलत कुंजी है (हाथ में मौजूद फ्रेम के लिए `decode()` को गलत `src_mac` /
`dst_mac` दिया गया हो)।```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}")
pip install pyrmax[pcap] (या uv sync --extra pcap) — dpkt को शामिल करता है
ताकि pyrmax.pcap.iter_ac(path) / iter_m(path) .pcap या .pcapng कैप्चर से
पैकेट स्ट्रीम कर सकें (लिंक प्रकार DLT_IEEE802_11_RADIO)।
फ़ॉर्मेट फ़ाइल के मैजिक नंबर से स्वतः पहचाना जाता है।pip install pyrmax[live] — pcapy-ng जोड़ता है, जो मॉनिटर-मोड वायरलेस
इंटरफ़ेस से लाइव कैप्चर के लिए है। CLI के -i / --iface फ्लैग
और प्रोग्रामेटिक pyrmax.pcap.iter_airmax_live(iface) जनरेटर द्वारा उपयोग किया जाता है।pip install pyrmax[scan] — scan कमांड के लिए scapy जोड़ता है
(लाइव स्निफ़/डिकोड + सक्रिय फ़ोर्स-एसोसिएशन हैंडशेक)।एक साथ कई इंस्टॉल करें, जैसे uv sync --extra scan --extra emulate।
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
## विकास```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
कॉन्फ़िगरेशन pyproject.toml ([tool.pyright]) में रहता है:
typeCheckingMode = "basic" — बिना dpkt / kaitaistruct / pycryptodome सीमा से जूझे संरचनात्मक समस्याओं को पकड़ता है
(उन पैकेजों के साथ type stubs नहीं आते)।src/pyrmax/_generated/ को बाहर रखा गया है — Kaitai-जनरेटेड फ़ाइलों में पहले से # type: ignore होता है और हर
kaitai-struct-compiler रन पर उन्हें अधिलेखित कर दिया जाता है।MGMT_Frame.src) को छूती हैं, उन्हें बिखरी हुई ignore टिप्पणियों के बजाय स्थानीय binding पर Any एनोटेशन से
पार किया जाता है।src/pyrmax/_generated/ के अंतर्गत जनरेट किए गए Python फ़ाइलें कमिट की जाती हैं ताकि
पैकेज Kaitai टूलचेन के बिना इंस्टॉल हो सके। .ksy संपादित करने के बाद पुनर्जनरेट करने के लिए:```sh
kaitai-struct-compiler -t python --outdir src/pyrmax/_generated/ ksy/*.ksy
field_14field_9csta_field_68ic_6b8enc_tokenversion < 9 AC डिकोडरों को वास्तविक कैप्चर के विरुद्ध सत्यापित करें —
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।pip install pyrmax[emulate] — emulate कमांड के लिए scapy जोड़ता है
(बीकन इंजेक्ट करना + नकली डिवाइस के रूप में डिस्कवरी हैंडशेक का उत्तर देना)।