- /cap_bssids.txt append on save; handshakeLoadPersistedBssids after SD mount - Boot order: SD then load file then first WiFi scan (reds match SD) - Pre-save walk of in-memory PCAP counting EAPOL-Key frames - SD_AppendLine / SD_ForEachLine; drop g_sd_ready clear on write fail - README + MUST_DO updates Made-with: Cursor
206 lines
5.6 KiB
C++
206 lines
5.6 KiB
C++
/*
|
|
* WiFi Handshake Capture - ESP32-C6 1.47" LCD
|
|
* Multi-target capture: up to 2 networks per channel (deauth + wait, rotate until all done).
|
|
* 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
|
|
};
|
|
|
|
CaptureState captureState = STATE_SCANNING;
|
|
unsigned long capture_start_time = 0;
|
|
|
|
// ─── 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/%d",
|
|
slot->ssid, (int)slot->eapol_count, (int)HANDSHAKE_EAPOL_FRAMES);
|
|
}
|
|
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 uint32_t last_eapol_sig = 0xffffffffu;
|
|
uint32_t sig = 0;
|
|
for (int s = 0; s < MAX_SLOTS; s++)
|
|
sig |= (uint32_t)latest_eapol_count[s] << (8 * s);
|
|
if (capturePhase == PHASE_CAPTURING && sig != last_eapol_sig) {
|
|
char ebuf[48];
|
|
int p = snprintf(ebuf, sizeof(ebuf), "Ch%d " LV_SYMBOL_CHARGE " E", current_channel);
|
|
for (int s = 0; s < MAX_SLOTS && p < (int)sizeof(ebuf) - 6; s++) {
|
|
p += snprintf(ebuf + p, sizeof(ebuf) - (size_t)p, "%s%d",
|
|
s ? "/" : "", (int)latest_eapol_count[s]);
|
|
}
|
|
Ui_SetWifiStatus(ebuf);
|
|
last_eapol_sig = sig;
|
|
}
|
|
}
|
|
|
|
// ─── Setup ───
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(200);
|
|
Serial.printf("handshake-capture-c6 v%s\n", HANDSHAKE_FIRMWARE_VERSION);
|
|
|
|
LCD_Init();
|
|
Set_Backlight(100);
|
|
Lvgl_Init();
|
|
|
|
// WiFi up before SD (shared SPI); first scan only after persistence load so reds match SD file.
|
|
handshakeCaptureInit();
|
|
|
|
HANDSHAKE_LOGLN("[SETUP] mounting SD after WiFi init");
|
|
if (!SD_Init()) {
|
|
Ui_SetWifiStatus("SD: no card");
|
|
WiFiScanner_Refresh();
|
|
} else {
|
|
Ui_SetWifiStatus("SD OK");
|
|
handshakeLoadPersistedBssids();
|
|
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;
|
|
}
|
|
if (!SD_IsReady()) {
|
|
HANDSHAKE_LOGLN("[CAP] SD not ready — calling SD_Init before capture");
|
|
SD_Init();
|
|
if (!SD_IsReady()) {
|
|
HANDSHAKE_LOGLN("[CAP] BLOCKED: SD_IsReady still false — UI: Insert SD card");
|
|
Ui_SetWifiStatus("Insert SD card");
|
|
delay(800);
|
|
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 > HANDSHAKE_CHANNEL_TIMEOUT_MS) {
|
|
Ui_SetWifiStatus("Ch timeout");
|
|
stopCapture();
|
|
captureState = STATE_SCANNING;
|
|
delay(2000);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
delay(16);
|
|
}
|