444 lines
19 KiB
Python
444 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Convert enhanced HTML to C string format for embedding
|
|
"""
|
|
|
|
def escape_c_string(s):
|
|
"""Escape string for C embedding"""
|
|
s = s.replace('\\', '\\\\')
|
|
s = s.replace('"', '\\"')
|
|
s = s.replace('\n', '\\n')
|
|
return s
|
|
|
|
# Read the enhanced HTML
|
|
with open('enhanced_ui.html', 'r') as f:
|
|
html_content = f.read()
|
|
|
|
# Add complete JavaScript functionality
|
|
js_complete = '''
|
|
// Complete JavaScript implementation
|
|
const charts = {};
|
|
let networkActivityData = { labels: [], datasets: [{ label: 'Networks', data: [], borderColor: '#00ff41', backgroundColor: 'rgba(0,255,65,0.1)' }] };
|
|
let rssiData = { labels: [], datasets: [{ label: 'RSSI Distribution', data: [], backgroundColor: '#00ff41' }] };
|
|
let packetFlowData = { labels: [], datasets: [{ label: 'Packets/sec', data: [], borderColor: '#00d4ff' }] };
|
|
let signalTimeData = { labels: [], datasets: [] };
|
|
let channelUtilData = { labels: [], datasets: [{ label: 'Utilization %', data: [], backgroundColor: '#00ff41' }] };
|
|
|
|
let selected24ghz = null;
|
|
let selected5ghz = null;
|
|
let autoScanInterval = null;
|
|
let packetCount = 0;
|
|
let startTime = Date.now();
|
|
|
|
function initCharts() {
|
|
const chartOptions = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
animation: { duration: 0 },
|
|
plugins: {
|
|
legend: { labels: { color: '#00ff41', font: { family: 'JetBrains Mono' } } }
|
|
},
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#00ff41', font: { family: 'JetBrains Mono' } },
|
|
grid: { color: 'rgba(0,255,65,0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#00ff41', font: { family: 'JetBrains Mono' } },
|
|
grid: { color: 'rgba(0,255,65,0.1)' }
|
|
}
|
|
}
|
|
};
|
|
|
|
charts.networkActivity = new Chart(document.getElementById('network-activity-chart'), {
|
|
type: 'line',
|
|
data: networkActivityData,
|
|
options: chartOptions
|
|
});
|
|
|
|
charts.rssi = new Chart(document.getElementById('rssi-chart'), {
|
|
type: 'bar',
|
|
data: rssiData,
|
|
options: chartOptions
|
|
});
|
|
|
|
charts.packetFlow = new Chart(document.getElementById('packet-flow-chart'), {
|
|
type: 'line',
|
|
data: packetFlowData,
|
|
options: {
|
|
...chartOptions,
|
|
scales: {
|
|
x: {
|
|
ticks: { color: '#00d4ff', font: { family: 'JetBrains Mono' } },
|
|
grid: { color: 'rgba(0,212,255,0.1)' }
|
|
},
|
|
y: {
|
|
ticks: { color: '#00d4ff', font: { family: 'JetBrains Mono' } },
|
|
grid: { color: 'rgba(0,212,255,0.1)' }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
charts.signalTime = new Chart(document.getElementById('signal-time-chart'), {
|
|
type: 'line',
|
|
data: signalTimeData,
|
|
options: chartOptions
|
|
});
|
|
|
|
charts.channelUtil = new Chart(document.getElementById('channel-util-chart'), {
|
|
type: 'bar',
|
|
data: channelUtilData,
|
|
options: {
|
|
...chartOptions,
|
|
scales: {
|
|
...chartOptions.scales,
|
|
y: {
|
|
...chartOptions.scales.y,
|
|
max: 100
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateDashboard() {
|
|
fetch('/api/system-info')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
document.getElementById('free-heap').textContent = formatBytes(data.free_heap);
|
|
const uptime = Math.floor((Date.now() - startTime) / 1000);
|
|
document.getElementById('uptime').textContent = formatTime(uptime);
|
|
|
|
const sysInfo = document.getElementById('system-info');
|
|
sysInfo.innerHTML = `
|
|
<div class="terminal-line"><span class="terminal-prompt">root@esp32-c5:</span> <span class="terminal-output">IDF Version: ${data.idf_version}</span></div>
|
|
<div class="terminal-line"><span class="terminal-prompt">root@esp32-c5:</span> <span class="terminal-output">Chip Model: ${data.chip_model}</span></div>
|
|
<div class="terminal-line"><span class="terminal-prompt">root@esp32-c5:</span> <span class="terminal-output">Cores: ${data.chip_cores}</span></div>
|
|
<div class="terminal-line"><span class="terminal-prompt">root@esp32-c5:</span> <span class="terminal-output">MAC: ${data.mac_address}</span></div>
|
|
<div class="terminal-line"><span class="terminal-prompt">root@esp32-c5:</span> <span class="terminal-output">Features: ${data.features}</span></div>
|
|
`;
|
|
});
|
|
}
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes < 1024) return bytes + ' B';
|
|
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
|
|
return (bytes / 1048576).toFixed(2) + ' MB';
|
|
}
|
|
|
|
function formatTime(seconds) {
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = seconds % 60;
|
|
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
|
}
|
|
|
|
// WiFi Scanner
|
|
document.getElementById('scan-btn')?.addEventListener('click', function() {
|
|
this.disabled = true;
|
|
const status = document.getElementById('scan-status');
|
|
status.className = 'status-indicator active';
|
|
document.getElementById('scan-status-text').textContent = 'SCANNING...';
|
|
|
|
fetch('/api/scan')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
displayNetworks(data.networks);
|
|
updateRSSIChart(data.networks);
|
|
document.getElementById('networks-count').textContent = data.networks.length;
|
|
}
|
|
this.disabled = false;
|
|
status.className = 'status-indicator inactive';
|
|
document.getElementById('scan-status-text').textContent = 'READY';
|
|
});
|
|
});
|
|
|
|
function displayNetworks(networks) {
|
|
const tbody = document.querySelector('#networks-table tbody');
|
|
tbody.innerHTML = '';
|
|
networks.forEach(net => {
|
|
const row = document.createElement('tr');
|
|
const rssiPercent = Math.min(100, Math.max(0, (net.rssi + 100) * 2));
|
|
row.innerHTML = `
|
|
<td>${net.ssid || '(HIDDEN)'}</td>
|
|
<td><code>${net.bssid}</code></td>
|
|
<td>${net.band}</td>
|
|
<td>${net.channel}</td>
|
|
<td>${net.rssi} dBm</td>
|
|
<td>${net.security}</td>
|
|
<td>
|
|
<div class="signal-bar">
|
|
<div class="signal-bar-fill" style="width: ${rssiPercent}%"></div>
|
|
</div>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function updateRSSIChart(networks) {
|
|
const rssiRanges = { '-90 to -80': 0, '-80 to -70': 0, '-70 to -60': 0, '-60 to -50': 0, '-50 to -40': 0, '-40+': 0 };
|
|
networks.forEach(net => {
|
|
if (net.rssi < -90) rssiRanges['-90 to -80']++;
|
|
else if (net.rssi < -80) rssiRanges['-80 to -70']++;
|
|
else if (net.rssi < -70) rssiRanges['-70 to -60']++;
|
|
else if (net.rssi < -60) rssiRanges['-60 to -50']++;
|
|
else if (net.rssi < -50) rssiRanges['-50 to -40']++;
|
|
else rssiRanges['-40+']++;
|
|
});
|
|
|
|
rssiData.labels = Object.keys(rssiRanges);
|
|
rssiData.datasets[0].data = Object.values(rssiRanges);
|
|
charts.rssi.update();
|
|
}
|
|
|
|
// Packet Sniffer
|
|
let sniffing = false;
|
|
let sniffInterval = null;
|
|
|
|
document.getElementById('start-sniff')?.addEventListener('click', function() {
|
|
const channel = document.getElementById('sniff-channel').value;
|
|
const filter = document.getElementById('packet-filter').value;
|
|
|
|
fetch(`/api/sniff/start?channel=${channel}&filter=${filter}`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
sniffing = true;
|
|
this.disabled = true;
|
|
document.getElementById('stop-sniff').disabled = false;
|
|
sniffInterval = setInterval(fetchPackets, 1000);
|
|
}
|
|
});
|
|
});
|
|
|
|
document.getElementById('stop-sniff')?.addEventListener('click', function() {
|
|
fetch('/api/sniff/stop')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
sniffing = false;
|
|
this.disabled = true;
|
|
document.getElementById('start-sniff').disabled = false;
|
|
if (sniffInterval) {
|
|
clearInterval(sniffInterval);
|
|
sniffInterval = null;
|
|
}
|
|
});
|
|
});
|
|
|
|
function fetchPackets() {
|
|
if (!sniffing) return;
|
|
fetch('/api/sniff/packets')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.packets && data.packets.length > 0) {
|
|
data.packets.forEach(pkt => addPacketToLog(pkt));
|
|
packetCount += data.packets.length;
|
|
document.getElementById('packets-count').textContent = packetCount;
|
|
updatePacketFlowChart(data.packets.length);
|
|
}
|
|
});
|
|
}
|
|
|
|
function addPacketToLog(packet) {
|
|
const log = document.getElementById('packet-log');
|
|
const time = new Date().toLocaleTimeString();
|
|
const line = document.createElement('div');
|
|
line.className = 'terminal-line';
|
|
line.innerHTML = `<span class="terminal-prompt">[${time}]</span> <span class="terminal-output">${packet.type}</span> <span class="terminal-prompt">SRC:</span> <span class="terminal-output">${packet.src}</span> <span class="terminal-prompt">DST:</span> <span class="terminal-output">${packet.dst}</span> <span class="terminal-prompt">RSSI:</span> <span class="terminal-output">${packet.rssi} dBm</span>`;
|
|
log.appendChild(line);
|
|
log.scrollTop = log.scrollHeight;
|
|
if (log.children.length > 100) log.removeChild(log.firstChild);
|
|
}
|
|
|
|
function updatePacketFlowChart(count) {
|
|
const now = new Date().toLocaleTimeString();
|
|
packetFlowData.labels.push(now);
|
|
packetFlowData.datasets[0].data.push(count);
|
|
if (packetFlowData.labels.length > 20) {
|
|
packetFlowData.labels.shift();
|
|
packetFlowData.datasets[0].data.shift();
|
|
}
|
|
charts.packetFlow.update('none');
|
|
}
|
|
|
|
// Bluetooth Scanner
|
|
document.getElementById('bt-scan-btn')?.addEventListener('click', function() {
|
|
this.disabled = true;
|
|
document.getElementById('bt-scan-stop-btn').disabled = false;
|
|
document.getElementById('bt-scan-status').className = 'status-indicator active';
|
|
|
|
fetch('/api/bt/scan/start')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
setInterval(fetchBTDevices, 2000);
|
|
}
|
|
});
|
|
});
|
|
|
|
function fetchBTDevices() {
|
|
fetch('/api/bt/devices')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.devices) {
|
|
displayBTDevices(data.devices);
|
|
}
|
|
});
|
|
}
|
|
|
|
function displayBTDevices(devices) {
|
|
const tbody = document.querySelector('#bt-devices-table tbody');
|
|
tbody.innerHTML = '';
|
|
devices.forEach(dev => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${dev.name || 'Unknown'}</td>
|
|
<td><code>${formatBTAddr(dev.addr)}</code></td>
|
|
<td>${dev.rssi} dBm</td>
|
|
<td>${dev.type === 1 ? 'BLE' : 'Classic'}</td>
|
|
<td><button class="btn" onclick="selectBTJam('${formatBTAddr(dev.addr)}')">SELECT</button></td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function formatBTAddr(addr) {
|
|
return addr.map(b => b.toString(16).padStart(2, '0')).join(':').toUpperCase();
|
|
}
|
|
|
|
// Deauth Engine
|
|
document.getElementById('deauth-scan-btn')?.addEventListener('click', function() {
|
|
fetch('/api/scan')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
displayDeauthNetworks(data.networks);
|
|
}
|
|
});
|
|
});
|
|
|
|
function displayDeauthNetworks(networks) {
|
|
const targets = document.getElementById('deauth-targets');
|
|
let html = '<table><tr><th>SSID</th><th>BSSID</th><th>BAND</th><th>CH</th><th>SELECT</th></tr>';
|
|
networks.forEach(net => {
|
|
html += `<tr><td>${net.ssid || '(HIDDEN)'}</td><td><code>${net.bssid}</code></td><td>${net.band}</td><td>${net.channel}</td><td>`;
|
|
if (net.band === '2.4GHz') {
|
|
html += `<button class="btn" onclick="selectDeauthTarget('24ghz', '${net.ssid}', '${net.bssid}', ${net.channel})">2.4GHz</button>`;
|
|
}
|
|
if (net.band === '5GHz') {
|
|
html += `<button class="btn" onclick="selectDeauthTarget('5ghz', '${net.ssid}', '${net.bssid}', ${net.channel})">5GHz</button>`;
|
|
}
|
|
html += '</td></tr>';
|
|
});
|
|
html += '</table>';
|
|
targets.innerHTML = html;
|
|
}
|
|
|
|
function selectDeauthTarget(band, ssid, bssid, channel) {
|
|
const target = {ssid, bssid, channel};
|
|
if (band === '24ghz') selected24ghz = target;
|
|
else selected5ghz = target;
|
|
updateDeauthTargets();
|
|
}
|
|
|
|
function updateDeauthTargets() {
|
|
const targets = document.getElementById('deauth-targets');
|
|
let html = '';
|
|
if (selected24ghz) {
|
|
html += `<div style="background:#1565C0;padding:10px;margin:10px 0;border:1px solid #00ff41;"><strong>2.4GHz:</strong> ${selected24ghz.ssid} (${selected24ghz.bssid}) CH:${selected24ghz.channel} <button class="btn" onclick="selected24ghz=null;updateDeauthTargets();">REMOVE</button></div>`;
|
|
}
|
|
if (selected5ghz) {
|
|
html += `<div style="background:#E65100;padding:10px;margin:10px 0;border:1px solid #00ff41;"><strong>5GHz:</strong> ${selected5ghz.ssid} (${selected5ghz.bssid}) CH:${selected5ghz.channel} <button class="btn" onclick="selected5ghz=null;updateDeauthTargets();">REMOVE</button></div>`;
|
|
}
|
|
targets.innerHTML = html;
|
|
}
|
|
|
|
document.getElementById('deauth-start-btn')?.addEventListener('click', function() {
|
|
if (!selected24ghz && !selected5ghz) {
|
|
alert('SELECT AT LEAST ONE TARGET');
|
|
return;
|
|
}
|
|
const duration = parseInt(document.getElementById('deauth-duration').value);
|
|
const body = {duration};
|
|
if (selected24ghz) body.target_24ghz = selected24ghz;
|
|
if (selected5ghz) body.target_5ghz = selected5ghz;
|
|
|
|
if (!confirm('START DEAUTH ATTACK? USE ONLY ON YOUR NETWORKS!')) return;
|
|
|
|
fetch('/api/deauth/start', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(body)
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
this.disabled = true;
|
|
document.getElementById('deauth-stop-btn').disabled = false;
|
|
setInterval(updateDeauthStats, 1000);
|
|
}
|
|
});
|
|
});
|
|
|
|
function updateDeauthStats() {
|
|
fetch('/api/deauth/status')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
document.getElementById('deauth-total').textContent = data.total_packets || 0;
|
|
document.getElementById('deauth-24').textContent = data.packets_24ghz || 0;
|
|
document.getElementById('deauth-5').textContent = data.packets_5ghz || 0;
|
|
document.getElementById('deauth-time').textContent = (data.elapsed_time || 0) + 's';
|
|
});
|
|
}
|
|
|
|
// Navigation
|
|
document.querySelectorAll('.nav-item').forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
|
|
item.classList.add('active');
|
|
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
|
|
document.getElementById(item.getAttribute('data-page')).classList.add('active');
|
|
});
|
|
});
|
|
|
|
// Initialize
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initCharts();
|
|
loadSystemInfo();
|
|
updateDashboard();
|
|
setInterval(updateDashboard, 1000);
|
|
});
|
|
|
|
function loadSystemInfo() {
|
|
updateDashboard();
|
|
}
|
|
'''
|
|
|
|
# Replace the placeholder JavaScript
|
|
html_content = html_content.replace(
|
|
' // API functions and event handlers would go here...\n // (This is a template - full implementation continues)',
|
|
js_complete
|
|
)
|
|
|
|
# Convert to C string format
|
|
c_string = 'static const char index_html[] = \\\n'
|
|
lines = html_content.split('\n')
|
|
for i, line in enumerate(lines):
|
|
escaped = escape_c_string(line)
|
|
if i < len(lines) - 1:
|
|
c_string += f'"{escaped}\\n"\\\n'
|
|
else:
|
|
c_string += f'"{escaped}";'
|
|
|
|
# Write output
|
|
with open('enhanced_ui_c_string.txt', 'w') as f:
|
|
f.write(c_string)
|
|
|
|
print(f"Converted HTML to C string format")
|
|
print(f"Original size: {len(html_content)} bytes")
|
|
print(f"C string size: {len(c_string)} bytes")
|
|
print("File: enhanced_ui_c_string.txt")
|