667 lines
26 KiB
C
667 lines
26 KiB
C
/**
|
|
* @file simple_web_server.c
|
|
* @brief ESP32-C5 Toolkit Web Server with Dual-Band Deauth
|
|
*
|
|
* Features:
|
|
* - Multi-target selection (2.4GHz + 5GHz)
|
|
* - Real dual-band deauth attack
|
|
* - BSSID display for proper targeting
|
|
*/
|
|
|
|
#include "esp_http_server.h"
|
|
#include "esp_log.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_system.h"
|
|
#include "esp_chip_info.h"
|
|
#include "esp_mac.h"
|
|
#include "cJSON.h"
|
|
#include "deauth_engine.h"
|
|
#include "bt_scanner.h"
|
|
#include "driver/temperature_sensor.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
// Temperature sensor handle
|
|
static temperature_sensor_handle_t temp_sensor = NULL;
|
|
|
|
#define TAG "WebServer"
|
|
|
|
// Store last scan results for target selection
|
|
static wifi_ap_record_t scan_results[30];
|
|
static uint16_t scan_count = 0;
|
|
|
|
// Forward declare HTML
|
|
static const char* get_index_html(void);
|
|
|
|
// ============================================================================
|
|
// HANDLERS
|
|
// ============================================================================
|
|
|
|
static esp_err_t root_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "Serving index.html");
|
|
const char* html = get_index_html();
|
|
httpd_resp_set_type(req, "text/html");
|
|
httpd_resp_send(req, html, strlen(html));
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Enhanced scan with BSSID and security
|
|
static esp_err_t scan_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "WiFi scan requested");
|
|
|
|
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_wifi_scan_start(&scan_config, true);
|
|
|
|
scan_count = 30;
|
|
esp_wifi_scan_get_ap_records(&scan_count, scan_results);
|
|
|
|
cJSON *root = cJSON_CreateObject();
|
|
cJSON *networks = cJSON_CreateArray();
|
|
|
|
for (int i = 0; i < scan_count; i++) {
|
|
cJSON *ap = cJSON_CreateObject();
|
|
|
|
// SSID
|
|
cJSON_AddStringToObject(ap, "ssid", (char*)scan_results[i].ssid);
|
|
|
|
// BSSID as string
|
|
char bssid[18];
|
|
snprintf(bssid, sizeof(bssid), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
scan_results[i].bssid[0], scan_results[i].bssid[1],
|
|
scan_results[i].bssid[2], scan_results[i].bssid[3],
|
|
scan_results[i].bssid[4], scan_results[i].bssid[5]);
|
|
cJSON_AddStringToObject(ap, "bssid", bssid);
|
|
|
|
cJSON_AddNumberToObject(ap, "rssi", scan_results[i].rssi);
|
|
cJSON_AddNumberToObject(ap, "channel", scan_results[i].primary);
|
|
cJSON_AddStringToObject(ap, "band", scan_results[i].primary > 14 ? "5GHz" : "2.4GHz");
|
|
|
|
// Security
|
|
const char* security;
|
|
switch (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);
|
|
cJSON_AddNumberToObject(ap, "index", i);
|
|
|
|
cJSON_AddItemToArray(networks, ap);
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "networks", networks);
|
|
cJSON_AddNumberToObject(root, "count", scan_count);
|
|
|
|
char *json_str = cJSON_PrintUnformatted(root);
|
|
httpd_resp_set_type(req, "application/json");
|
|
httpd_resp_send(req, json_str, strlen(json_str));
|
|
|
|
free(json_str);
|
|
cJSON_Delete(root);
|
|
|
|
ESP_LOGI(TAG, "Found %d networks", scan_count);
|
|
return ESP_OK;
|
|
}
|
|
|
|
// System info with attack status and ESP32-C5 features
|
|
static esp_err_t sysinfo_handler(httpd_req_t *req) {
|
|
cJSON *root = cJSON_CreateObject();
|
|
|
|
// Chip info
|
|
esp_chip_info_t chip_info;
|
|
esp_chip_info(&chip_info);
|
|
cJSON_AddStringToObject(root, "chip", "ESP32-C5");
|
|
cJSON_AddNumberToObject(root, "cores", chip_info.cores);
|
|
cJSON_AddNumberToObject(root, "revision", chip_info.revision);
|
|
|
|
// Memory
|
|
cJSON_AddNumberToObject(root, "heap", esp_get_free_heap_size());
|
|
cJSON_AddNumberToObject(root, "min_heap", esp_get_minimum_free_heap_size());
|
|
|
|
// Temperature sensor
|
|
float temp_celsius = 0;
|
|
if (temp_sensor == NULL) {
|
|
temperature_sensor_config_t temp_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
|
|
temperature_sensor_install(&temp_config, &temp_sensor);
|
|
}
|
|
if (temp_sensor) {
|
|
temperature_sensor_enable(temp_sensor);
|
|
temperature_sensor_get_celsius(temp_sensor, &temp_celsius);
|
|
temperature_sensor_disable(temp_sensor);
|
|
}
|
|
cJSON_AddNumberToObject(root, "temp", (int)(temp_celsius * 10) / 10.0);
|
|
|
|
// MAC address
|
|
uint8_t mac[6];
|
|
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
|
char mac_str[18];
|
|
snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
cJSON_AddStringToObject(root, "mac", mac_str);
|
|
|
|
// Features
|
|
cJSON_AddStringToObject(root, "features", "WiFi6,BLE5,Thread");
|
|
cJSON_AddStringToObject(root, "version", "2.0.0");
|
|
cJSON_AddBoolToObject(root, "attacking", deauth_is_running());
|
|
|
|
// Get attack stats if running
|
|
if (deauth_is_running()) {
|
|
uint32_t total, p24, p5, elapsed;
|
|
deauth_get_stats(&total, &p24, &p5, &elapsed);
|
|
cJSON_AddNumberToObject(root, "packets_total", total);
|
|
cJSON_AddNumberToObject(root, "packets_24ghz", p24);
|
|
cJSON_AddNumberToObject(root, "packets_5ghz", p5);
|
|
cJSON_AddNumberToObject(root, "elapsed", elapsed);
|
|
}
|
|
|
|
char *json_str = cJSON_PrintUnformatted(root);
|
|
httpd_resp_set_type(req, "application/json");
|
|
httpd_resp_send(req, json_str, strlen(json_str));
|
|
|
|
free(json_str);
|
|
cJSON_Delete(root);
|
|
return ESP_OK;
|
|
}
|
|
|
|
// REAL deauth start - parses JSON with targets
|
|
static esp_err_t deauth_start_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "Deauth start requested");
|
|
|
|
// Read POST body
|
|
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';
|
|
|
|
ESP_LOGI(TAG, "Received: %s", buf);
|
|
|
|
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 *bssid = cJSON_GetObjectItem(t24, "bssid");
|
|
cJSON *ssid = cJSON_GetObjectItem(t24, "ssid");
|
|
cJSON *channel = cJSON_GetObjectItem(t24, "channel");
|
|
|
|
if (cJSON_IsString(bssid) && cJSON_IsNumber(channel)) {
|
|
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;
|
|
if (cJSON_IsString(ssid)) {
|
|
strncpy(target_24.ssid, ssid->valuestring, sizeof(target_24.ssid) - 1);
|
|
}
|
|
target_24.active = true;
|
|
ESP_LOGI(TAG, "2.4GHz target: %s CH:%d", target_24.ssid, target_24.channel);
|
|
}
|
|
}
|
|
|
|
// Parse 5GHz target
|
|
cJSON *t5 = cJSON_GetObjectItem(root, "target_5ghz");
|
|
if (t5) {
|
|
cJSON *bssid = cJSON_GetObjectItem(t5, "bssid");
|
|
cJSON *ssid = cJSON_GetObjectItem(t5, "ssid");
|
|
cJSON *channel = cJSON_GetObjectItem(t5, "channel");
|
|
|
|
if (cJSON_IsString(bssid) && cJSON_IsNumber(channel)) {
|
|
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;
|
|
if (cJSON_IsString(ssid)) {
|
|
strncpy(target_5.ssid, ssid->valuestring, sizeof(target_5.ssid) - 1);
|
|
}
|
|
target_5.active = true;
|
|
ESP_LOGI(TAG, "5GHz target: %s CH:%d", target_5.ssid, target_5.channel);
|
|
}
|
|
}
|
|
|
|
// Get duration (default 30 seconds)
|
|
cJSON *dur = cJSON_GetObjectItem(root, "duration");
|
|
uint32_t duration = cJSON_IsNumber(dur) ? dur->valueint : 30;
|
|
|
|
cJSON_Delete(root);
|
|
|
|
// Start the REAL 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) {
|
|
ESP_LOGI(TAG, "Attack started for %lu seconds", duration);
|
|
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"Attack started\"}");
|
|
} else {
|
|
ESP_LOGW(TAG, "Failed to start attack");
|
|
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Failed to start - check targets\"}");
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Stop attack
|
|
static esp_err_t deauth_stop_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "Deauth stop requested");
|
|
deauth_stop_attack();
|
|
httpd_resp_set_type(req, "application/json");
|
|
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"Attack stopped\"}");
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Attack status endpoint
|
|
static esp_err_t deauth_status_handler(httpd_req_t *req) {
|
|
cJSON *root = cJSON_CreateObject();
|
|
|
|
bool running = deauth_is_running();
|
|
cJSON_AddBoolToObject(root, "running", running);
|
|
|
|
if (running) {
|
|
uint32_t total, p24, p5, elapsed;
|
|
deauth_get_stats(&total, &p24, &p5, &elapsed);
|
|
cJSON_AddNumberToObject(root, "packets_total", total);
|
|
cJSON_AddNumberToObject(root, "packets_24ghz", p24);
|
|
cJSON_AddNumberToObject(root, "packets_5ghz", p5);
|
|
cJSON_AddNumberToObject(root, "elapsed", elapsed);
|
|
}
|
|
|
|
char *json_str = cJSON_PrintUnformatted(root);
|
|
httpd_resp_set_type(req, "application/json");
|
|
httpd_resp_send(req, json_str, strlen(json_str));
|
|
|
|
free(json_str);
|
|
cJSON_Delete(root);
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Bluetooth scan start (NimBLE)
|
|
static esp_err_t bt_scan_start_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "BLE scan start requested");
|
|
httpd_resp_set_type(req, "application/json");
|
|
|
|
bool success = bt_scan_start();
|
|
if (success) {
|
|
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"BLE scan started\"}");
|
|
} else {
|
|
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Failed to start BLE scan\"}");
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Bluetooth scan stop
|
|
static esp_err_t bt_scan_stop_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "BLE scan stop requested");
|
|
httpd_resp_set_type(req, "application/json");
|
|
|
|
bool success = bt_scan_stop();
|
|
if (success) {
|
|
httpd_resp_sendstr(req, "{\"status\":\"ok\",\"message\":\"BLE scan stopped\"}");
|
|
} else {
|
|
httpd_resp_sendstr(req, "{\"status\":\"error\",\"message\":\"Failed to stop BLE scan\"}");
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Bluetooth devices list
|
|
static esp_err_t bt_devices_handler(httpd_req_t *req) {
|
|
ESP_LOGI(TAG, "BLE devices requested");
|
|
|
|
bt_device_t bt_devs[30];
|
|
int count = bt_get_devices(bt_devs, 30);
|
|
|
|
cJSON *root = cJSON_CreateObject();
|
|
cJSON *device_array = cJSON_CreateArray();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
cJSON *dev = cJSON_CreateObject();
|
|
|
|
// MAC address
|
|
char addr[18];
|
|
snprintf(addr, sizeof(addr), "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
bt_devs[i].addr[0], bt_devs[i].addr[1],
|
|
bt_devs[i].addr[2], bt_devs[i].addr[3],
|
|
bt_devs[i].addr[4], bt_devs[i].addr[5]);
|
|
cJSON_AddStringToObject(dev, "addr", addr);
|
|
cJSON_AddStringToObject(dev, "name", bt_devs[i].name[0] ? bt_devs[i].name : "Unknown");
|
|
cJSON_AddNumberToObject(dev, "rssi", bt_devs[i].rssi);
|
|
cJSON_AddStringToObject(dev, "type", "BLE");
|
|
|
|
cJSON_AddItemToArray(device_array, dev);
|
|
}
|
|
|
|
cJSON_AddItemToObject(root, "devices", device_array);
|
|
cJSON_AddNumberToObject(root, "count", count);
|
|
|
|
char *json_str = cJSON_PrintUnformatted(root);
|
|
httpd_resp_set_type(req, "application/json");
|
|
httpd_resp_send(req, json_str, strlen(json_str));
|
|
free(json_str);
|
|
cJSON_Delete(root);
|
|
|
|
ESP_LOGI(TAG, "Returned %d BLE devices", count);
|
|
return ESP_OK;
|
|
}
|
|
|
|
// ============================================================================
|
|
// URI DEFINITIONS
|
|
// ============================================================================
|
|
|
|
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_sysinfo = {.uri = "/api/system-info", .method = HTTP_GET, .handler = sysinfo_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_deauth_status = {.uri = "/api/deauth/status", .method = HTTP_GET, .handler = deauth_status_handler};
|
|
static httpd_uri_t uri_bt_scan_start = {.uri = "/api/bt/scan/start", .method = HTTP_GET, .handler = bt_scan_start_handler};
|
|
static httpd_uri_t uri_bt_scan_stop = {.uri = "/api/bt/scan/stop", .method = HTTP_GET, .handler = bt_scan_stop_handler};
|
|
static httpd_uri_t uri_bt_devices = {.uri = "/api/bt/devices", .method = HTTP_GET, .handler = bt_devices_handler};
|
|
|
|
// ============================================================================
|
|
// WEB SERVER
|
|
// ============================================================================
|
|
|
|
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 = 15;
|
|
|
|
ESP_LOGI(TAG, "Starting web server on port %d", config.server_port);
|
|
|
|
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_sysinfo);
|
|
httpd_register_uri_handler(server, &uri_deauth_start);
|
|
httpd_register_uri_handler(server, &uri_deauth_stop);
|
|
httpd_register_uri_handler(server, &uri_deauth_status);
|
|
httpd_register_uri_handler(server, &uri_bt_scan_start);
|
|
httpd_register_uri_handler(server, &uri_bt_scan_stop);
|
|
httpd_register_uri_handler(server, &uri_bt_devices);
|
|
|
|
ESP_LOGI(TAG, "Web Server started at http://192.168.4.1");
|
|
} else {
|
|
ESP_LOGE(TAG, "Failed to start web server!");
|
|
}
|
|
|
|
return server;
|
|
}
|
|
|
|
void stop_webserver(void) {
|
|
if (server) {
|
|
httpd_stop(server);
|
|
server = NULL;
|
|
}
|
|
}
|
|
|
|
void init_web_server(void) {
|
|
start_webserver();
|
|
}
|
|
|
|
// ============================================================================
|
|
// HTML - Multi-target selection with Channel Graph
|
|
// ============================================================================
|
|
|
|
static const char* get_index_html(void) {
|
|
static const char html[] =
|
|
"<!DOCTYPE html>"
|
|
"<html><head>"
|
|
"<meta charset='UTF-8'>"
|
|
"<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
|
|
"<title>ESP32-C5 Toolkit</title>"
|
|
"<style>"
|
|
"*{margin:0;padding:0;box-sizing:border-box}"
|
|
"body{font-family:'Courier New',monospace;background:#0a0a0a;color:#0f0;padding:15px}"
|
|
"h1{text-align:center;margin-bottom:15px;text-shadow:0 0 10px #0f0}"
|
|
"h3{margin:10px 0 5px;color:#0f0}"
|
|
".container{max-width:900px;margin:0 auto}"
|
|
".panel{background:#111;border:1px solid #0f0;padding:15px;margin:10px 0;border-radius:5px}"
|
|
".btn{background:#0f0;color:#000;border:none;padding:8px 15px;cursor:pointer;font-family:inherit;margin:3px;border-radius:3px;font-weight:bold}"
|
|
".btn:hover{background:#0c0}"
|
|
".btn-danger{background:#f00;color:#fff}"
|
|
".btn-danger:hover{background:#c00}"
|
|
".btn-warn{background:#f80;color:#000}"
|
|
"#networks{max-height:250px;overflow-y:auto}"
|
|
".network{padding:8px;border:1px solid #333;margin:3px 0;cursor:pointer;font-size:0.9em}"
|
|
".network:hover{border-color:#0f0}"
|
|
".network.selected-24{background:#003;border-color:#08f}"
|
|
".network.selected-5{background:#330;border-color:#f80}"
|
|
".ssid{font-weight:bold}"
|
|
".bssid{color:#888;font-size:0.85em}"
|
|
".info{color:#888;font-size:0.85em}"
|
|
".band-24{color:#08f}.band-5{color:#f80}"
|
|
".targets{display:flex;gap:10px;flex-wrap:wrap;margin:10px 0}"
|
|
".target-card{flex:1;min-width:200px;padding:10px;border-radius:5px}"
|
|
".target-24{background:#002;border:2px solid #08f}"
|
|
".target-5{background:#220;border:2px solid #f80}"
|
|
".status{padding:10px;background:#001;border:1px solid #0f0;margin-top:10px}"
|
|
".attack-running{color:#f00;animation:blink 1s infinite}"
|
|
"@keyframes blink{50%{opacity:0.5}}"
|
|
".warn{background:#220;border:1px solid #f80;padding:10px;margin:10px 0;color:#fa0}"
|
|
"input[type=number]{background:#222;border:1px solid #0f0;color:#0f0;padding:5px;width:60px}"
|
|
".channel-graph{display:flex;align-items:flex-end;height:60px;gap:2px;padding:5px 0;border-bottom:1px solid #333}"
|
|
".channel-bar{flex:1;background:#0f0;min-width:12px;transition:height 0.3s;min-height:3px;border-radius:2px 2px 0 0}"
|
|
".channel-bar:hover{background:#0ff}"
|
|
".channel-label{text-align:center;font-size:0.7em;color:#888}"
|
|
".bar-container{display:flex;flex-direction:column;align-items:center;flex:1}"
|
|
".graph-section{margin:10px 0}"
|
|
".graphs-container{display:flex;gap:15px;flex-wrap:wrap}"
|
|
".graph-box{flex:1;min-width:280px}"
|
|
"</style>"
|
|
"</head><body>"
|
|
"<div class='container'>"
|
|
"<h1>📡 ESP32-C5 TOOLKIT</h1>"
|
|
|
|
"<div class='panel'>"
|
|
"<h2>🔍 WiFi Scanner</h2>"
|
|
"<button class='btn' onclick='scanNetworks()'>SCAN ALL BANDS</button>"
|
|
"<span id='scan-status' style='margin-left:10px'></span>"
|
|
"<div id='networks' style='margin-top:10px'></div>"
|
|
"</div>"
|
|
|
|
"<div class='panel'>"
|
|
"<h2>📶 Channel Usage</h2>"
|
|
"<div class='graphs-container'>"
|
|
"<div class='graph-box'>"
|
|
"<h3 class='band-24'>2.4 GHz (CH 1-13)</h3>"
|
|
"<div class='channel-graph' id='graph-24'></div>"
|
|
"</div>"
|
|
"<div class='graph-box'>"
|
|
"<h3 class='band-5'>5 GHz</h3>"
|
|
"<div class='channel-graph' id='graph-5'></div>"
|
|
"</div>"
|
|
"</div>"
|
|
"</div>"
|
|
|
|
"<div class='panel'>"
|
|
"<h2>⚡ Dual-Band Deauth</h2>"
|
|
"<div class='warn'>⚠ WiFi AP offline during attack! Auto-restores after.</div>"
|
|
"<p>Click networks to select targets:</p>"
|
|
"<div class='targets'>"
|
|
"<div class='target-card target-24'>2.4GHz: <span id='t24-name'>None</span></div>"
|
|
"<div class='target-card target-5'>5GHz: <span id='t5-name'>None</span></div>"
|
|
"</div>"
|
|
"<p>Duration: <input type='number' id='duration' value='30' min='10' max='300'> sec</p>"
|
|
"<button class='btn btn-danger' onclick='startDeauth()'>⚡ START</button>"
|
|
"<button class='btn' onclick='stopDeauth()'>STOP</button>"
|
|
"<button class='btn btn-warn' onclick='clearTargets()'>CLEAR</button>"
|
|
"<div class='status' id='deauth-status'>Status: Idle</div>"
|
|
"</div>"
|
|
|
|
"<div class='panel'>"
|
|
"<h2>📶 Bluetooth Scanner</h2>"
|
|
"<button class='btn' onclick='startBtScan()'>START BT SCAN</button>"
|
|
"<button class='btn' onclick='stopBtScan()'>STOP SCAN</button>"
|
|
"<span id='bt-scan-status' style='margin-left:10px'></span>"
|
|
"<div id='bt-devices' style='margin-top:10px;max-height:200px;overflow-y:auto'></div>"
|
|
"</div>"
|
|
|
|
"<div class='panel'>"
|
|
"<h2>💻 System</h2>"
|
|
"<div id='sysinfo'>Loading...</div>"
|
|
"</div>"
|
|
|
|
"</div>"
|
|
|
|
"<script>"
|
|
"let networks=[];"
|
|
"let target24=null,target5=null;"
|
|
"const ch5list=[36,40,44,48,149,153,157,161,165];"
|
|
|
|
"function initGraphs(){"
|
|
"let h24='';for(let i=1;i<=13;i++)h24+='<div class=\"bar-container\"><div class=\"channel-bar\" id=\"b24-'+i+'\" style=\"height:2px;background:#0f0\"></div><div class=\"channel-label\">'+i+'</div></div>';"
|
|
"document.getElementById('graph-24').innerHTML=h24;"
|
|
"let h5='';ch5list.forEach(c=>h5+='<div class=\"bar-container\"><div class=\"channel-bar\" id=\"b5-'+c+'\" style=\"height:2px;background:#0f0\"></div><div class=\"channel-label\">'+c+'</div></div>');"
|
|
"document.getElementById('graph-5').innerHTML=h5;}"
|
|
|
|
"function updateChannelGraph(networks){"
|
|
"let ch24=Array(14).fill(0);"
|
|
"let ch5={36:0,40:0,44:0,48:0,149:0,153:0,157:0,161:0,165:0};"
|
|
"networks.forEach(n=>{"
|
|
"if(n.channel<=13)ch24[n.channel]++;"
|
|
"else if(ch5[n.channel]!==undefined)ch5[n.channel]++;"
|
|
"});"
|
|
"let max24=Math.max(...ch24,1);"
|
|
"for(let i=1;i<=13;i++){"
|
|
"let count=ch24[i];"
|
|
"let h=count>0?Math.max(8,Math.round((count/max24)*55)):3;"
|
|
"let bar=document.getElementById('b24-'+i);"
|
|
"if(bar){"
|
|
"bar.style.height=h+'px';"
|
|
"bar.title='CH'+i+': '+count+' networks';"
|
|
"bar.style.background=count===0?'#0a0':count<=2?'#0f0':count<=4?'#ff0':'#f00';"
|
|
"}"
|
|
"}"
|
|
"let max5=Math.max(...Object.values(ch5),1);"
|
|
"ch5list.forEach(c=>{"
|
|
"let count=ch5[c]||0;"
|
|
"let h=count>0?Math.max(8,Math.round((count/max5)*55)):3;"
|
|
"let bar=document.getElementById('b5-'+c);"
|
|
"if(bar){"
|
|
"bar.style.height=h+'px';"
|
|
"bar.title='CH'+c+': '+count+' networks';"
|
|
"bar.style.background=count===0?'#0a0':count<=2?'#0f0':count<=4?'#ff0':'#f00';"
|
|
"}"
|
|
"});"
|
|
"}"
|
|
|
|
"async function scanNetworks(){"
|
|
"document.getElementById('scan-status').textContent='Scanning...';"
|
|
"document.getElementById('networks').innerHTML='<p>Scanning...</p>';"
|
|
"try{"
|
|
"const res=await fetch('/api/scan');"
|
|
"const data=await res.json();"
|
|
"networks=data.networks;"
|
|
"document.getElementById('scan-status').textContent='Found '+data.count;"
|
|
"renderNetworks();"
|
|
"updateChannelGraph(networks);"
|
|
"}catch(e){document.getElementById('scan-status').textContent='Error';}}"
|
|
|
|
"function renderNetworks(){"
|
|
"let html='';"
|
|
"networks.forEach((n,i)=>{"
|
|
"let cls='network';"
|
|
"if(target24&&target24.bssid===n.bssid)cls+=' selected-24';"
|
|
"if(target5&&target5.bssid===n.bssid)cls+=' selected-5';"
|
|
"html+='<div class=\"'+cls+'\" onclick=\"selectNetwork('+i+')\">';"
|
|
"html+='<span class=\"ssid\">'+n.ssid+'</span> ';"
|
|
"html+='<span class=\"info\">CH:'+n.channel+' '+n.rssi+'dBm <span class=\"'+(n.channel<=14?'band-24':'band-5')+'\">'+n.band+'</span> '+n.security+'</span>';"
|
|
"html+='</div>';});"
|
|
"document.getElementById('networks').innerHTML=html||'No networks';}"
|
|
|
|
"function selectNetwork(i){const n=networks[i];if(n.channel<=14){target24=n;document.getElementById('t24-name').textContent=n.ssid;}else{target5=n;document.getElementById('t5-name').textContent=n.ssid;}renderNetworks();}"
|
|
|
|
"function clearTargets(){target24=null;target5=null;document.getElementById('t24-name').textContent='None';document.getElementById('t5-name').textContent='None';renderNetworks();}"
|
|
|
|
"async function startDeauth(){"
|
|
"if(!target24&&!target5){alert('Select target first!');return;}"
|
|
"const dur=document.getElementById('duration').value;"
|
|
"if(!confirm('Start attack for '+dur+'s?\\nWiFi will be offline!'))return;"
|
|
"const body={duration:parseInt(dur)};"
|
|
"if(target24)body.target_24ghz={ssid:target24.ssid,bssid:target24.bssid,channel:target24.channel};"
|
|
"if(target5)body.target_5ghz={ssid:target5.ssid,bssid:target5.bssid,channel:target5.channel};"
|
|
"try{await fetch('/api/deauth/start',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});"
|
|
"document.getElementById('deauth-status').innerHTML='<span class=\"attack-running\">ATTACKING!</span>';}"
|
|
"catch(e){document.getElementById('deauth-status').textContent='Error';}}"
|
|
|
|
"async function stopDeauth(){await fetch('/api/deauth/stop');document.getElementById('deauth-status').textContent='Stopped';}"
|
|
|
|
"async function loadSysInfo(){try{const r=await fetch('/api/system-info');const d=await r.json();"
|
|
"let h='<b>'+d.chip+'</b> Rev:'+d.revision+' | Temp:<b>'+d.temp+'°C</b> | Heap:'+(d.heap/1024).toFixed(0)+'KB<br>';"
|
|
"h+='MAC:'+d.mac+' | '+d.features+' | v'+d.version;"
|
|
"if(d.attacking)h+='<br><span class=\"attack-running\">⚡ ATTACKING</span> Pkts:'+d.packets_total;"
|
|
"document.getElementById('sysinfo').innerHTML=h;}catch(e){}}"
|
|
|
|
"let btScanInterval=null;"
|
|
"async function startBtScan(){"
|
|
"document.getElementById('bt-scan-status').textContent='Starting scan...';"
|
|
"document.getElementById('bt-devices').innerHTML='<p>Scanning...</p>';"
|
|
"try{"
|
|
"const res=await fetch('/api/bt/scan/start');"
|
|
"const data=await res.json();"
|
|
"if(data.status==='ok'){"
|
|
"document.getElementById('bt-scan-status').textContent='Scanning...';"
|
|
"btScanInterval=setInterval(updateBtDevices,2000);"
|
|
"updateBtDevices();"
|
|
"}else{document.getElementById('bt-scan-status').textContent='Error: '+data.message;}"
|
|
"}catch(e){document.getElementById('bt-scan-status').textContent='Error';}}"
|
|
|
|
"async function stopBtScan(){"
|
|
"if(btScanInterval){clearInterval(btScanInterval);btScanInterval=null;}"
|
|
"try{await fetch('/api/bt/scan/stop');"
|
|
"document.getElementById('bt-scan-status').textContent='Stopped';"
|
|
"}catch(e){}}"
|
|
|
|
"async function updateBtDevices(){"
|
|
"try{"
|
|
"const res=await fetch('/api/bt/devices');"
|
|
"const data=await res.json();"
|
|
"let html='';"
|
|
"if(data.devices&&data.devices.length>0){"
|
|
"data.devices.forEach(dev=>{"
|
|
"html+='<div class=\"network\">';"
|
|
"html+='<span class=\"ssid\">'+(dev.name||'Unknown')+'</span> ';"
|
|
"html+='<span class=\"info\">'+dev.addr+' '+dev.rssi+'dBm <span class=\"band-24\">'+dev.type+'</span></span>';"
|
|
"html+='</div>';"
|
|
"});"
|
|
"}else{html='<p>No devices found</p>';}"
|
|
"document.getElementById('bt-devices').innerHTML=html;"
|
|
"document.getElementById('bt-scan-status').textContent='Found '+data.count+' devices';"
|
|
"}catch(e){document.getElementById('bt-scan-status').textContent='Error loading devices';}}"
|
|
|
|
"initGraphs();loadSysInfo();setInterval(loadSysInfo,5000);"
|
|
"</script>"
|
|
"</body></html>";
|
|
|
|
return html;
|
|
}
|