tworjaga/ESP32Gotchi

GitHub: tworjaga/ESP32Gotchi

一款运行在 ESP32 上的低成本自主式 Wi-Fi 握手包嗅探器与接入点记录器,能够独立捕获 WPA/WPA2 四步握手并输出带有 GPS 位置信息的 PCAP 和 CSV 日志。

Stars: 4 | Forks: 0

# ESP32Gotchi [![Platform](https://img.shields.io/badge/Platform-ESP32--WROOM--32-blue)](https://www.espressif.com/en/products/socs/esp32) [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![Build](https://img.shields.io/badge/Build-Arduino_IDE_2.x-orange)](https://www.arduino.cc/) [![Author](https://img.shields.io/badge/Author-tworjaga-lightgrey)](https://github.com/tworjaga) ## 概述 ESP32Gotchi 是一个独立的被动式 Wi-Fi 握手包嗅探器和 AP 记录器,其灵感来源于 Pwnagotchi 项目。它运行在价值约 10-15 欧元的硬件组合上,无需主机计算机,并直接将标准的 PCAP 文件和 CSV 日志写入 microSD 卡。从开机起,所有操作均自主进行。 该固件使用 FreeRTOS 运行五个独立任务、一个混杂模式 Wi-Fi 回调、符合 IEEE 802.11-2020 标准的 EAPOL 解析、一个专门用于防止 I/O 阻塞数据包处理的 SD 写入任务,以及一个用于地理位置标记的可选 GPS 任务。 ## 硬件 ### 物料清单 | 组件 | 规格 | 预估成本 | |-----------|--------------|--------------| | MCU | ESP32 DevKit V1, 30-pin, ESP32-WROOM-32 | ~5 EUR | | 显示屏 | 0.96″ SSD1306 OLED, 128×64, I2C (4-pin) | ~3 EUR | | 存储 | MicroSD SPI 模块,兼容 3.3V | ~1 EUR | | 按钮 | 轻触开关 | <0.50 EUR | | LED | 3mm 或 5mm LED + 220 Ω 电阻 | <0.50 EUR | | 电源(便携) | LiPo 3.7V + TP4056 USB-C 充电模块 | ~2 EUR | | **GPS (可选)** | **Neo-6M UART 模块** | **~3 EUR** | **总计:~10–15 EUR (包含 GPS)** ### 接线 **OLED — I2C** ``` ESP32 GPIO21 -> SDA ESP32 GPIO22 -> SCL ESP32 3.3V -> VCC ESP32 GND -> GND ``` **MicroSD — SPI** ``` ESP32 GPIO18 -> SCK ESP32 GPIO23 -> MOSI ESP32 GPIO19 -> MISO ESP32 GPIO5 -> CS ESP32 3.3V -> VCC ESP32 GND -> GND ``` **按钮 — GPIO4** ``` ESP32 GPIO4 -> Button -> GND (internal pull-up enabled in firmware) ``` **LED (可选)** ``` ESP32 GPIO2 -> 220 ohm resistor -> LED anode LED cathode -> GND ``` **GPS Neo-6M — UART2 (可选, v1.2.2)** ``` ESP32 GPIO16 -> Neo-6M TX (UART2 RX) ESP32 GPIO17 -> Neo-6M RX (UART2 TX) ESP32 3.3V -> Neo-6M VCC ESP32 GND -> Neo-6M GND ``` **电池电压检测 — ADC (可选, v1.2.2)** ``` Vbat -> 100k resistor -> GPIO34 -> 100k resistor -> GND (100k/100k divider: Vadc = Vbat / 2) ``` ### 供电选项 仅 USB(开发/测试台使用): ``` USB -> ESP32 DevKit V1 ``` 便携(电池供电): ``` LiPo 3.7V -> TP4056 -> ESP32 VIN ``` ## 硬件架构 ``` ESP32-WROOM-32 |-- OLED SSD1306 (I2C: GPIO21/22) |-- MicroSD module (SPI: GPIO18/19/23/5) |-- Tactile button (GPIO4, active-low) |-- Status LED (GPIO2, optional) |-- Neo-6M GPS (UART2: GPIO16/17, optional) ← v1.2.2 |-- Batt ADC divider (GPIO34, optional) ← v1.2.2 |-- LiPo + TP4056 (optional, portable) ``` ## 固件 ### 架构 五个具有明确核心绑定的 FreeRTOS 任务: | 任务 | 核心 | 优先级 | 栈 | 功能 | |------|------|----------|-------|----------| | `task_hop` | 0 | **6** | 2 KB | 在信道 1–11 之间循环切换,停留 200 ms。核心 0 上的最高优先级——保证运行。 | | `task_proc` | 0 | 5 | 6 KB | 从队列中提取数据包,解析 802.11/EAPOL,管理握手槽位。在空闲时段排空 AP 日志队列。 | | `task_write` | 0 | **4** | 6 KB | 接收(按槽位索引)已完成的握手包,将 PCAP 写入 SD 卡。排空 AP 日志队列,并每 60 秒写入一次统计信息 CSV。 | | `task_ui` | 1 | 1 | 4 KB | 每 200 ms 更新一次 OLED,处理 LED、按钮和电池 ADC 读取。 | | `task_gps` | 1 | 2 | 3 KB | 解析来自 UART2 的 NMEA 报文;更新 `g_gps` 结构体和软件 RTC。*(v1.2.2 新增)* | ### 内存布局 所有数据包存储均在启动时静态分配。运行时不使用 `malloc()` 或 `free()`(任务栈和一个 GPS 语句缓冲区是仅有的动态分配,仅在启动时执行一次)。 | 区域 | 大小 | 用途 | |--------|------|---------| | `pkt_pool_mem[32][1600]` | 51 200 B | 传输中的数据包缓冲区(由 `promisc_cb` 申请,由 `task_proc` 释放) | | `hs_raw_pool_mem[32][1600]` | 51 200 B | 握手帧存储(保留至 PCAP 写入完毕,然后释放) | | `g_hs[16]` 元数据 | ~640 B | 握手槽位状态(指向 `hs_raw_pool_mem` 的索引,而非帧数据) | | `g_ap_table[256][6]` | 1 536 B | AP 哈希表 | | **用户静态内存总计** | **~104 KB** | 完全在 Wi-Fi 协议栈之后剩余的约 200 KB 可用范围内 | ### EAPOL 检测 实现了 IEEE 802.11-2020 §12.7.2 key_info 位字段: | 消息 | Pairwise | ACK | MIC | Install | Secure | |---------|----------|-----|-----|---------|--------| | Msg 1 | 1 | 1 | 0 | 0 | 0 | | Msg 2 | 1 | 0 | 1 | 0 | 0 | | Msg 3 | 1 | 1 | 1 | 1 | 1 | | Msg 4 | 1 | 0 | 1 | 0 | 1 | 必须捕获到所有四个消息才能将握手标记为完成。不完整的槽位将在 15 秒后过期。 ### PCAP 输出 文件被写入 SD 卡的 `/handshakes/` 目录下。 命名规则:`hs__.pcap` 示例:`hs_aa_bb_cc_dd_ee_ff_3721.pcap` 当 GPS 时间可用时,PCAP 数据包时间戳将使用 GPS UTC 时钟,而不是 `millis()`。 格式:标准 libpcap(魔数 `0xa1b2c3d4`),网络类型 105 (IEEE 802.11)。文件无需转换即可直接在 Wireshark 中打开。 ### AP 日志输出 (v1.2.2 新增) 在信标和探测响应帧中看到的新 BSSID 会被记录到 `/aplog/aps.csv`: ``` timestamp,bssid,rssi_dbm,channel 2024-06-01T14:32:01Z,AA:BB:CC:DD:EE:FF,-62,6 2024-06-01T14:32:04Z,11:22:33:44:55:66,-74,11 ``` 当文件大小达到 4 MB 时,会自动轮转为 `aps_1.csv`。当 GPS 定位可用时,时间戳为 ISO-8601 UTC 格式;否则为运行时间字符串(`UP+00042s`)。 ### 统计日志输出 (v1.2.2 新增) 设备统计信息快照将每 60 秒追加到 `/stats/stats.csv`: ``` timestamp,ap_count,pkt_rate,free_heap,rssi_last,rssi_drops,gps_lat,gps_lon,gps_fix 2024-06-01T14:33:00Z,14,87,148320,-67,203,48.208176,16.373819,1 ``` 文件达到 4 MB 时将轮转为 `stats_1.csv`。 ### OLED 显示布局 显示屏现在有四个页面,通过短按按钮循环切换。 **第 1 页 — 统计** ``` (o_o) 1/4 HS: 12 CH: 6 AP: 34 PKT: 128 RSSI:-67 D:203 ``` **第 2 页 — GPS** ``` (o_o) 2/4 FIX: YES LAT:48.20818 LON:16.37382 ALT:183m SAT:8 HDOP:1.1 ``` **第 3 页 — SD** ``` (o_o) 3/4 SD: OK FREE:3821MB APS: 14KB STS: 2KB WR: OK ``` **第 4 页 — 系统** ``` (o_o) 4/4 HEAP:148320B UP: 01:22:07 PKT: 87/s DRP: 203 BAT: 78% ``` 表情状态: - `(o_o)` — 正常扫描中 - `(^o^)` — 正在收集 EAPOL 帧 - `(X_X)` — 错误(SD 卡缺失、空间不足) - `(-_-)` — 空闲 ### LED 模式 | 模式 | 含义 | |---------|---------| | 慢闪 (1 Hz) | 正常扫描中 | | 快闪 (5 Hz) | 正在进行握手捕获 | | 单次短闪 | 握手包已保存至 SD 卡 | | 3 次长闪 (2 s) | SD 卡错误 — 重复提示 | ### 按钮行为 | 按压时长 | 操作 | |---------------|--------| | 短按 (50 ms – 3 s) | 切换 OLED 页面 (统计 → GPS → SD → 系统 → 统计) | | 长按 (3 s – 6 s) | `ESP.restart()` | | 超长按 (≥ 6 s) | 进入深度睡眠(通过同一按钮唤醒) | ### GPS 集成 (v1.2.2 新增) 将 Neo-6M(或兼容)GPS 模块连接到 UART2 (GPIO16/GPIO17)。`task_gps` 会解析带有 NMEA 校验和验证的 `$GPRMC` 和 `$GPGGA` 语句。当存在有效的定位时: - `/aplog/aps.csv` 和 `/stats/stats.csv` 中的所有时间戳都将切换为 ISO-8601 UTC。 - PCAP 数据包记录的时间戳将使用 GPS UTC 而非 `millis()`。 - GPS 页面(第 2 页)将显示纬度、经度、海拔、卫星数量和 HDOP。 如果超过 10 秒未收到状态为 `A` 的有效 `$GPRMC` 报文,定位将被视为过期;固件会将 `FIX: YES` 降级为 `FIX: NO`,并停止报告坐标,直到下一次获得良好定位。如果在开机后 60 秒内未获得定位,将向 Serial 输出一次警告日志,并在没有 GPS 的情况下继续运行。 GPS 模块是可选的。如果未接线,`g_gps_available` 将保持为 `false`,并且所有时间戳将回退到运行时间字符串。无需修改代码。 ### 电池监控 (v1.2.2 新增) ### 启动自检 (v1.2.2 新增) 在启动时,固件会在启动任何任务之前,在 OLED 上运行可视化自检序列: ``` Self-test v1.2.2 SD: OK free: 3821MB ``` ``` Self-test v1.2.2 GPS: UART OK ``` ``` Self-test v1.2.2 HEAP: 152340B ``` ``` Self-test v1.2.2 WIFI: OK ``` ``` Cheapagotchi v1.2.2 Ready ``` 每个画面显示约 600 ms。如果 SD 卡检测失败,将显示 `SD: FAIL`,设备将在不记录 SD 日志的情况下继续运行。 ### 深度睡眠 (v1.2.2 新增) 超长按按钮(≥ 6 秒)会触发干净的关机序列: 1. 禁用混杂模式。 2. 冲刷写入队列(超时时间最多 2 秒)。 3. 挂起 `task_write`。 4. 干净地卸载 SD 卡。 5. OLED 显示 `SLEEP ZZZ / Hold BTN to wake`。 6. `esp_deep_sleep_start()` — 在 GPIO4 为 LOW(同一按钮)时唤醒。 ## 构建与烧录 ### 要求 - Arduino IDE 2.x 或 PlatformIO - 由 Espressif 提供的 ESP32 板包,版本 2.0.x 或更高 - U8g2 库(通过 Arduino Library Manager 安装) - GPS 或电池监控不需要额外的库 ### Arduino IDE 1. 安装板包:`File -> Preferences -> Additional Boards Manager URLs` 添加:`https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json` 2. 安装 U8g2:`Tools -> Manage Libraries -> search "U8g2"` 3. 板设置: Board : ESP32 Dev Module Partition scheme : Default 4MB with spiffs CPU Frequency : 240 MHz Flash mode : QIO Upload speed : 921600 4. 打开 `Cheapagotchi.ino`,编译并烧录。 ### PlatformIO ``` [env:esp32dev] platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 lib_deps = olikraus/U8g2 board_build.partitions = default.csv ``` ## SD 卡 - 格式:FAT32 - 建议最小容量:2 GB - 固件会在首次启动时自动创建 `/handshakes/`、`/aplog/` 和 `/stats/`。 - 每次写入 PCAP 前的最小可用空间检查:1 MB。如果空间低于阈值,设备将继续嗅探但跳过保存。 - CSV 文件在达到 4 MB 时轮转(`aps.csv` → `aps_1.csv`,`stats.csv` → `stats_1.csv`)。 - 如果 SD 卡缺失或发生故障,设备将在下一次重试周期重试初始化,并显示 `SD: ERR`。 ## Serial 调试输出 以 115200 波特率连接。输出示例: ``` [BOOT] ESP32 Cheapagotchi v1.2.2 [SD] OK [WIFI] promiscuous active [MEM] free heap: 152340 bytes [GPS] no fix within 60 s — continuing without fix [HS] aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 msg1 [HS] aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 msg2 [HS] aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 msg3 [HS] aa:bb:cc:dd:ee:ff -> 11:22:33:44:55:66 msg4 [HS] saved /handshakes/hs_aa_bb_cc_dd_ee_ff_3721.pcap total=1 [STAT] pkt/s=23 rssi=-67dBm drops=142 thr=-80dBm ap_log_drops=0 hs_pool_drops=0 [BTN] page -> 1 [SLEEP] entering deep sleep ``` ## 仓库结构 ``` ESP32Gotchi/ |-- Cheapagotchi.ino # Full firmware source |-- README.md |-- LICENSE |-- hardware/ | └── BOM.md # Bill of materials └── docs/ └── pcap_analysis.md # Notes on opening captures in Wireshark ``` ## 故障排除 | 症状 | 可能原因 | 解决方法 | |---------|-------------|-----| | 开机显示 `SD: ERR` | 未插入 SD 卡、接线错误、不是 FAT32 格式 | 检查 SPI 接线,重新格式化为 FAT32 | | OLED 空白 | I2C 地址不匹配或接线故障 | 检查 SDA/SCL,使用 I2C 扫描仪确认地址 0x3C | | 未捕获到握手包 | 附近没有发生 WPA2 4 次握手交换 | 使用测试 AP;基于 deauth 的抓包超出了本固件的范围 | | 设备反复重启 | 看门狗触发 — 任务挂起 | 检查 Serial 输出的最后一行日志;通过 Issues 提交报告 | | PCAP 无法在 Wireshark 中打开 | 写入损坏(保存期间断电) | 删除未完成的文件;确保电源稳定 | | 第 2 页显示 `GPS: N/A` | GPS 的 UART 未接线或模块无响应 | 检查 GPIO16/17 接线;GPS 是可选的 | | 60 秒后显示 `FIX: NO` | 看不到卫星 | 移步室外;等待首次定位(冷启动可能需要 1-2 分钟) | | `BAT: 0%` | 分压器未接线或引脚错误 | 检查 GPIO34 / 分压电阻;该功能需要硬件支持 | | Serial 显示 `ap_log_drops > 0` | AP 日志队列已满 — 信标密度过高 | 在密集的射频环境中属于正常现象;记录并未丢失,只是进行了限流 | ## 技术规格 | 参数 | 数值 | |-----------|-------| | MCU | Xtensa LX6 双核, 240 MHz | | RAM | 520 KB SRAM | | Wi-Fi | 802.11 b/g/n, 2.4 GHz | | 扫描信道 | 1 – 11 | | 信道停留时间 | 200 ms | | 数据包队列深度 | 32 个 | | 数据包池块 | 32 × 1 600 B (静态) | | 握手原始帧池块 | 32 × 1 600 B (静态) | | 最大并发握手槽位 | 16 | | 最大跟踪 AP 数量 | 192 (哈希表, 256 个桶) | | 握手槽位超时 | 15 s | | 新槽位速率限制 | 每 100 ms 1 个 | | RSSI 过滤阈值 | –80 dBm (可调) | | PCAP 网络类型 | 105 (802.11) | | GPS 波特率 | 9 600 (Neo-6M 默认值) | | GPS 定位过期超时 | 10 s | | 统计日志记录间隔 | 60 s | | CSV 轮转大小 | 4 MB | | 电池 ADC 采样 | 16 次采样平均值 | | 电池更新间隔 | 30 s | | 看门狗超时 | 30 s | | 最小 SD 可用空间 | 1 MB | | 运行时堆内存分配 | **0** (不包括一次性的 GPS 缓冲区) | ## 法律声明 此工具仅供**经过授权的安全研究和教育用途**使用。 在大多数司法管辖区,未经明确书面许可捕获您不拥有的网络的 Wi-Fi 握手包是违法的。 作者不对滥用行为承担任何责任。 ## 许可证 MIT — 详情请见 [LICENSE](LICENSE)。 ## 联系方式 作者:[@tworjaga](https://github.com/tworjaga) Telegram:[@smtrcv](https://t.me/smtrcv) Issues:[github.com/tworjaga/ESP32Gotchi/issues](https://github.com/tworjaga/ESP32Gotchi/issues)
标签:ESP32, FreeRTOS, UML, Wi-Fi嗅探, 数据抓包, 物联网, 网络安全, 防御绕过, 隐私保护