一个用 Lua 编写的 Wireshark/tshark 解析器,用于解析 Ubiquiti AirMAX(AC + M)以及配套的 Mikrotik / RouterBoard 厂商信息元素(Information Elements)。
它是 pyrmax 的线上传输(over-the-wire)对应实现:解码逻辑逐字段地镜像了该 Python 包。AC 数据包格式是通过对 AirMAX AC 固件二进制文件进行逆向工程得到的;M 和 RouterBoard 的布局则来自早期公开发布的、关于旧版 AirMAX-M 厂商 IE 的笔记。
AirMAX 承载于 802.11 厂商专用 IE(tag 221)中。Wireshark 本身已经解析 802.11 帧,并将厂商 IE 显示为一个不透明的数据块;wiremax 获取该数据块,对其进行解密(AES-128-ECB,密钥由帧中的 MAC 地址派生),然后渲染出一个已解析、可过滤的子树。
Tag: Vendor Specific: Ubiquiti Inc (built-in 802.11 dissector)
AirMAX AC (Vendor Specific IE) ← added by wiremax
Flags: 0x02 ( .... ..1. = Encrypted: True )
Message Type: Beacon (1)
Encrypted Length: 48
[Decrypted payload (AES-128-ECB)]
[AES Key …: 1f162a13… (dst=broadcast)]
Version: 9
Source MAC: 24:5a:4c:44:57:fd
Radio MAC (mac_0c): 24:5a:4c:44:57:fd
Capability Flags: 0x0000003e
Mixed Mode: 0
Radioname: LB1
SSID: labalUBI2
TLV: Radioname (1), len 3
TLV: SSID (2), len 9
TLV: Padding (0)
解密后的布局取决于 Message Type:beacon 帧携带 mac_0c / cap_flags / mixed_mode 以及名称 TLV(如上所示);assoc 请求/响应携带 chainmask、cap_flags 和受版本门控的尾部字段(field_9c、rssi、fwname、txpower);probe 请求只有头部;deauth 携带一个 jiffies 随机数(nonce)和一个不透明的认证令牌(并且其源 MAC 经过 XOR 掩码处理,wiremax 会将其还原)。
| 变体 | 覆盖范围 |
|---|---|
| AirMAX AC | 外部头部 + 解密后的共享头部(version + src_mac)+ 按消息类型划分、受版本门控的主体(beacon、assoc 请求/响应、probe 请求、deauth)+ 名称 TLV |
| AirMAX M | 外部外壳 + 解密后的 9 个已记录字节(version、msg_type、src_mac、enable)+ 原始剩余部分 |
| RouterBoard (Mikrotik) | OUI/type + 子 IE + 设备名称(明文) |
未记录的字节区域以原始形式显示(Unknown [n:len]),绝不臆造——这与 pyrmax 以及 ac/ 知识库遵循同样的原则。
一个后置解析器(postdissector)(它不会替换内置的 802.11 解析器,而是在其后运行)。对每一帧,它:
Field 提取器读取每一个 wlan.tag.oui 以及 wlan.sa / wlan.da;00:27:22)、M(00:15:6d)或 RouterBoard(00:0c:42)的 OUI——对于 AC/M,还要求 OUI-type 为 FF FF FF,因为 00:15:6d 也为 Mikrotik 以及会出现在同一帧中的另一个 Ubiquiti IE 所使用;AES-128-ECB 解密负载,密钥 = HMAC-SHA1(dst_mac, src_mac)[:16],先尝试使用捕获到的 802.11 目的地址,失败则回退到广播地址(身份帧即使以单播发送,也使用广播密钥)——胜出的是解密负载中内嵌的源 MAC 与 802.11 源地址匹配的那一个;加密相关部分(SHA-1、HMAC-SHA1、AES-128 解密)是自包含的纯 Lua 实现——Wireshark 没有向 Lua 暴露任何加密 API。负载只有几个 16 字节的块,因此性能无关紧要。密钥由公开的 MAC 派生而来,因此这属于混淆(obfuscation),而非安全机制。
一次性使用(无需安装):
tshark -X lua_script:airmax.lua -r capture.pcap -V
wireshark -X lua_script:airmax.lua capture.pcap
**持久化安装(Wireshark GUI + tshark):**将整个 wiremax/ 文件夹放入个人 Lua 插件(Personal Lua Plugins)目录。确切路径可在 Wireshark 的 Help ▸ About Wireshark ▸ Folders ▸ Personal Lua Plugins 下查看;默认路径如下:
| 操作系统 | 默认个人 Lua 插件路径 |
|---|---|
| Linux | ~/.local/lib/wireshark/plugins/ |
| macOS | ~/.local/lib/wireshark/plugins/(或 ~/.config/wireshark/plugins/) |
| Windows | %APPDATA%\Wireshark\plugins\ |
迭代开发时使用符号链接(symlink)最为理想——重新加载(Ctrl+Shift+L)即可自动应用修改,无需复制步骤:
ln -s "$PWD" ~/.local/lib/wireshark/plugins/wiremax
Wireshark 会递归加载插件文件夹下的所有 .lua 文件;wsairmax/ 下的子模块是普通模块(单独加载也无害),而 test/selftest.lua 在 Wireshark 内部是空操作(no-op)。
示例:
tshark -r cap.pcap -Y 'airmax.ac.ssid' -T fields -e wlan.sa -e airmax.ac.ssid
tshark -r cap.pcap -Y 'airmax.m.msg_type == 1' -T fields -e airmax.m.src_mac
tshark -r cap.pcap -Y 'airmax.rb.device_name' -T fields -e airmax.rb.device_name
抓包必须是 802.11 监控模式(radiotap);承载这些 IE 的管理帧(beacon / probe response / assoc)在 802.11 层不加密——只有 IE 内部的 AirMAX 负载经过 AES 封装,而 wiremax 会将其解封。
test/selftest.lua 在独立的 Lua 5.4 解释器(不含 Wireshark)下运行加密与解码逻辑,对照实测基准向量(ground-truth vectors)以及标准 SHA-1 / RFC-2202 HMAC 向量进行校验:
lua test/selftest.lua # from this directory
针对真实抓包进行端到端验证(pyrmax 在 tests/samples/ 下提供了示例 pcap):
tshark -X lua_script:airmax.lua -r airmax_ac_beacon.pcap -V
tshark -X lua_script:airmax.lua -r airmax_m_probe_response.pcap -V
wiremax/
├── airmax.lua # Wireshark entry: ProtoFields + postdissector + tree
├── wsairmax/ # pure-Lua modules (no Wireshark dependency)
│ ├── util.lua # hex / MAC helpers
│ ├── sha1.lua # SHA-1 + HMAC-SHA1
│ ├── aes.lua # AES-128 ECB decrypt
│ ├── crypto.lua # key derivation + decrypt (mirrors pyrmax/_crypto.py)
│ ├── ac.lua # AirMAX AC decoder (outer ksy: airmax_ac; payload hand-written per 09b)
│ ├── m.lua # AirMAX M decoder (ksy: airmax_m[_payload])
│ └── routerboard.lua # RouterBoard decoder (ksy: routerboard)
├── test/selftest.lua # standalone-Lua test harness
└── README.md
msg_type 根据较早的 AirMAX-M 笔记将 1 = Beacon 标记,但在 probe 响应中也观察到了值 1——M 的消息类型空间尚未完全映射,因此请以原始数值为准。enc_len,一个 u16 大端序(09b §3);旧版本按 1 字节读取只是在负载 < 256 时才碰巧正确。version < 9 的解码器同样未经验证(TX 只发出 9)。请参阅 test/selftest.lua 中的合成向量。当 pyrmax(src/pyrmax/ac.py 或其 ksy/*.ksy 模式)中新增或重命名字段时,请在对应的 wsairmax/*.lua 解码器中同步该改动;如果该字段对用户可见,请在 airmax.lua 中添加一个 ProtoField。test/selftest.lua 中的基准向量来自 pyrmax 示例 pcap,因此任何分歧都会在那里显现。
| 过滤器 | 含义 |
|---|
airmax.ac.msg_type | 消息类型:1 Beacon,2 Assoc Req,3 Assoc Resp,4 Probe Req,0xC Deauth |
airmax.ac.flags.encrypted | 加密位(0x02) |
airmax.ac.version | 解密后的格式版本(版本门控;TX 发出 9) |
airmax.ac.src_mac | 解密头部中的源 MAC(deauth 会将其还原) |
airmax.ac.mac_0c | beacon 无线 MAC(BSSID) |
airmax.ac.cap_flags + airmax.ac.cap_flags.chanbw/.high_density/.auth_deauth/.compat_11ax | 能力位域(§11) |
airmax.ac.radioname、airmax.ac.ssid、airmax.ac.fwname | AC 名称 / 固件字符串 |
airmax.ac.jiffies_nonce、airmax.ac.enc_token | deauth 随机数 + 不透明认证令牌 |
airmax.ac.tlv.tag / .len / .data | 原始 TLV 遍历 |
airmax.m.version、airmax.m.msg_type、airmax.m.src_mac、airmax.m.enable | M 的已记录字段 |
airmax.m.unknown_rest | 未记录的 M 尾部数据 |
airmax.rb.device_name | RouterBoard 设备名称 |
airmax.ac.key / airmax.m.key | 派生的 AES 密钥(自动生成,用于验证) |