Python用Socket嗅探WiFi帧却收到以太网帧(非802.11)的原因
解决WiFi监听模式下抓不到802.11信标帧的问题
嘿,我明白你现在的困惑了——明明已经把无线网卡切到监听模式,但用AF_PACKET的raw socket拿到的还是以太网帧,根本没法提取WiFi的SSID对吧?咱们先把问题根源说清楚,再给你改代码:
问题出在哪?
你用的socket.AF_PACKET + socket.ntohs(3)(也就是ETH_P_ALL)是用来抓有线以太网链路层帧的接口。哪怕无线网卡开了监听模式,Linux内核默认会把收到的802.11无线帧转换成以太网帧格式给上层应用,所以你拿到的永远是“伪装”成以太网帧的数据,碰不到原始的WiFi管理帧(比如信标帧)。
要抓原始的802.11帧,得换一种socket配置,还要专门解析802.11的帧结构。
解决方案步骤
1. 确认无线网卡真的在监听模式
先确保你的网卡已经正确切换到监听模式,比如用命令:
# 替换成你的无线网卡名,比如wlan0 sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up
用iw dev检查,看到type monitor就说明成功了。
2. 修改代码,抓原始802.11帧
我们需要修改socket的创建方式,然后添加解析802.11信标帧的逻辑。这里是修改后的完整代码:
import os import socket import struct def unpack_80211_frame(data): # 解析802.11帧控制字段(前2字节) frame_control = struct.unpack('!H', data[:2])[0] frame_type = (frame_control >> 2) & 0x03 # 0=管理帧, 1=控制帧, 2=数据帧 frame_subtype = (frame_control >> 4) & 0x0F # 信标帧属于管理帧,子类型为8 if frame_type == 0 and frame_subtype == 8: # 跳过帧头的固定部分(共16字节:帧控制、时长、3个MAC地址、序列号) pos = 16 # 遍历802.11的标签字段,找SSID标签(编号0) while pos < len(data): tag_num = struct.unpack('B', data[pos:pos+1])[0] tag_len = struct.unpack('B', data[pos+1:pos+2])[0] if tag_num == 0: # SSID标签的编号是0 # 提取SSID,解码成字符串(忽略编码错误) ssid = data[pos+2:pos+2+tag_len].decode('utf-8', errors='ignore') return ssid, frame_type, frame_subtype # 跳到下一个标签 pos += 2 + tag_len # 不是信标帧的话返回None return None, frame_type, frame_subtype if __name__ == '__main__': # 替换成你的监听模式接口名,比如wlan0mon或者wlan0 monitor_interface = "wlan0mon" # 创建能抓原始802.11帧的socket # PF_PACKET + SOCK_RAW + ETH_P_ALL(0x0003) 可以捕获所有链路层帧 conn = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0003)) # 绑定到监听模式的网卡接口 conn.bind((monitor_interface, 0)) print(f"开始监听{monitor_interface}上的WiFi信标帧...") while True: raw_data, addr = conn.recvfrom(65536) ssid, frame_type, frame_subtype = unpack_80211_frame(raw_data) if ssid: print(f"发现WiFi网络: *{ssid}*")
关键代码解释
- Socket创建:用
PF_PACKET(和AF_PACKET在Linux下等价,但更强调链路层抓包),绑定到监听模式的接口,这样就能拿到原始的802.11帧。 - 802.11帧解析:信标帧是管理帧的一种(类型0,子类型8),我们通过遍历帧里的标签字段找到SSID标签(编号0),提取对应的字符串。
- 权限要求:必须用root权限运行这个脚本,因为抓原始socket需要管理员权限。
额外注意事项
- 如果你的系统里网络管理器(比如NetworkManager)在运行,可能会干扰监听模式,建议临时关闭它:
sudo systemctl stop NetworkManager - 不是所有无线网卡都支持监听模式,尤其是一些内置的低功耗网卡,如果你发现抓不到帧,可能需要换支持监听的USB无线网卡。
内容的提问来源于stack exchange,提问作者Alicja Głowacka




