- Reduce time spent in capture spinlock by snapshotting slot BSSIDs and only locking for shared-state updates - Add PMKID KDE detection to save on EAPOL M1 when present - Sweep channels with remaining WPA targets instead of camping one channel Made-with: Cursor
187 lines
4.7 KiB
C++
187 lines
4.7 KiB
C++
/*
|
|
* WiFi Handshake Capture - ESP32-C6 1.47" LCD
|
|
* Multi-target capture: up to 3 networks per channel.
|
|
* Green = pending, orange blink = active target, 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 = 50000; // ~3 cycles (observe+deauth+capture)
|
|
|
|
// ─── Live capture visual update ───
|
|
|
|
void updateCaptureVisuals() {
|
|
static unsigned long last_vis = 0;
|
|
static bool blink = false;
|
|
|
|
unsigned long now = millis();
|
|
if (now - last_vis < 400) return;
|
|
last_vis = now;
|
|
blink = !blink;
|
|
|
|
// Overall progress: captured / total WPA targets
|
|
int captured = 0, total = 0;
|
|
for (int i = 0; i < MAX_NETWORKS; i++) {
|
|
if (networks[i].ssid.isEmpty()) break;
|
|
if (!isCaptureable(i)) continue;
|
|
total++;
|
|
if (networks[i].handshake_captured) captured++;
|
|
}
|
|
Ui_SetProgress(captured, total);
|
|
|
|
// Per-network visual state
|
|
for (int i = 0; i < MAX_NETWORKS; i++) {
|
|
if (networks[i].ssid.isEmpty()) break;
|
|
|
|
// Is this network in an active capture slot?
|
|
int si = -1;
|
|
for (int s = 0; s < MAX_SLOTS; s++) {
|
|
if (slots[s].active && slots[s].network_index == i) {
|
|
si = s;
|
|
break;
|
|
}
|
|
}
|
|
|
|
char buf[48];
|
|
|
|
if (networks[i].handshake_captured) {
|
|
// ✓ captured — red, static
|
|
snprintf(buf, sizeof(buf), LV_SYMBOL_OK " %s", networks[i].ssid.c_str());
|
|
Ui_SetNetworkText(i, buf);
|
|
Ui_SetNetworkColor(i, lv_palette_main(LV_PALETTE_RED));
|
|
}
|
|
else if (si >= 0) {
|
|
// ⚡ active target — orange, blinking
|
|
CaptureSlot* slot = &slots[si];
|
|
if (capturePhase == PHASE_OBSERVING) {
|
|
snprintf(buf, sizeof(buf), LV_SYMBOL_CHARGE " %s +%dc",
|
|
slot->ssid, slot->client_count);
|
|
} else {
|
|
snprintf(buf, sizeof(buf), LV_SYMBOL_CHARGE " %s %d/2",
|
|
slot->ssid, slot->eapol_count);
|
|
}
|
|
Ui_SetNetworkText(i, buf);
|
|
|
|
lv_color_t color = blink ?
|
|
lv_palette_main(LV_PALETTE_ORANGE) :
|
|
lv_palette_darken(LV_PALETTE_ORANGE, 2);
|
|
Ui_SetNetworkColor(i, color);
|
|
}
|
|
// else: pending — stays green from scan, no update needed
|
|
}
|
|
|
|
// Status line reflects current phase
|
|
static CapturePhase last_phase = PHASE_CAPTURING;
|
|
if (capturePhase != last_phase) {
|
|
if (capturePhase == PHASE_OBSERVING)
|
|
Ui_SetWifiStatus("Mapping clients...");
|
|
else
|
|
Ui_SetWifiStatus("Capturing...");
|
|
last_phase = capturePhase;
|
|
}
|
|
|
|
static int last_eapol = -1;
|
|
if (capturePhase == PHASE_CAPTURING && latest_eapol_count != last_eapol) {
|
|
char ebuf[32];
|
|
snprintf(ebuf, sizeof(ebuf), "Ch %d " LV_SYMBOL_CHARGE " EAPOL %d",
|
|
current_channel, latest_eapol_count);
|
|
Ui_SetWifiStatus(ebuf);
|
|
last_eapol = latest_eapol_count;
|
|
}
|
|
}
|
|
|
|
// ─── Setup ───
|
|
|
|
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;
|
|
}
|
|
|
|
// ─── Main loop ───
|
|
|
|
void loop() {
|
|
Timer_Loop();
|
|
handshakeCaptureProcessPending();
|
|
handshakeCaptureLoop();
|
|
|
|
switch (captureState) {
|
|
case STATE_SCANNING: {
|
|
Ui_ShowProgress(false);
|
|
if (getCaptureableCount() == 0) {
|
|
Ui_SetWifiStatus("Rescan");
|
|
WiFiScanner_Refresh();
|
|
delay(500);
|
|
break;
|
|
}
|
|
startMultiCapture();
|
|
if (is_capturing) {
|
|
captureState = STATE_CAPTURING;
|
|
capture_start_time = millis();
|
|
Ui_ShowProgress(true);
|
|
Ui_SetWifiStatus("Mapping clients...");
|
|
} else {
|
|
Ui_SetWifiStatus("No targets");
|
|
delay(1000);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case STATE_CAPTURING: {
|
|
updateCaptureVisuals();
|
|
|
|
bool any_active = false;
|
|
for (int i = 0; i < MAX_SLOTS; i++) {
|
|
if (slots[i].active) {
|
|
any_active = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!any_active && getCaptureableCount() == 0) {
|
|
Ui_SetWifiStatus(LV_SYMBOL_OK " All 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);
|
|
}
|