zero1byte/FrameHunter32
GitHub: zero1byte/FrameHunter32
Stars: 1 | Forks: 0
# ESP32 WPA2 Handshake Sniffer
A bare-metal ESP-IDF firmware for the ESP32 that passively captures WPA2 4-way EAPOL handshakes over Wi-Fi and outputs them in a format ready for offline password analysis with `aircrack-ng`.
## Features
- **Interactive UART shell** — type commands over the serial monitor at 115200 baud
- **AP scanner** — active scan listing SSID, BSSID, channel, security type, and RSSI
- **Beacon sniffer** — generic promiscuous capture of 802.11 beacon frames
- **Handshake capture** — two-phase engine that hunts a target beacon then captures the full WPA2 4-way EAPOL exchange
- **Deauthentication** — injects a raw 802.11 deauth frame to force a client to re-associate (triggering a handshake)
- **text2pcap output** — hex dump in a format that converts directly to `.pcap` for Wireshark and aircrack-ng
- **Ctrl+C abort** — press `Ctrl+C` at any time to cancel a running capture and return to the prompt
## Hardware Requirements
- ESP32 development board (any variant with Wi-Fi)
- USB–serial connection to a host running `idf.py monitor`
## Software Requirements
| Tool | Version |
|---|---|
| ESP-IDF | v5.x (v4.4+ also works) |
| Python | 3.8+ (IDF dependency) |
| text2pcap | Part of Wireshark — `sudo apt install wireshark-common` |
| aircrack-ng | `sudo apt install aircrack-ng` |
## Project Structure
.
├── main.c # app_main: UART init, NVS init, spawns CLI task
├── cli.c / .h # Interactive UART shell, argument parser, Ctrl+C abort
├── sniffer.c / .h # Generic promiscuous beacon capture engine
├── handshake.c / .h# Two-phase beacon + EAPOL capture engine
├── deauth.c / .h # Raw 802.11 deauth frame injection
├── wifi_mgr.c / .h # Wi-Fi driver lifecycle (idempotent init/start/stop)
└── CMakeLists.txt # IDF component registration
## Build & Flash
# Set up IDF environment (adjust path to your IDF installation)
. $IDF_PATH/export.sh
# Configure target
idf.py set-target esp32
# Build
idf.py build
# Flash and open serial monitor (adjust port as needed)
idf.py -p /dev/ttyUSB0 flash monitor
## Shell Commands
Once the monitor is open you will see the `esp32>` prompt.
### `help`
Print the command reference.
### `scan`
Run an active 802.11 scan and list all nearby access points with SSID, BSSID, channel, security type, and RSSI. Useful for finding the channel and BSSID of your target before running `handshake`.
esp32> scan
### `config`
Show the current persistent settings (channel, SSID, BSSID, STA MAC).
esp32> config
### `sniffer [options]`
Start a generic promiscuous capture of beacon frames on the configured channel. Stops after the requested number of frames (default 10) and dumps them in text2pcap format.
esp32> sniffer --channel 6 --ssid MyNetwork
esp32> sniffer --channel 6 --ssid "My Home Network" --count 20
esp32> sniffer --count 5
**Options**
| Option | Default | Description |
|---|---|---|
| `--ssid ` | (any) | Filter by SSID. Use quotes for SSIDs with spaces. |
| `--channel <1-13>` | 6 | 802.11 channel to listen on. |
| `--bssid ` | (any) | Filter by AP MAC address. |
| `--count <1-50>` | 10 | Number of beacon frames to capture before stopping. |
### `handshake [options]`
Two-phase blocking capture:
- **Phase 1** — hunts a beacon matching `--ssid` / `--bssid` on the specified channel
- **Phase 2** — switches to DATA-frame capture and collects the 4-way EAPOL exchange (M1–M4)
esp32> handshake --channel 1 --ssid MyNetwork
esp32> handshake --channel 6 --ssid "JioFiber Home" --bssid AA:BB:CC:DD:EE:FF
esp32> handshake --channel 6 --bssid AA:BB:CC:DD:EE:FF
Options are **persistent** — set them once and they apply to every subsequent command until changed:
esp32> handshake --channel 6
esp32> handshake --ssid JioFiber-A6ceM # channel 6 still applies
**SSIDs with spaces** must be wrapped in double or single quotes:
esp32> handshake --ssid "My Home Network"
esp32> sniffer --ssid 'Office WiFi 5G' --count 15
### `deauth --bssid --sta-mac `
Inject a raw 802.11 deauthentication frame targeting a specific client. Forces the client to re-associate, which triggers a new WPA2 handshake that `handshake` can capture.
esp32> deauth --bssid AA:BB:CC:DD:EE:FF --sta-mac 11:22:33:44:55:66
### Ctrl+C
Press **Ctrl+C** during any blocking command (`scan`, `sniffer`, `handshake`) to abort the capture cleanly and return to the `esp32>` prompt. Wi-Fi is torn down gracefully before returning.
## Capture Workflow
### Step 1 — Find the target
esp32> scan
Note the SSID, BSSID, and channel of your target AP.
### Step 2 — (Optional) Deauth a client to force a handshake
esp32> deauth --bssid AA:BB:CC:DD:EE:FF --sta-mac 11:22:33:44:55:66
### Step 3 — Capture the handshake
esp32> handshake --channel 6 --ssid MyNetwork
The shell will print:
[Phase 1] Hunting beacon on channel 6 for SSID "MyNetwork" ...
[Phase 1] Beacon captured!
BSSID : AA:BB:CC:DD:EE:FF
[Phase 2] Capturing EAPOL 4-way handshake (need 4 frames)...
[Phase 2] EAPOL frames: 1 / 4
[Phase 2] EAPOL frames: 2 / 4
[Phase 2] EAPOL frames: 3 / 4
[Phase 2] EAPOL frames: 4 / 4 -- done.
Followed by the hex dump in text2pcap format.
### Step 4 — Convert to pcap
Copy the hex dump from the serial monitor output into a file `capture.txt`, then:
text2pcap -F pcap -l 105 capture.txt out.pcap
The `-l 105` flag sets the link-layer type to IEEE 802.11 (raw, no radiotap header, no FCS).
### Step 5 — Crack the password
aircrack-ng out.pcap -w /usr/share/wordlists/rockyou.txt
## Architecture Notes
### Why two phases?
The ESP32 promiscuous filter can only pass one frame type at a time efficiently. Phase 1 uses `WIFI_PROMIS_FILTER_MASK_MGMT` to quickly find the target beacon and learn its BSSID. Phase 2 switches to `WIFI_PROMIS_FILTER_MASK_DATA` to receive the EAPOL DATA frames. The filter switch happens in the CLI task (not inside the callback) because ESP-IDF driver API calls must not be made from within the Wi-Fi task context.
### Why IRAM_ATTR on the callbacks?
The promiscuous callbacks run inside the Wi-Fi driver task. Placing them in IRAM avoids flash instruction-cache stalls during high frame-rate capture, which would otherwise cause frames to be dropped.
### Wrong-password resilience
The EAPOL state machine handles the case where the AP retransmits Message 1 after a failed password attempt (e.g. during a connection retry). When a sequence violation is detected, `eapol_sequence_reset()` rolls the frame buffer pointer back to just after the beacon so stale frames are overwritten rather than accumulating and filling the buffer. Without this fix the buffer fills on the failed attempt and `store_frame()` silently drops every frame of the subsequent correct handshake.
## Known Limitations
- The ESP32 2.4 GHz radio only supports channels 1–13. 5 GHz networks are not visible.
- `esp_wifi_80211_tx` used by `deauth` is an unofficial IDF API. It works on current IDF versions but is not part of the public stable API.
- The UART shell reads one character at a time; there is no command history or line editing beyond backspace.
- Captured frames are stored in RAM. The buffer holds a maximum of 10 frames for the sniffer and 5 frames (1 beacon + 4 EAPOL) for the handshake engine. Increasing `MAX_SNIFF_PACKETS` or `MAX_FRAME_SIZE` beyond available DRAM will cause a boot-time assertion failure.
## Legal Notice
This tool is intended for **authorised security testing and educational purposes only**. Capturing network traffic or injecting deauthentication frames on networks you do not own or have explicit written permission to test may be illegal in your jurisdiction. The authors accept no liability for misuse.
标签:客户端加密