
وحدة فك تشفير/ترميز لإطارات بروتوكول AirMAX اللاسلكي من Ubiquiti؛ تحليل التقاطات pcap أو الالتقاط المباشر، اكتشاف الأجهزة، فحص البرامج الثابتة المعرضة للثغرات، صياغة حزم مشوهة، ومحاكاة أهداف AirMAX AC/M.
وحدة فك ترميز وترميز لإطارات بروتوكول Ubiquiti AirMAX، تغطي كلا سلسلتي AC (البرنامج الثابت WA) وM (البرنامج الثابت XW/XM).
ksy/. أما حمولة AC
(المقيّدة بالإصدار، والتي تتبدّل حسب msg_type، مع إزالة قناع XOR لـ deauth)
فهي مكتوبة يدويًا في ac.py — وهذا المنطق لا يناسب Kaitai المخصص للتحليل فقط.| النسخة | فك الترميز | الترميز | تكرار pcap |
|---|---|---|---|
| AC | ✅ جميع أنواع الرسائل الخمسة (beacon، assoc req/resp، probe req، deauth) | ✅ معكوس مطابق للبايت + دوال البناء (builders) + seal | ✅ |
| M | جزئي (9 بايتات موثقة؛ والباقي كـ unknown_rest) | ✅ يعيد الرأس الموثّق + unknown_rest بلا فقدان | ✅ |
| Routerboard.com IE (مرافق لـ M) | ✅ اسم الجهاز + قائمة sub-IE | غير متاح | ✅ |
تنسيق إطار AC على الشبكة يتبع
docs/ac_wire_format.md
(مؤكَّدة مقابل ubnt_poll_host.ko وفقًا لـ [P]). جميع الأعداد الصحيحة متعددة البايت في 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)، وenc_token الخاص بـ deauth (قابل للاستخراج لكن مفتاحه لم
يُهندَس عكسيًا بعد، لذا لا يمكن التحقق منه). انظر
docs/ac_wire_format.md §11-§12.version < 9 مقابل التقاطات حقيقية —
مُنشئ الإرسال (TX builder) يُصدر الإصدار 9 فقط، لذا المسارات ذات الإصدار الأقل (اسم غير موسوم،
وحقول ذيل مفقودة) مطبَّقة وفق المواصفات لكن
غير مُتحقَّق منها على الشبكة.unknown_rest) — معتمة حاليًا.pcap.FrameMeta: القناة، RSSI،
المعدل. حاليًا يتم تعبئة الطابع الزمني/عناوين MAC/BSSID فقط.tests/samples/ — حاليًا:
airmax_ac_beacon.pcap (إطار واحد، beacon) و
airmax_m_probe_response.pcap (إطار واحد، استجابة probe). المزيد من الأنواع
(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، وبالتالي
لن تحتاج إلى مطابقة عناصر معلومات M وRouterboard يدويًا.
discover — ملخّص الجهاز```shpython -m pyrmax discover capture.pcap
تُجمَّع الإطارات حسب عنوان MAC المصدر لمعيار 802.11، وتتراكم الأقران، وتندمج ملاحظات حمولة 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 واحدة
يتوقع المُفكِّك وحدات البايت الخاصة بـ Vendor Specific IE في 802.11 **بدءًا من
OUI** — يجب أن يكون غلاف IE (معرّف العنصر `0xDD` + الطول) قد أُزيل
بالفعل. يتم استخراج `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
)
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)