diff --git a/ESP32-DIV/ESP32-DIV.ino b/ESP32-DIV/ESP32-DIV.ino index 010d7a5b..2320fc36 100644 --- a/ESP32-DIV/ESP32-DIV.ino +++ b/ESP32-DIV/ESP32-DIV.ino @@ -19,6 +19,32 @@ TFT_eSPI tft = TFT_eSPI(); PCF8574 pcf(PCF8574_I2C_ADDR); +// BUGFIX: Safe button release wait with timeout +// Prevents infinite hang if a button gets stuck (e.g., hardware short, debris) +#define BUTTON_RELEASE_TIMEOUT_MS 5000 + +void waitForButtonRelease(uint8_t btn) { + unsigned long start = millis(); + while (isButtonPressed(btn)) { + if (millis() - start > BUTTON_RELEASE_TIMEOUT_MS) { + Serial.printf("[WARN] Button %d stuck - forcing release after %dms\n", btn, BUTTON_RELEASE_TIMEOUT_MS); + break; + } + delay(10); // Yield to FreeRTOS / watchdog + } +} + +void waitForButtonRelease2(uint8_t btn1, uint8_t btn2) { + unsigned long start = millis(); + while (isButtonPressed(btn1) || isButtonPressed(btn2)) { + if (millis() - start > BUTTON_RELEASE_TIMEOUT_MS) { + Serial.printf("[WARN] Buttons stuck - forcing release after %dms\n", BUTTON_RELEASE_TIMEOUT_MS); + break; + } + delay(10); + } +} + void setBrightness(uint8_t value) { ledcWrite(PWM_CHANNEL, value); } @@ -944,8 +970,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -978,8 +1003,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1012,8 +1036,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1046,8 +1069,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1080,8 +1102,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1114,8 +1135,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1147,8 +1167,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1208,8 +1227,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1240,8 +1258,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1272,8 +1289,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1304,8 +1320,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1336,8 +1351,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1368,8 +1382,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1400,8 +1413,7 @@ void handleWiFiSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1473,8 +1485,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1507,8 +1518,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1543,8 +1553,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1579,8 +1588,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1615,8 +1623,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1682,8 +1689,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1714,8 +1720,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1747,8 +1752,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1780,8 +1784,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1813,8 +1816,7 @@ void handleBluetoothSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1889,8 +1891,7 @@ void handleNRFSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1923,8 +1924,7 @@ void handleNRFSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -1984,8 +1984,7 @@ void handleNRFSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2016,8 +2015,7 @@ void handleNRFSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2090,8 +2088,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2125,8 +2122,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2160,8 +2156,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2223,8 +2218,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2256,8 +2250,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2289,8 +2282,7 @@ void handleSubGHzSubmenuButtons() { feature_exit_requested = false; displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); break; } } @@ -2328,8 +2320,7 @@ static void runToolsFeatureExitCleanup() { resetTouchNavHeldState(); displaySubmenu(); delay(200); - while (isButtonPressed(BTN_SELECT)) { - } + waitForButtonRelease(BTN_SELECT); } static void runToolsFeature(int idx, void (*setupFn)(), void (*loopFn)()) { @@ -2447,9 +2438,7 @@ void handleToolsSubmenuButtons() { static void otherDismissPlaceholder() { delay(25); - while (isButtonPressed(BTN_SELECT) || isButtonPressed(BTN_LEFT)) { - delay(5); - } + waitForButtonRelease2(BTN_SELECT, BTN_LEFT); while (!isButtonPressed(BTN_SELECT) && !isButtonPressed(BTN_LEFT)) { int x = 0, y = 0; if (!readTouchXYDismiss(x, y) && !readTouchXY(x, y)) { diff --git a/ESP32-DIV/bluetooth.cpp b/ESP32-DIV/bluetooth.cpp index cc88669b..3c760d5c 100644 --- a/ESP32-DIV/bluetooth.cpp +++ b/ESP32-DIV/bluetooth.cpp @@ -530,9 +530,10 @@ void Beats_Studio_Pro() { attack_state = 1; } -void Betas_Fit_Pro() { +void Beats_Fit_Pro() { device_choice = 0; device_index = 15; + attack_state = 1; } void Beats_Studio_Buds_Plus() { @@ -615,7 +616,7 @@ void setAdvertisingData() { Beats_Studio_Pro(); break; case 16: - Betas_Fit_Pro(); + Beats_Fit_Pro(); break; case 17: Beats_Studio_Buds_Plus(); @@ -703,8 +704,11 @@ void toggleAdvertising() { for (int i = 0; i < 6; i++) { dummy_addr[i] = random(256); if (i == 0) { - dummy_addr[i] |= 0xF0; + dummy_addr[i] |= 0xC0; // FIXED: was 0xF0, 0xC0 = valid BLE random static address } + // BUGFIX: Apply random MAC to the BLE stack (was never applied - OPSEC critical) + esp_ble_gap_set_rand_addr((uint8_t*)dummy_addr); + BLEDevice::setOwnAddrType(BLE_ADDR_TYPE_RANDOM); } BLEAdvertisementData oAdvertisementData = getAdvertismentData(); @@ -1053,7 +1057,7 @@ BLEAdvertisementData getOAdvertisementData() { packet[i++] = 0x05; packet[i++] = 0xC1; const uint8_t types[] = { 0x27, 0x09, 0x02, 0x1e, 0x2b, 0x2d, 0x2f, 0x01, 0x06, 0x20, 0xc0 }; - packet[i++] = types[rand() % sizeof(types)]; + packet[i++] = types[random(sizeof(types))]; esp_fill_random(&packet[i], 3); i += 3; packet[i++] = 0x00; @@ -1105,8 +1109,11 @@ void sourappleLoop() { for (int i = 0; i < 6; i++) { dummy_addr[i] = random(256); if (i == 0) { - dummy_addr[i] |= 0xF0; + dummy_addr[i] |= 0xC0; // FIXED: was 0xF0, 0xC0 = valid BLE random static address } + // BUGFIX: Apply random MAC to the BLE stack (was never applied - OPSEC critical) + esp_ble_gap_set_rand_addr((uint8_t*)dummy_addr); + BLEDevice::setOwnAddrType(BLE_ADDR_TYPE_RANDOM); } BLEAdvertisementData oAdvertisementData = getOAdvertisementData(); diff --git a/ESP32-DIV/gps.cpp b/ESP32-DIV/gps.cpp index 441c746c..53970d88 100644 --- a/ESP32-DIV/gps.cpp +++ b/ESP32-DIV/gps.cpp @@ -152,15 +152,65 @@ float navAltM = NAN; bool rmcNavValid = false; uint32_t lastNavMs = 0; +// BUGFIX: Validate NMEA checksum before accepting sentence +// Previously just stripped it without verification — corrupted GPS data was silently accepted +bool validateAndStripChecksum(char* s) { + // Remove trailing CR/LF first + char* cr = strchr(s, '\r'); + if (cr) *cr = '\0'; + char* lf = strchr(s, '\n'); + if (lf) *lf = '\0'; + + char* star = strchr(s, '*'); + if (!star) { + return false; // No checksum present — reject + } + + // Parse the expected checksum (2 hex digits after *) + uint8_t expected = 0; + if (star[1] && star[2]) { + char hexStr[3] = { star[1], star[2], '\0' }; + expected = (uint8_t)strtoul(hexStr, NULL, 16); + } else { + *star = '\0'; + return false; // Malformed checksum + } + + // Compute XOR checksum over everything between $ and * + uint8_t computed = 0; + char* p = s; + if (*p == '$' || *p == '!') p++; // Skip leading $ or ! (NMEA/AIS) + while (p < star) { + computed ^= (uint8_t)*p++; + } + + *star = '\0'; // Strip the checksum from the sentence + + if (computed != expected) { + Serial.printf("[GPS] Checksum mismatch\n"); + return false; + } + return true; +} + +// Legacy wrapper — maintains old interface but now validates void stripChecksum(char* s) { + // Try to validate; if it fails, still strip for best-effort parsing + // but log the error so bad data is visible in serial monitor char* star = strchr(s, '*'); if (star) { + // We have a checksum — validate it + char temp[256]; + strncpy(temp, s, sizeof(temp) - 1); + temp[sizeof(temp) - 1] = '\0'; + if (!validateAndStripChecksum(temp)) { + Serial.println("[GPS] WARNING: Bad NMEA checksum"); + } + // Still strip it from the original for backward compat *star = '\0'; } char* cr = strchr(s, '\r'); - if (cr) { - *cr = '\0'; - } + if (cr) *cr = '\0'; } int splitCommaFields(char* s, const char** fields, int maxFields) { diff --git a/ESP32-DIV/hardware_detect.h b/ESP32-DIV/hardware_detect.h new file mode 100644 index 00000000..fe33b3eb --- /dev/null +++ b/ESP32-DIV/hardware_detect.h @@ -0,0 +1,112 @@ +// hardware_detect.h — Module presence detection for ESP32-DIV +// IMPROVEMENT: Check for NRF24, CC1101, GPS, and IR hardware before launching features + +#ifndef HARDWARE_DETECT_H +#define HARDWARE_DETECT_H + +#include +#include +#include "shared.h" + +struct HardwareStatus { + bool nrf24_present = false; + bool cc1101_present = false; + bool gps_present = false; + bool ir_present = false; + bool pn532_present = false; + bool sd_present = false; + bool pcf8574_present = false; +}; + +extern HardwareStatus hwStatus; + +inline bool probeNRF24() { + pinMode(CSN_PIN_1, OUTPUT); + digitalWrite(CSN_PIN_1, LOW); + uint8_t status = SPI.transfer(0xFF); + digitalWrite(CSN_PIN_1, HIGH); + bool present = (status != 0x00 && status != 0xFF); + Serial.printf("[HW] NRF24 probe: STATUS=0x%02X -> %s\n", status, present ? "FOUND" : "NOT FOUND"); + return present; +} + +inline bool probeCC1101() { + pinMode(CC1101_CS, OUTPUT); + digitalWrite(CC1101_CS, LOW); + delayMicroseconds(10); + SPI.transfer(0x31 | 0xC0); + uint8_t version = SPI.transfer(0x00); + digitalWrite(CC1101_CS, HIGH); + bool present = (version == 0x14 || version == 0x04); + Serial.printf("[HW] CC1101 probe: VERSION=0x%02X -> %s\n", version, present ? "FOUND" : "NOT FOUND"); + return present; +} + +inline bool probeGPS(Stream& gpsSerial, unsigned long timeoutMs = 2000) { + unsigned long start = millis(); + while (millis() - start < timeoutMs) { + if (gpsSerial.available()) { + if (gpsSerial.read() == '$') { + Serial.println("[HW] GPS probe: NMEA data received -> FOUND"); + return true; + } + } + delay(10); + } + Serial.println("[HW] GPS probe: No NMEA data within timeout -> NOT FOUND"); + return false; +} + +inline bool probePCF8574(uint8_t addr) { + Wire.beginTransmission(addr); + uint8_t err = Wire.endTransmission(); + bool present = (err == 0); + Serial.printf("[HW] PCF8574 probe: addr=0x%02X -> %s\n", addr, present ? "FOUND" : "NOT FOUND"); + return present; +} + +inline void showHardwareStatus(TFT_eSPI& tft, int startY = 200) { + tft.setTextSize(1); + tft.setTextFont(1); + struct ModuleInfo { const char* name; bool present; }; + ModuleInfo modules[] = { + { "NRF24", hwStatus.nrf24_present }, + { "CC1101", hwStatus.cc1101_present }, + { "GPS", hwStatus.gps_present }, + { "PN532", hwStatus.pn532_present }, + { "SD Card", hwStatus.sd_present }, + }; + int y = startY; + tft.setTextColor(TFT_DARKGREY); + tft.drawString("Hardware:", 8, y); + y += 12; + for (auto& m : modules) { + tft.setTextColor(m.present ? TFT_GREEN : TFT_DARKGREY); + tft.drawString(m.present ? "+" : "-", 8, y); + tft.setTextColor(m.present ? TFT_WHITE : TFT_DARKGREY); + tft.drawString(m.name, 18, y); + y += 10; + } +} + +inline bool checkHardwareForFeature(TFT_eSPI& tft, const char* featureName, bool requiredHw) { + if (requiredHw) return true; + tft.fillScreen(TFT_BLACK); + tft.setTextColor(TFT_RED); + tft.setTextSize(1); + tft.setTextFont(2); + tft.drawString("Hardware Not Found", 20, 80); + tft.setTextColor(TFT_WHITE); + tft.setTextFont(1); + tft.drawString(featureName, 20, 110); + tft.drawString("requires hardware that was not", 20, 125); + tft.drawString("detected during startup.", 20, 138); + tft.setTextColor(TFT_YELLOW); + tft.drawString("Check wiring and restart.", 20, 160); + tft.setTextColor(TFT_DARKGREY); + tft.drawString("Press any button to go back", 20, 190); + delay(3000); + return false; +} + +#endif // HARDWARE_DETECT_H diff --git a/ESP32-DIV/wifi.cpp b/ESP32-DIV/wifi.cpp index ebcf85c3..67ef6cce 100644 --- a/ESP32-DIV/wifi.cpp +++ b/ESP32-DIV/wifi.cpp @@ -894,6 +894,15 @@ void ptmLoop() { deauths = 0; rssiSum = 0; } + +// IMPROVEMENT: Cleanup handler +void exit() { + esp_wifi_set_promiscuous(false); + esp_wifi_set_promiscuous_rx_cb(nullptr); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + Serial.println("[WiFi] PacketMonitor exit: promisc disabled, radio reset"); +} } namespace BeaconSpammer { @@ -1451,6 +1460,13 @@ void beaconSpamLoop() { } } } + +// IMPROVEMENT: Cleanup handler +void exit() { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + Serial.println("[WiFi] BeaconSpammer exit: stopped, radio reset"); +} } namespace DeauthDetect { @@ -2673,6 +2689,14 @@ void wifiscanLoop() { lastDetailView = true; } } + +// IMPROVEMENT: Cleanup handler +void exit() { + WiFi.scanDelete(); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + Serial.println("[WiFi] WifiScan exit: scan stopped, radio reset"); +} } namespace CaptivePortal {