Files
cute-handshake-capture/handshake-capture-c6/handshake-capture-c6.ino
drjones 50338a6d5c Implement 4-phase capture architecture: observe, target, capture
- Phase 2 (Observation): passive client mapping from data frame headers,
  ClientInfo array per slot (up to 8 clients), 5s observation window
- Phase 3 (Targeting): unicast deauth burst to each discovered client
  instead of broadcast, reason code 7, 5 frames per client
- Phase 4 (Capture): timeout + retry loop cycles back to observation
  to refresh stale client list (2 cycles within 30s channel timeout)
- Fix EAPOL detection: compute LLC/SNAP offset from QoS vs non-QoS
  header length, verify AA-AA-03 SNAP prefix, skip protected frames
- Concurrency: portMUX spinlock for callback/main-loop shared state,
  no dynamic alloc in callback, pre-allocated 4KB PCAP buffer per slot
- Phase-aware LCD status display (mapping → capturing → EAPOL count)

Made-with: Cursor
2026-03-17 22:11:41 -07:00

112 lines
2.6 KiB
C++

/*
* WiFi Handshake Capture - ESP32-C6 1.47" LCD
* Multi-target capture: up to 3 networks per channel, deauth all, capture all.
* Display: green = not captured, red = captured.
*/
#include "Display_ST7789.h"
#include "LVGL_Driver.h"
#include "WiFi_Scanner.h"
#include "SD_Card.h"
#include "HandshakeCapture.h"
enum CaptureState {
STATE_SCANNING,
STATE_CAPTURING,
STATE_WAITING
};
CaptureState captureState = STATE_SCANNING;
unsigned long capture_start_time = 0;
const unsigned long CHANNEL_TIMEOUT_MS = 30000;
void setup() {
Serial.begin(115200);
LCD_Init();
Set_Backlight(100);
Lvgl_Init();
if (!SD_Init()) {
Ui_SetWifiStatus("SD: no card");
} else {
Ui_SetWifiStatus("SD OK");
}
handshakeCaptureInit();
WiFiScanner_Refresh();
captureState = STATE_SCANNING;
}
void loop() {
Timer_Loop();
handshakeCaptureProcessPending();
handshakeCaptureLoop();
switch (captureState) {
case STATE_SCANNING: {
if (getCaptureableCount() == 0) {
Ui_SetWifiStatus("Rescan");
WiFiScanner_Refresh();
delay(500);
break;
}
startMultiCapture();
if (is_capturing) {
captureState = STATE_CAPTURING;
capture_start_time = millis();
Ui_SetWifiStatus("Mapping clients");
} else {
Ui_SetWifiStatus("No targets");
delay(1000);
}
break;
}
case STATE_CAPTURING: {
bool any_active = false;
for (int i = 0; i < MAX_SLOTS; i++) {
if (slots[i].active) {
any_active = true;
break;
}
}
// Phase-aware status display
static CapturePhase last_shown_phase = PHASE_CAPTURING;
if (capturePhase != last_shown_phase) {
Ui_SetWifiStatus(capturePhase == PHASE_OBSERVING ? "Mapping clients" : "Capturing...");
last_shown_phase = capturePhase;
}
static int last_eapol = -1;
if (capturePhase == PHASE_CAPTURING && latest_eapol_count != last_eapol) {
char buf[64];
snprintf(buf, sizeof(buf), "EAPOL %d/2", latest_eapol_count);
Ui_SetWifiStatus(buf);
last_eapol = latest_eapol_count;
}
if (!any_active && getCaptureableCount() == 0) {
Ui_SetWifiStatus("Done");
stopCapture();
captureState = STATE_SCANNING;
delay(2000);
} else if (!any_active) {
stopCapture();
captureState = STATE_SCANNING;
} else if (millis() - capture_start_time > CHANNEL_TIMEOUT_MS) {
Ui_SetWifiStatus("Ch timeout");
stopCapture();
captureState = STATE_SCANNING;
delay(2000);
}
break;
}
case STATE_WAITING:
break;
}
delay(16);
}