You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动