2. Beacon帧间隔与SSID广播配置实验
任务目标
编写一个Python脚本,解析WiFi Beacon管理帧的二进制数据,提取SSID、BSSID、信道、支持速率、DS参数集等关键信息。脚本需支持从pcap文件中读取原始帧数据,并输出可读的解析结果。
环境依赖
仅需Python 3.6+标准库,无需第三方包。pcap文件解析部分自行实现最小读取逻辑,不依赖scapy等库,便于理解帧结构。
Beacon帧结构要点
Beacon帧属于管理帧(type=0),subtype=8。帧体(Frame Body)前12字节为固定字段,之后是变长的Tagged Parameters(TLV格式)。每个Tag由1字节Tag Number、1字节Length和对应的Value组成。本案例解析以下关键Tag:
- SSID (Tag 0):服务集标识
- Supported Rates (Tag 1):支持速率,单位Mbps,低7位为速率值
- DS Parameter Set (Tag 3):当前信道号
- HT Capabilities (Tag 45):提取信道宽度信息
完整实现代码
import struct
import sys
def parse_pcap(filepath):
"""读取pcap文件,返回所有Beacon帧的原始字节列表"""
beacons = []
with open(filepath, 'rb') as f:
# pcap全局头:magic(4) + version(4) + 保留(8) + snaplen(4) + network(4)
global_header = f.read(24)
if len(global_header) < 24:
return beacons
magic = struct.unpack('<I', global_header[:4])[0]
# 判断字节序(处理0xa1b2c3d4或0xd4c3b2a1)
little_endian = (magic == 0xa1b2c3d4)
endian = '<' if little_endian else '>'
while True:
# 每个包记录头:ts_sec(4) ts_usec(4) incl_len(4) orig_len(4)
rec_header = f.read(16)
if len(rec_header) < 16:
break
ts_sec, ts_usec, incl_len, orig_len = struct.unpack(endian + 'IIII', rec_header)
packet_data = f.read(incl_len)
if len(packet_data) < incl_len:
break
# 跳过Radiotap头(如果有),这里假设链路层为IEEE 802.11
# 检查是否为Beacon帧:Frame Control前2字节
if len(packet_data) < 24:
continue
frame_control = struct.unpack(endian + 'H', packet_data[:2])[0]
frame_type = (frame_control >> 2) & 0x3
frame_subtype = (frame_control >> 4) & 0xF
if frame_type == 0 and frame_subtype == 8: # 管理帧-Beacon
beacons.append(packet_data)
return beacons
def parse_beacon(frame):
"""解析单个Beacon帧,返回信息字典"""
info = {}
# Frame Control(2) + Duration(2) + DA(6) + SA(6) + BSSID(6) + Seq(2) = 24字节
# 帧体从偏移24开始
if len(frame) < 24:
return info
# BSSID:帧头中偏移16-21(SA之后的地址字段)
bssid_bytes = frame[16:22]
info['BSSID'] = ':'.join(f'{b:02X}' for b in bssid_bytes)
# 帧体
body = frame[24:]
if len(body) < 12:
return info
# 固定字段:Timestamp(8) + Beacon Interval(2) + Capability(2)
info['Timestamp'] = struct.unpack('<Q', body[:8])[0]
info['Beacon Interval'] = struct.unpack('<H', body[8:10])[0]
capability = struct.unpack('<H', body[10:12])[0]
info['Capability'] = f'0x{capability:04X}'
# Tagged Parameters从偏移12开始
pos = 12
ssid = None
rates = []
channel = None
ht_cap = None
while pos + 2 <= len(body):
tag_num = body[pos]
tag_len = body[pos + 1]
tag_value = body[pos + 2 : pos + 2 + tag_len]
if len(tag_value) < tag_len:
break
if tag_num == 0: # SSID
ssid = tag_value.decode('utf-8', errors='replace')
elif tag_num == 1: # Supported Rates
for rate_byte in tag_value:
rate = rate_byte & 0x7F
rates.append(rate / 2 if rate_byte & 0x80 else rate)
elif tag_num == 3: # DS Parameter Set
if tag_len >= 1:
channel = tag_value[0]
elif tag_num == 45: # HT Capabilities
if tag_len >= 2:
ht_cap = tag_value[1] # 第2字节包含信道宽度位
pos += 2 + tag_len
info['SSID'] = ssid if ssid is not None else '(隐藏)'
info['Supported Rates (Mbps)'] = rates
info['Channel'] = channel
if ht_cap is not None:
# 第1位为0表示20MHz,为1表示20/40MHz
info['HT Channel Width'] = '20/40 MHz' if (ht_cap & 0x01) else '20 MHz'
return info
def main():
if len(sys.argv) < 2:
print('用法: python beacon_parser.py <pcap文件路径>')
sys.exit(1)
beacons = parse_pcap(sys.argv[1])
if not beacons:
print('未找到Beacon帧')
return
print(f'共捕获 {len(beacons)} 个Beacon帧\n')
for i, frame in enumerate(beacons[:5]): # 最多显示前5个
info = parse_beacon(frame)
print(f'--- Beacon {i+1} ---')
for key, value in info.items():
print(f'{key}: {value}')
print()
if __name__ == '__main__':
main()
运行与验证
运行方式:
python beacon_parser.py capture.pcap
验证步骤:
- 准备一个包含Beacon帧的pcap文件(可用Wireshark抓包或从公开数据集获取)
- 运行脚本,观察输出是否包含SSID、BSSID、信道等字段
- 用Wireshark打开同一pcap文件,对比某个Beacon帧的解析结果:展开
IEEE 802.11 Beacon frame,检查SSID、BSSID、DS Parameter Set中的信道号是否与脚本输出一致
通过条件:脚本输出的BSSID与Wireshark中显示的Source/BSSID一致;SSID、信道号与Wireshark中Tag解析结果完全匹配;Supported Rates数值与Wireshark中列出的速率一致(注意Wireshark可能显示为Mbps值,需对比换算关系)。
常见问题
- pcap字节序问题:脚本已自动检测magic number判断字节序,若解析失败请确认pcap文件为标准格式
- Radiotap头:如果pcap文件包含Radiotap头(如使用monitor模式抓包),需在解析前跳过Radiotap头长度。本脚本假设链路层直接为802.11帧,若遇到解析异常可先检查pcap的Network字段
- 隐藏SSID:Tag 0长度为0时,SSID显示为“(隐藏)”