涂鸦 T5(ESP32-S3)抓 WPA2 握手 + 显卡 hashcat 自动化破解------全链路实战复盘
合规声明:本文所有操作仅在我自有/已获授权的家庭网络上进行。对未经授权的网络实施监听、去认证攻击、密码破解在大多数国家和地区属于违法行为。请读者仅将本文用于学习无线安全原理与防御目的。
一、效果一览(TL;DR)
| 环节 | 结果 |
|---|---|
| 目标 | 家庭热点(SSID:602W / 602陈,WPA2-PSK) |
| 抓包硬件 | 涂鸦 T5 开发板(ESP32-S3),约 ¥20 |
| 破解硬件 | 笔记本 RTX 5060 Laptop GPU |
| 最终结果 | 抓到完整 M1+M2 握手后,显卡 14 秒 跑出密码 12345678 |
| 显卡实测速度 | 486.0 kH/s (独显)/ 504.5 kH/s(含核显合计) |
一句话原理:单片机负责"偷听 + 踢人",显卡负责"猜密码"。全程不碰路由器本身,只听空气里的无线电波,然后离线暴力验证。
二、原理:单片机干什么,显卡干什么
2.1 单片机:监听器 + "踢人"的门卫
普通电脑网卡只收"发给自己的"数据,而 ESP32-S3 的 WiFi 芯片可以切到监听模式(promiscuous mode) ,像一台全频收音机,把锁定信道上空气中所有原始 802.11 帧都收下来。它干三件事:
- 扫描定位 :找到目标热点在哪个信道(实测
602W在信道 4,RSSI -57;602陈同信道 4,RSSI -50),锁定蹲守; - 踢人(发 deauth 去认证帧) :伪造一个"路由器叫你滚下线"的无线包发给在线设备。设备被踢后手机会自动重连------重连那一刻必须重新走一遍登录流程(四次握手),而这个流程是明文飞在空气里的;
- 抓拍:四次握手的帧飞过空气时被它抄下来,通过 USB 串口(115200 波特率)实时传回电脑,存成 pcap 文件。
它解不了任何密,就是个偷听器 + 门卫。电脑网卡在 Windows 上干不了这事(不能监听、不能发原始帧),二十块钱的 ESP32 反而是最顺手的工具。
2.2 显卡:离线验证机
关键点:密码从来不通过空气传输 。握手里飞的是密码算出来的"指纹"(MIC)。所以抓到的东西不能反推出密码,但可以验证任何一个猜测:
text
候选密码
│ PBKDF2-HMAC-SHA1(密码, SSID, 4096 轮迭代) ← 故意设计得慢
▼
PMK(成对主密钥)
│ + AP 的 MAC + 手机的 MAC + 两个随机数(ANonce/SNonce)
▼
PTK(临时密钥)→ 取前 16 字节 KCK
│ 用 KCK 对抄下来的 M2 帧算签名
▼
算出的 MIC vs 空气里抄下来的 MIC
一样 → 这个候选就是真密码
不一样 → 换下一个候选
每猜一次要算 4096 轮 SHA-1,CPU 一秒只能试几千个;显卡有几千个核心,同一道题上万份并发一起算(hashcat),速度快两个数量级。
2.3 为什么 M2 是唯一的钥匙
四次握手四个帧,能用来离线验证的只有 M1+M2 这一对:
| 帧 | 内容 | 能否用于破解 |
|---|---|---|
| M1(AP→设备) | ANonce,明文 | 提供一个随机数 |
| M2(设备→AP) | SNonce + MIC | 唯一同时带 SNonce 和可验证 MIC 的帧 |
| M3(AP→设备) | ANonce + MIC | 缺 SNonce |
| M4(设备→AP) | MIC,但 SNonce 字段全零 | 算不出 PTK |
M4 自己把 SNonce 清零了,所以 M3/M4 拿到手也没用。另外本目标的 M1 不带 PMKID(keydata 全空),PMKID 攻击路线也走不通------M2 是唯一的钥匙。这也是整个项目反复卡壳的地方:三次抓包(两次 M1+M3、一次 M1×3+M3+M4)全都单缺 M2。
三、硬件与软件环境
| 类别 | 型号/版本 |
|---|---|
| 抓包硬件 | 涂鸦 T5 开发板(ESP32-S3 内核),USB CDC 串口 COM25 @115200 |
| 显卡 | NVIDIA GeForce RTX 5060 Laptop(8GB)+ Intel UHD 770 核显 |
| 固件工具链 | PlatformIO Core 6.2.0,esptool(pio 内置 v4.11.0) |
| 破解工具 | hashcat 7.1.2(-m 22000 WPA-PBKDF2-PMKID+EAPOL) |
| 脚本语言 | Python 3.12 + pyserial |
目录结构(工作目录 D:\Desktop\wifi):
text
firmware/src/main.cpp ESP32-S3 嗅探固件(v8)
sniff_capture.py 串口 → pcap 采集
hs_extract.py pcap → hashcat 22000 哈希(含自测)
watch_m2.py 自动值守:监控 M2 → 提哈希 → 调 GPU → 记录结果
crack_run.py 手动分阶段破解入口
hashcat/hashcat-7.1.2/ hashcat 本体(必须在它自己的目录里运行)
wifi_dict_*.txt 等 密码本若干
四、系统架构
text
┌────────────────────────── ESP32-S3(涂鸦 T5)──────────────────────────┐
│ 扫描定位目标 SSID/BSSID → 锁信道(ch4) │
空气中的 802.11 帧 │ promiscuous 监听 → 只入队 EAPOL 候选帧 + 目标相关管理帧(队列 20 帧) │
─────────────────▶│ 学习在线 STA 的 MAC → 周期发 广播+单播 deauth 踢人 │
│ 见到 EAPOL 后 4 秒静默不发(防自残撞掉 M2) │
└───────────────┬──────────────────────────────────────────────────────┘
│ USB 串口 hex 行(带长度+XOR 校验)
▼
┌────────────────────────── Windows PC ─────────────────────────────────┐
│ sniff_capture.py:校验 → 组 pcap(LINKTYPE 105) │
│ watch_m2.py 每 45 秒轮询 pcap: │
│ 出现可配对的 M1+M2 → 调 hs_extract 生成 22000 哈希 │
│ → hashcat 11 阶段自动跑(字典→纯数字→手机号) │
│ → potfile 出现明文 → 写入 找到密码.txt │
│ (值守还负责:抓包进程死了自动拉起;按 BSSID 从日志取当前 SSID) │
└───────────────────────────────────────────────────────────────────────┘
五、固件设计(v7 → v8)
5.1 v7 诊断版:全链路计数器定位丢帧环节
v7 固件的核心是每 10 秒打一行全链路计数,把 M2 丢失环节定位到具体一段:
text
MSG cb=258596 badrx=0 mgmt=0 data=13242 eapol=0 m2=0 qdrop=0 printed=0 deauth=4599 uni=2292 stas=0
│ │ │ │ │ │ │ │ │ └ 在线 STA 数
│ │ │ │ │ │ │ └────── 单播 deauth 数
│ │ │ │ │ │ └ 打印帧数
│ │ │ │ │ └ 队列丢弃
│ │ │ │ └── 分类为 M2 的帧数 ★核心指标
│ │ │ └── 通过 BSSID 过滤的数据帧
│ │ └ 目标相关管理帧
│ └ FCS 等坏帧
└ 回调总进入
靠它发现了两个关键事实:data 一直涨但 eapol=0 说明设备根本没掉线重连;另一个热点接口翻转时 data 冻结、stas 归零。
5.2 v8 的三处关键改动
改动一:单播去认证。 v7 只发广播 deauth(收件人 FF:FF:FF:FF:FF:FF),现代手机普遍无视。v8 从通过 BSSID 过滤的数据帧里学习在线 STA 的 MAC (上行看 SA、下行看 DA),对每台设备定点单播踢:
cpp
static void learn_sta(const uint8_t* m) {
if (m[0] & 1) return; // 广播/组播不算
if (is_tgt_mac(m)) return; // AP 自己不算
// ...LRU 式 8 槽位表,2 分钟内有过数据的才算"在线"
}
// 踢人时:广播 + 对每台在线 STA 单播
send_deauth(tgt_bssid[t], NULL); // 广播
send_deauth(tgt_bssid[t], sta_mac[i]); // 单播定点
改动二:握手让路(防自残)。 去认证的发射窗口期间芯片听不到东西;四次握手全程才几十毫秒,v7 的 3 连发 deauth 正好可能蹭掉 M2------前两次抓包就是单缺 M2。v8 检测到目标 EAPOL 后 4 秒静默:
cpp
if (now - last_eapol_ms < EAPOL_QUIET_MS) {
delay(50); // 握手窗口内让路,不发射
}
改动三:扫描避让。 周期性重扫描要切信道,期间听不到帧。v8 在 EAPOL 活跃后 30 秒内顺延扫描(SCAN_AVOID_MS)。
其余保持 v7:队列 20 帧×400 字节、EAPOL 候选帧带 XOR 校验 hex 输出、逐帧元数据行(速率/MCS/重试/RSSI),输出格式与 PC 端管线完全兼容。
5.3 编译与烧录
bash
cd firmware
pio run # RAM 15.3% / Flash 18.6%
pio run -t upload --upload-port COM25 # 623680 字节,约 12.6 秒烧完
六、PC 侧管线
6.1 sniff_capture.py:串口 → pcap
- 解析固件的
EAPOL/BEACON/MGMThex 行,长度 + XOR 双校验,坏行丢弃并打印; - 写标准 pcap(
LINKTYPE_IEEE802_11 = 105),追加模式,进程重启不丢已抓帧; - 串口自动探测(不再写死 COM 号):优先原配置口,失败则枚举所有 USB 串口。
6.2 hs_extract.py:pcap → hashcat 22000 哈希
- 解析四次握手帧,按 keyinfo 位(ACK/MIC/Secure)分类 M1--M4;
- 配对同 BSSID + 同 STA 的 M1+M2,生成 hashcat 22000 行:
text
WPA*02*<MIC 32 hex>*<mac_ap>*<mac_sta>*<essid hex>*<anonce>*<eapol(MIC清零)>*00
- 踩过并修掉的 bug:固件对部分非 QoS 数据帧在 LLC 头前多带 2 字节填充,固定按 24 字节帧头解析会漏帧(一次 M3 就是这样丢的)。修复为 24/26 两种偏移都探测 LLC SNAP 头;
- 自带
--selftest:用已知密码合成握手 → 生成哈希 → hashcat 12 秒解出,验证整条链路。
6.3 watch_m2.py:自动值守
每 45 秒轮询一次 pcap,全自动闭环:
- 出现可配对的 M1+M2(且排除自家那台输错过密码的手机 MAC)→ 生成哈希文件;
- 依次跑 11 个 hashcat 阶段(见第七节),独立 potfile,每阶段日志落盘;
- potfile 出现明文 → 写入
找到密码.txt,全程事件 + 10 分钟心跳写入watchdog.log; - 自愈:抓包进程不在了自动重新拉起(PID 文件 + OpenProcess 探活,2 分钟退避);
- SSID 盐映射 :SSID 是 WPA 哈希的盐,热点会改名(
602陈↔602W,两个 BSSID),脚本从固件日志的 target 行按 BSSID 取当前 SSID,否则密码对了也解不出来。
值守进程本身用 WMI Win32_Process.Create 启动(见踩坑 3)。
七、破解阶段与字典规模
11 个阶段按"性价比"排序,命中即停:
| # | 阶段 | 内容 | 量级 |
|---|---|---|---|
| 01 | 字典 | wifi_dict_all.txt |
1,042,860 |
| 02 | 字典 | rockyou.txt |
14,344,391 |
| 03 | 字典 | dict_master.txt |
1,919,559 |
| 04 | 字典 | dict_cn_1m.txt |
1,000,000 |
| 05 | 字典 | dict_letter.txt |
47,603 |
| 06 | 掩码 | 6 位纯数字 ?d?d?d?d?d?d |
100 万 |
| 07 | 掩码 | 7 位纯数字 | 1,000 万 |
| 08 | 掩码 | 8 位纯数字 | 1 亿 |
| 09 | 掩码 | 9 位纯数字 | 10 亿 |
| 10 | 掩码 | 11 位手机号 -1 3456789,1?1?d?d?d?d?d?d?d?d?d |
70 亿 |
| 11 | 掩码 | 10 位纯数字 | 100 亿 |
全部密码本合计 19,588,441 行(去重前)。
八、性能实测(RTX 5060 Laptop,-m 22000)
实测方法:改一个字节 SSID 盐构造"永不可能命中"的哈希,跑完整 rockyou 读速度行。
| 设备 | 速度 |
|---|---|
| RTX 5060 Laptop(独显) | 486.0 kH/s ≈ 48.6 万次/秒 |
| Intel UHD 770(核显) | 18,535 H/s |
| 合计 | 504.5 kH/s ≈ 50 万次/秒 |
注意"一次"= 完整走一遍 PBKDF2-HMAC-SHA1 4096 轮 + PRF + MIC 计算,相当于每秒约 20 亿次 SHA-1 运算 。14,344,391 条的 rockyou 实测 20 秒跑完。
换算成攻击耗时(按 48.6 万次/秒):
| 密码空间 | 数量级 | 耗时 |
|---|---|---|
| 6 位纯数字 | 10⁶ | ~2 秒 |
| 7 位纯数字 | 10⁷ | ~21 秒 |
| 8 位纯数字 | 10⁸ | ~3.4 分钟 |
| 9 位纯数字 | 10⁹ | ~34 分钟 |
| 11 位手机号 | 7×10⁹ | ~4 小时 |
| 10 位纯数字 | 10¹⁰ | ~5.7 小时 |
结论很直白:纯数字密码在现代消费级显卡面前等于裸奔;字母+数字混合到 10 位以上、或用长随机密码,才是安全的。
九、踩坑清单
实战中踩过的坑,按时间线:
- 假阳性握手 :输错密码的手机也会算一个"签名"发出去,被显卡解出来的是你输错的那个数(本例中是
99999999)。判断标准:只发了 M2、没有 M3/M4 的握手大概率是失败尝试;真正联网成功的设备才会有 M3/M4。 - 结论只对单条握手有效:"已排除 8 位纯数字"只对当时那条握手成立,换了新握手(新设备/新密码)必须全部重跑。
- 后台进程静默死亡 :从终端
Start-Process启动的后台 Python 进程,会随命令结束被作业对象(Job Object)连带回收,日志戛然而止、无任何报错。解法:用 WMIWin32_Process.Create启动脱离作业对象,并在值守脚本里加进程自愈。 - hashcat 找不到 OpenCL 内核 :它按相对路径找
./OpenCL/,必须在 hashcat 自己的目录里运行(或设 cwd),否则秒退。 - 广播 deauth 被 PMF 无视 :对一台在线设备发了 2200+ 个单播 deauth 它都不掉线(
data照涨),基本可断定开了 PMF(802.11w)。此时主动踢人失效,只能等设备自然重连(锁屏唤醒、断流重试、热点开关都会触发)。 - 去认证发射窗口撞掉 M2:见 5.2 改动二。
- SSID 是哈希的盐 :热点改名(
602陈↔602W)会导致用旧 SSID 提的哈希永远解不出来。必须按 BSSID 动态取当前 SSID。 - 帧头长度想当然:非 QoS 数据帧的 LLC 头前可能多 2 字节填充,解析要按 LLC SNAP 魔数探测偏移而不是固定 24。
- 串口号会变:重新插拔/烧录后 COM 号可能漂移,采集脚本要自动探测。
十、实战结果
时间线(全部真实数据):
text
09-28 01:47 抓到自己手机输错密码的握手 → 解出 99999999(假阳性,排除)
09-28 09:13 抓到设备 fc:ab:d0:c5:73:99 的 M1+M3×4(缺 M2)
09-28 11:53 抓到设备 48:45:e6:6c:71:43 的 M1×3+M3+M4(缺 M2)
09-30 12:24 烧录 v8 固件:单播 deauth 生效(stas=1, uni 递增)
09-30 13:18 热点接口翻转(602W → 602陈),在线设备被甩下线(stas 1→0)
09-30 15:56:24 设备回连,v8 抓到完整 M1+M2 → 自动生成哈希
09-30 15:56:38 RTX 5060 从 104 万字典中解出密码(14 秒)
potfile 记录:
text
9ee0e9128048e907b6ca12d58b29c51b:7ac62b24e63e:fcabd0c57399:602W:12345678
可靠性判据:发 M2 的那台设备在此前(09-28 09:13)就成功完成过认证(抓到过它的 M3),说明它手机里存的是真密码,MIC 数学验证通过------排除了踩坑 1 的假阳性情形。
十一、延伸:为什么 WPA3 不怕这套
- 防离线猜测:WPA3-SAE 的握手设计使抓到的帧无法用来离线验证候选密码,显卡这套"猜+验"直接失效;
- PMF/802.11w:加密管理帧,伪造 deauth 被丢弃------本项目踩坑 5 已实际观察到;
- 防御建议:路由器开 WPA3(或 WPA2/WPA3 过渡)、开 PMF、密码 12 位以上随机组合、关闭 WPS。
本文基于真实家庭网络授权测试整理,文中 MAC/SSID 等信息已获家人同意发布。