简介:一款Python编写的WiFi密码破译小工具,聚焦暴力破解算法在无线网络审计中的应用,适合网络安全初学者与Python编程爱好者学习研究,作者明确声明仅限合法交流使用。压缩包内共2个文件:.py源码可阅读修改,.exe程序可免环境双击运行,整体约246.56MB;源码中体现了密码字典生成、多线程尝试、握手包验证等思路,exe则打包了完整交互界面,便于观察从输入WiFi名称到自动连接并输出密码的完整流程。目前已有7336人学习浏览,是热度较高的实战型小工具。通过研究该实现,读者既能理解暴力破解的时间复杂度与弱口令风险,也能学习Python调用无线网卡接口、多线程并发控制等实用技巧;作者另提供运行效率更优的多线程版本源码,适合想深入优化算法的进阶读者。务必注意,工具仅限用于自己设备和授权测试,切勿用于非法入侵。
1. python wifi密码破译器:别急着双击exe,它真正测的是自家网络的弱口令
拿到一个 python wifi密码破译器的压缩包,里面通常躺着两样东西:能直接双击的 exe 和一份完整源码。反直觉的一点是,exe 只是交付形式,真正决定你能不能改信道、换字典、调校验逻辑的是源码。这类工具能做的不是键入目标点一下出密码,而是把无线网卡切到监听模式,抓到路由器与客户端之间协商时的那几帧握手包,再用一批常见口令做离线验证。它适合拿来给自家路由器做弱口令审计,也适合无线测试人员二次开发。明确目标后会发现,密码是否被“破译”,往往取决于字典里有没有这个口令,以及你的网卡有没有把该抓的帧完整抓下来。
下面我按自己复现类似工具的习惯,把原理、实现、打包和排错拆开讲,既说清楚它怎么工作,也把常见坑列在最后。
2. 先搞懂 WPA2/WPA3 的握手机制,才知道“破译器”在验证什么
2.1 四次握手、PMK 与 PTK:一个字典候选要过三关
WPA2-PSK 并不是拿明文密码直接参与无线通信。路由器与客户端共享的是 PSK,实际通信密钥由它派生。第一步,用 PSK 作为输入,以 SSID 作为盐值,跑 4096 次 PBKDF2-HMAC-SHA1,得到 32 字节的 PMK。这一步是离线字典攻击的主要成本之一,Python 里一个候选密码大概要跑几毫秒,十万条字典就是几百秒,所以工具设计上要尽量复用同一份握手包,而不是反复抓包。
第二步是四次握手:路由器先发一个 ANonce,客户端收到后回应自己的 SNonce,同时附上 MIC 校验值;路由器拿到后发第三条消息携带 GTK,客户端再用第四条消息确认。要还原握手,需要记录 AP 的 MAC、客户端的 MAC、两个 nonce 和消息里携带的 MIC。字典里的每个候选密码要同时通过三关才算命中:PMK 派生结果一致、PTK 能和帧内信息协同、MIC 能被正确复算。任何一关因为字节序或者 MAC 排列出错,结果就是误判,这也是新手最容易对着“零命中”结果白费时间的地方。
2.2 WPA3 的 SAE 让离线验证变困难,但并非没有测试空间
路由器从 WPA2 切到 WPA3 之后,认证协议换成 SAE,基于 Dragonfly 密钥交换。SAE 的好处是即使攻击者抓到整个认证过程,也无法直接拿消息做离线字典验证,因为交换过程中引入的临时值和口令配合得足够紧密。很多现成的 wifi 密码测试工具在 WPA3 网络上会明显“变哑”,这是协议本身的进步,不是工具坏了。
但在测试自家网络时,WPA3 仍然可以通过两个途径观察:一是路由器在兼容模式常会同时开启 WPA2 的协商入口,老设备连接时自动降到 WPA2,这时又能抓到传统握手;二是 PMKID 这种由路由器在关联阶段主动暴露的派生值,在某些固件上仍可用于离线校验。对 Python 检测器来说,设计上应该保留两种解析分支,既能处理 802.11i 四次握手,也能识别 PMKID 字段,而不是只写死一条链路。
提示:本工具的使用范围严格限定为自家路由器、或你拥有书面授权测试的无线网络。越权扫描他人网络属于违法行为。
3. 从零搭一套 Python Wi-Fi 密码强度检测器:抓包、握手、字典校验
3.1 准备环境:监听模式网卡与依赖库
在确认网卡支持监听模式后,Linux 下可以用驱动配套命令把无线网卡从被管理模式切到监听模式,但最稳定的方案是直接在虚拟环境里安装抓包解析库和密码学库:
python -m venv wifiaudit source wifiaudit/bin/activate pip install scapy pycryptodome这里“抓包解析库”负责把无线电波里的 802.11 管理帧解析成对象,“密码学库”提供 PBKDF2、HMAC-SHA1 等底层运算。安装完成后,用iwconfig检查无线接口名,通常是 wlan0 或 wlan1。注意,如果无线网卡本身不支持 RF MONITOR 模式,后面所有步骤都跑不起来,这是整套工具的第一道硬门槛。笔记本自带的网卡里相当一部分只支持 managed 模式,尤其是部分省电型芯片,测试前要先确认。
3.2 扫描周边网络:从 Beacon 帧里读出 SSID、信道和加密位
最稳妥的切入点是写一个扫描器,把路由器周期性广播的 Beacon 帧收下来。Beacon 帧里带 SSID、BSSID、信道、支持的速率和加密信息。下面的函数会在指定网卡上监听一段时间,按 BSSID 去重:
from scapy.all import Dot11Beacon, Dot11Elt, sniff from collections import OrderedDict def scan_networks(iface, duration=30): nets = OrderedDict() def _handle(pkt): if not pkt.haslayer(Dot11Beacon): return bssid = pkt.addr2 ssid = pkt.info.decode(errors="ignore") cap = pkt[Dot11Beacon].cap channel = None elt = pkt[Dot11Elt] while elt: if elt.ID == 3: # DS 参数集元素装载信道编号 channel = elt.info[0] break elt = elt.payload nets[bssid] = { "ssid": ssid, "channel": channel, "privacy": bool(cap & 0x0010), # privacy 位,表示启用加密 } sniff(iface=iface, prn=_handle, store=0, timeout=duration) return list(nets.values())这段代码里值得注意的地方有三个:pkt.info.decode(errors="ignore")处理的是 SSID 里的中文和非 ASCII 字符,如果不忽略错误,一个乱码字节会让整个解析中断;cap 里的 privacy 位是 capabilities 字段的第 4 位,值为0x0010,与常见的短前导码等位要区分开;信道要从 DS 参数集元素去取,这个元素通常排在 Beacon 帧的固定位置附近,但不要硬编码偏移,正确做法是顺着 Dot11Elt 链表向后找。
3.3 抓取 EAPOL 握手包并保存 pcap
扫描拿到了目标信道后,把网卡固定到该信道,再用 BPF 过滤器直接锁定 EAPOL 帧,避免把无关流量全部写进内存。EAPOL 的以太网类型是 0x888e,抓包时只用这个过滤条件即可:
from scapy.all import sniff, wrpcap from scapy.layers.eap import EAPOL captured_frames = [] def _handle(pkt): if pkt.haslayer(EAPOL): eapol = pkt[EAPOL] if eapol.type == 0: # 0 表示握手消息 captured_frames.append(pkt) print(f"[+] 捕获握手帧: {pkt.addr2} -> {pkt.addr1}, len={len(pkt)}") sniff(iface=iface, filter="ether proto 0x888e", prn=_handle, store=0, timeout=60) wrpcap("handshake.pcap", captured_frames)捕获时机的选择比代码本身更关键。正常协商发生时,AP 与客户端之间有四次交互,但时间窗口很短。测试人员最常见的做法是在监听的同时触发一次客户端重连,比如让测试手机断开再连接。没有触发动作,空气里的 EAPOL 帧可能很久都不会出现。抓下来的握手帧能不能用于字典校验,取决于是否收齐了 ANonce、SNonce 和带 MIC 的那条消息,而不是总帧数。
3.4 字典校验:PBKDF2 派生 PMK,再用 HMAC 复算 MIC
最后一段逻辑从 pcap 里抽取关键字段,把字典条目逐一送进验证函数。核心代码如下:
import hashlib import hmac def derive_pmk(password, ssid): # WPA2-PSK 使用 4096 次迭代的 PBKDF2-HMAC-SHA1,输出 32 字节 return hashlib.pbkdf2_hmac("sha1", password.encode(), ssid.encode(), 4096, 32) def derive_ptk(pmk, ap_mac, client_mac, anonce, snonce): # 两个 MAC 按字节序排序后拼接,nonce 按 ANonce + SNonce 排列 mac_part = b"".join(sorted([ap_mac, client_mac])) nonce_part = anonce + snonce data = b"Pairwise key expansion" + b"\x00" + mac_part + nonce_part return _prf(pmk, data, 64) def _prf(key, data, length): # 802.11i 中的 PRF,以 HMAC-SHA1 按块扩展输出 out = b"" for i in range(0, (length + 19) // 20): out += hmac.new(key, data + bytes([i]), hashlib.sha1).digest() return out[:length] def check_candidate(candidate, ssid, ap_mac, client_mac, anonce, snonce, mic_expected, frame_body): pmk = derive_pmk(candidate, ssid) ptk = derive_ptk(pmk, ap_mac, client_mac, anonce, snonce) kck = ptk[:16] # KCK 是 PTK 的前 16 字节 mic_calc = hmac.new(kck, frame_body, hashlib.sha1).digest()[:16] return mic_calc == mic_expected, pmk这段代码的关键在参数语义,我按下面的规则处理,基本不会再出错:
- ap_mac、client_mac 必须是原始字节,不能是带冒号的字符串,传入之前先做一次转换。
- anonce、snonce 从 pcap 帧中的固定偏移读取,读取后保持原始字节顺序,不要做大小端反转。
- frame_body 必须是消息里 MIC 字段之前的那个完整 802.11 数据帧内容,若把包含 MIC 本身的原始帧直接传入,重算结果永远对不上。
对逐条验证的写法,建议先拿已知密码的小字典做回归测试,确认字节顺序、nonce 排列和 frame_body 切割方式都正确后再扩大字典。把百万条字典直接跑进去又遇到零命中时,你根本无法判断是字典没覆盖到,还是验证函数写错了。
4. 附带 exe 是如何生成的:打包配置与运行参数详解
4.1 打包配置:把源码、字典、解析依赖一并收拾干净
拿到源码后想生成附带的 exe,最常见路径是 PyInstaller。主程序入口需要常驻后台抓包,建议做一个main.py统一做参数解析,把扫描、抓包、校验三条子命令暴露到命令行。下面的 spec 文件把字典放进了可执行体内部,避免运行时找不到外部文件:
# wifiaudit.spec # -*- mode: python ; coding: utf-8 -*- a = Analysis( ['main.py'], pathex=['.'], binaries=[], datas=[('dicts/weak_passwords.txt', 'dicts')], hiddenimports=['scapy.layers.l2', 'scapy.layers.dot11', 'scapy.layers.eap'], hookspath=[], runtime_hooks=[], excludes=[], ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, name='WifiAudit', debug=False, strip=False, upx=True, console=True, uac_admin=True, )datas里的元组表示“源文件路径 -> 打包后相对路径”,字典在代码里用sys._MEIPASS/dicts/weak_passwords.txt访问;hiddenimports负责补上运行时才动态导入的 scapy 子层;uac_admin=True会为生成的可执行文件加上管理员权限请求,因为监听网卡和切换信道都需要系统权限。若不处理这一项,用户在双击 exe 时会遇到权限不足导致网卡初始化失败。
4.2 打包后的运行差异:onefile 与 onedir 怎么选
网上对“附exe与源码”的项目最大的误解,是把打包后 exe 当成了绿色免安装软件。实际上 PyInstaller 的 onefile 是把解压过程放在临时目录完成的,第一次启动慢、杀毒软件更容易报警;onedir 则生成一个文件夹,启动快、调整依赖方便。给用户分发时我一般推荐 onefile 加 console 窗口,让运行日志直接打在屏幕上,一旦抓包失败用户能立刻看到错误信息,而不是对着无界面程序猜它有没有在干活。
打包后还有个常见差异是路径。源码运行时,字典路径可能是./dicts/weak.txt;打包后的 exe 里,这一路径会变成以sys._MEIPASS为根的临时目录。写代码时最好先判断是否被打包:
import os import sys def resource_path(name): if hasattr(sys, "_MEIPASS"): return os.path.join(sys._MEIPASS, name) return os.path.join(os.path.dirname(__file__), name)这段逻辑把资源路径统一到一个入口,避免源码和 exe 两种状态下路径分叉。打包时注意,代码里用到的所有非 Python 资源都要显式声明在 datas 里,靠“复制到同目录”兜底的做法在 onefile 下必失败,因为解压出来的临时目录不一定和 exe 所在目录相同。
5. 避坑指南:这套 Wi-Fi 密码检测器最容易翻车的 5 个现场
5.1 网卡扫描一片空白,接口提示不支持监听模式
现象:运行扫描脚本后没有任何输出,用系统命令查看接口报告不支持 Monitor 模式。原因:部分无线网卡芯片的驱动固件根本没有开放监听模式,或者驱动版本过老无法完成切换。解决:先查网卡芯片型号,确认它支持 RFMON;Linux 下换用驱动配套的切换命令,再用iwconfig看 Mode 字段是否变成 Monitor。别指望 Python 层面能绕过这个限制,抓 802.11 帧必须由网卡暴露原生接口。
5.2 能扫到周边网络,但一直抓不到握手帧
现象:同一信道下扫描结果能列出多个路由器,但长时间监听只有 Beacon 帧。原因:没有客户端在目标网络上发起新连接,四次握手不会凭空出现。解决:在监听的同时让授权手机断开再重连 Wi-Fi,或者关闭路由器再打开。把抓包超时从 60 秒延长到 120 秒意义不大,没有触发动作时这个模式的命中率趋近于零。
5.3 字典明明包含正确密码,MIC 校验却始终失败
现象:拿一个已知密码的测试网络跑验证,字典中确有此密码,但验证函数返回 False。原因:帧体切割不对、nonce 拼错顺序、MAC 地址未转成原始字节,任一错误都会导致 MIC 无法复算。解决:解析 pcap 时先打印 PTK 前 16 字节和抓下的 MIC,用已知密码做回归,确认 ap_mac、client_mac、anonce、snonce 的排列顺序与协议规范一致。这个现场在调试中最难排查,误判时常让人错怪“字典不全”。
5.4 exe 打包后被杀毒软件拦截或运行后立刻退出
现象:双击 exe,安全软件弹出风险提示,允许运行后程序立刻闪退。原因:onefile 模式会增加压缩和自解压逻辑,行为特征接近带网络功能的工具,且打包时可能没有把运行时依赖完整带进去。解决:打包时排除调试信息,关闭 UPX 压缩,把console=True保留下来以便看到闪退前的报错;如果闪退来自缺模块,检查 hiddenimports 是否覆盖了运行时导入的层。不要因为一次误报就放弃 exe 方案,杀毒软件对未签名的网络抓包工具本来就判定为高风险。
5.5 字典跑了 100 万条没命中就下结论说“密码安全”
现象:一轮测试跑完,零命中,于是得出结论路由器很安全。原因:字典的覆盖范围不等于口令空间,常见弱口令和公开字典之外,一个独特的日常密码完全可能不在其中。解决:把结论写成“该密码未落入本字典,弱口令风险低”,而不是“无法破解”。把测试结果和字典大小一起留档,做周期性复核时才有可比性,每轮结果都记录是防止误判的基础。
6. 把检测工具变成弱口令常态化复核流程的三个习惯
拿到一套源码和 exe 之后,最大的价值不是跑一次“某某网络能不能破”,而是把它发展成自己网络的健康检查流程。我平时会做三件事。
第一,字典不追求大而全,追求贴着自己网络习惯定制。常见套路是“年份+姓名拼音+特殊符号”“纯数字八位”“手机号后八位”,这些在通用字典里覆盖得不好,需要自己用脚本去生成。把生成逻辑独立出来,每次按需生成新字典,避免把大量无关条目塞进去拖慢验证速度。
第二,保存的 pcap 文件按“日期+网卡+信道”重命名并留档。换新路由器、新机顶盒之后,可以回放旧握手包继续验证,不必每次都抱着电脑反复触发重连。最实用的组合是“握手包离线验证”和“在线重抓包验证”分开,前者检验密码碰撞,后者检验当前信号环境下能否拿到完整握手。
第三,验证结果一定要跟收包质量绑定。同一个网络,近距离测出“字典未命中”,隔一堵墙后再测可能因为重传帧损坏直接校验失败,这时候得出“密码变安全了”的结论就失真。每次测试前先看捕获会话里的 EAPOL 帧数,少于两个完整握手就直接重新触发重连,别拿残缺帧当依据。
最后提一个我自己的教训:最初我把所有逻辑都塞进一个脚本里,扫描、抓包、字典验证共用一份全局变量,改参数时总担心弄坏状态。后来拆成三个独立模块之后,既能单测,也能单独打包,这个习惯帮我不止一次避免了模块重写。离开这套流程前我还会提醒自己,“破译器”的使用边界是自家路由器和有书面授权的测试环境,测出弱口令之后及时改密码并开启路由器的防蹭网功能,才是这套工具真正的落脚点。希望帮到你。
本文还有配套的精品资源,点击获取