Driver spoof: mount namespace isolation keeps stock files visible to verification (dm-verity, Play Integrity, hash checks) while custom drivers load into target processes (surfaceflinger, wpa_supplicant, bluetooth). SELinux context, timestamps, perms, ownership all cloned from stock. Per-process or global modes. Configurable driver map for GPU, WiFi firmware, BT firmware. Stealth: process name masking (rtl_tcp->mediastream, etc), non-stock prop removal, MAC randomization (WiFi+BT), USB device permission tightening, log purging, logcat suppression. Full mode combines all stealth features. WebUI panels for both spoof and stealth control.
691 lines
30 KiB
HTML
691 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Driver Manager</title>
|
|
<style>
|
|
:root {
|
|
--bg: #0d1117;
|
|
--surface: #161b22;
|
|
--surface2: #21262d;
|
|
--border: #30363d;
|
|
--primary: #58a6ff;
|
|
--primary-dim: #388bfd;
|
|
--green: #3fb950;
|
|
--orange: #d29922;
|
|
--red: #f85149;
|
|
--text: #c9d1d9;
|
|
--text-dim: #8b949e;
|
|
--radius: 10px;
|
|
}
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
padding: 16px;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
.header { text-align: center; padding: 20px 0 12px; }
|
|
.header h1 { font-size: 20px; color: var(--primary); }
|
|
.header .sub { font-size: 12px; color: var(--text-dim); margin-top: 4px; }
|
|
|
|
.card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
padding: 14px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.card-title {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.8px;
|
|
color: var(--primary);
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.card-title .dot {
|
|
width: 8px; height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--green);
|
|
}
|
|
.card-title .dot.off { background: var(--red); }
|
|
.card-title .dot.warn { background: var(--orange); }
|
|
|
|
.row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 10px 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.row:last-child { border-bottom: none; }
|
|
.row-label { font-size: 14px; font-weight: 500; }
|
|
.row-desc { font-size: 11px; color: var(--text-dim); margin-top: 2px; }
|
|
.row-value { font-size: 13px; color: var(--green); font-family: monospace; }
|
|
|
|
select.sel {
|
|
background: var(--surface2);
|
|
color: var(--text);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 6px 10px;
|
|
font-size: 13px;
|
|
outline: none;
|
|
}
|
|
|
|
.info-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 6px;
|
|
}
|
|
.info-item {
|
|
background: var(--surface2);
|
|
border-radius: 6px;
|
|
padding: 8px 10px;
|
|
}
|
|
.info-item .lbl { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.3px; }
|
|
.info-item .val { font-size: 13px; font-weight: 500; margin-top: 2px; font-family: monospace; }
|
|
|
|
.log-box {
|
|
background: var(--surface2);
|
|
border-radius: 6px;
|
|
padding: 10px;
|
|
font-family: "Cascadia Code", "Fira Code", monospace;
|
|
font-size: 11px;
|
|
line-height: 1.5;
|
|
max-height: 180px;
|
|
overflow-y: auto;
|
|
color: var(--text-dim);
|
|
white-space: pre-wrap;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.btn-row { display: flex; gap: 6px; margin-top: 8px; }
|
|
.btn {
|
|
flex: 1;
|
|
padding: 8px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
background: var(--surface2);
|
|
color: var(--text);
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
}
|
|
.btn:active { opacity: 0.7; }
|
|
.btn-primary { background: var(--primary-dim); color: #fff; border-color: var(--primary); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>Driver Manager</h1>
|
|
<div class="sub">GPU / WiFi / Bluetooth / SDR / Controllers</div>
|
|
</div>
|
|
|
|
<!-- Hardware Info -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot"></span> Hardware</div>
|
|
<div class="info-grid">
|
|
<div class="info-item"><div class="lbl">Device</div><div class="val" id="hwDevice">—</div></div>
|
|
<div class="info-item"><div class="lbl">SoC</div><div class="val" id="hwSoc">—</div></div>
|
|
<div class="info-item"><div class="lbl">GPU</div><div class="val" id="hwGpu">—</div></div>
|
|
<div class="info-item"><div class="lbl">WiFi</div><div class="val" id="hwWifi">—</div></div>
|
|
<div class="info-item"><div class="lbl">Bluetooth</div><div class="val" id="hwBt">—</div></div>
|
|
<div class="info-item"><div class="lbl">USB Devices</div><div class="val" id="hwUsb">—</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- GPU -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="gpuDot"></span> GPU — PowerVR</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Driver Mode</div>
|
|
<div class="row-desc">Vulkan 1.4 / OpenGL ES 3.x / OpenCL 3.0</div>
|
|
</div>
|
|
<select class="sel" id="gpuMode" onchange="setMode('gpu_mode', this.value)">
|
|
<option value="performance">Performance</option>
|
|
<option value="balanced">Balanced</option>
|
|
<option value="powersave">Power Save</option>
|
|
<option value="compute">Compute (OpenCL)</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Vulkan Version</div></div>
|
|
<div class="row-value" id="gpuVulkan">—</div>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">GL Renderer</div></div>
|
|
<div class="row-value" id="gpuRenderer">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- WiFi -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="wifiDot"></span> WiFi — BCM4390</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Driver Mode</div>
|
|
<div class="row-desc">Nexmon: monitor + injection support</div>
|
|
</div>
|
|
<select class="sel" id="wifiMode" onchange="setMode('wifi_mode', this.value)">
|
|
<option value="standard">Standard</option>
|
|
<option value="monitor">Monitor (Nexmon)</option>
|
|
<option value="injection">Injection (Nexmon)</option>
|
|
<option value="restore">Restore Stock</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Interface</div></div>
|
|
<div class="row-value" id="wifiIface">—</div>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Nexmon Firmware</div></div>
|
|
<div class="row-value" id="wifiNexmon">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bluetooth -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="btDot"></span> Bluetooth — QCA</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Driver Mode</div>
|
|
<div class="row-desc">Standard / pentest (raw HCI, all profiles)</div>
|
|
</div>
|
|
<select class="sel" id="btMode" onchange="setMode('bt_mode', this.value)">
|
|
<option value="standard">Standard</option>
|
|
<option value="pentest">Pentest</option>
|
|
<option value="disabled">Disabled</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SDR -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="sdrDot"></span> SDR Drivers</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Radio Mode</div>
|
|
<div class="row-desc">RTL-SDR v1-4 / HackRF / Airspy / LimeSDR</div>
|
|
</div>
|
|
<select class="sel" id="sdrMode" onchange="setMode('sdr_mode', this.value)">
|
|
<option value="sdr">SDR Scanner</option>
|
|
<option value="dvbt">DVB-T (Digital TV)</option>
|
|
<option value="hackrf">HackRF (TX/RX)</option>
|
|
<option value="off">Off</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Decoder</div>
|
|
<div class="row-desc">Background signal decoder</div>
|
|
</div>
|
|
<select class="sel" id="decoderMode" onchange="setMode('decoder_mode', this.value)">
|
|
<option value="off">Off</option>
|
|
<option value="adsb">ADS-B (1090 MHz)</option>
|
|
<option value="fm">FM Radio</option>
|
|
<option value="spectrum">Spectrum Scan</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">USB SDR Devices</div></div>
|
|
<div class="row-value" id="sdrDevices">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RTL Mode Switcher -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="rtlDot"></span> RTL-SDR Mode</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Active Mode</div>
|
|
<div class="row-desc">Only one mode can use the dongle at a time</div>
|
|
</div>
|
|
<select class="sel" id="rtlMode" onchange="switchRtlMode(this.value)">
|
|
<option value="off">Off</option>
|
|
<option value="dvbt">DVB-T (Live TV)</option>
|
|
<option value="fm">FM Radio</option>
|
|
<option value="sdr">SDR Scanner (rtl_tcp)</option>
|
|
<option value="adsb">ADS-B Tracking</option>
|
|
<option value="spectrum">Spectrum Scan</option>
|
|
<option value="hackrf">HackRF TX/RX</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Process Status</div></div>
|
|
<div class="row-value" id="rtlStatus">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DVB-T / Kodi -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="kodiDot"></span> DVB-T Live TV / Kodi</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">DVB-T Frequency</div>
|
|
<div class="row-desc">Channel center frequency (Hz)</div>
|
|
</div>
|
|
<select class="sel" id="dvbtFreq" onchange="setConf('dvbt_freq', this.value)">
|
|
<option value="474000000">474 MHz (Ch 21)</option>
|
|
<option value="482000000">482 MHz (Ch 22)</option>
|
|
<option value="490000000">490 MHz (Ch 23)</option>
|
|
<option value="498000000">498 MHz (Ch 24)</option>
|
|
<option value="506000000">506 MHz (Ch 25)</option>
|
|
<option value="514000000">514 MHz (Ch 26)</option>
|
|
<option value="522000000">522 MHz (Ch 27)</option>
|
|
<option value="530000000">530 MHz (Ch 28)</option>
|
|
<option value="538000000">538 MHz (Ch 29)</option>
|
|
<option value="546000000">546 MHz (Ch 30)</option>
|
|
<option value="554000000">554 MHz (Ch 31)</option>
|
|
<option value="562000000">562 MHz (Ch 32)</option>
|
|
<option value="570000000">570 MHz (Ch 33)</option>
|
|
<option value="578000000">578 MHz (Ch 34)</option>
|
|
<option value="586000000">586 MHz (Ch 35)</option>
|
|
<option value="594000000">594 MHz (Ch 36)</option>
|
|
<option value="602000000">602 MHz (Ch 37)</option>
|
|
<option value="610000000">610 MHz (Ch 38)</option>
|
|
<option value="618000000">618 MHz (Ch 39)</option>
|
|
<option value="626000000">626 MHz (Ch 40)</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Kodi Stream</div>
|
|
<div class="row-desc">Add to Kodi PVR IPTV Simple Client</div>
|
|
</div>
|
|
<div class="row-value" id="kodiUrl">http://127.0.0.1:8554</div>
|
|
</div>
|
|
<div class="btn-row">
|
|
<button class="btn" onclick="exec('sh /data/adb/modules/driver-manager/scripts/kodi_dvbt_setup.sh setup'); log('Kodi DVB-T setup started')">Setup Kodi</button>
|
|
<button class="btn" onclick="exec('sh /data/adb/modules/driver-manager/scripts/kodi_dvbt_setup.sh scan'); log('Channel scan started')">Scan Channels</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- FM Radio -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="fmDot"></span> FM Radio</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Frequency (MHz)</div>
|
|
<div class="row-desc">FM broadcast frequency</div>
|
|
</div>
|
|
<select class="sel" id="fmFreq" onchange="setConf('fm_freq', this.value)">
|
|
<option value="88100000">88.1</option>
|
|
<option value="89900000">89.9</option>
|
|
<option value="91500000">91.5</option>
|
|
<option value="93100000">93.1</option>
|
|
<option value="95500000">95.5</option>
|
|
<option value="97100000">97.1</option>
|
|
<option value="98500000">98.5</option>
|
|
<option value="100000000">100.0</option>
|
|
<option value="101500000">101.5</option>
|
|
<option value="103100000">103.1</option>
|
|
<option value="104700000">104.7</option>
|
|
<option value="106300000">106.3</option>
|
|
<option value="107900000">107.9</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Game Controllers -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="padDot"></span> Game Controllers</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Controller Mode</div>
|
|
<div class="row-desc">All native: Xbox, PS5, PS4, Switch, Steam, Logitech, 8BitDo, Wacom</div>
|
|
</div>
|
|
<select class="sel" id="gamepadMode" onchange="setMode('gamepad_mode', this.value)">
|
|
<option value="auto">Auto (All)</option>
|
|
<option value="xbox">Xbox Only</option>
|
|
<option value="playstation">PlayStation Only</option>
|
|
<option value="nintendo">Nintendo Only</option>
|
|
<option value="off">Off</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Connected Controllers</div></div>
|
|
<div class="row-value" id="padDevices">—</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Driver Spoofing -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="spoofDot"></span> Driver Spoofing</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Spoof Engine</div>
|
|
<div class="row-desc">Mount namespace isolation — stock hashes preserved</div>
|
|
</div>
|
|
<select class="sel" id="spoofEnabled" onchange="setConf('spoof_enabled', this.value)">
|
|
<option value="0">Disabled</option>
|
|
<option value="1">Enabled</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div><div class="row-label">Active Spoofs</div></div>
|
|
<div class="row-value" id="spoofStatus">—</div>
|
|
</div>
|
|
<div class="btn-row">
|
|
<button class="btn" onclick="spoofAction('apply')">Apply Spoofs</button>
|
|
<button class="btn" onclick="spoofAction('restore')">Restore Stock</button>
|
|
<button class="btn" onclick="spoofAction('status')">Check Status</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stealth -->
|
|
<div class="card">
|
|
<div class="card-title"><span class="dot" id="stealthDot"></span> Stealth</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Stealth Mode</div>
|
|
<div class="row-desc">Hide module, mask processes, clean traces</div>
|
|
</div>
|
|
<select class="sel" id="stealthMode" onchange="setStealthMode(this.value)">
|
|
<option value="off">Off</option>
|
|
<option value="hide_module">Hide Module</option>
|
|
<option value="mask_procs">Mask Processes</option>
|
|
<option value="hide_props">Hide Props</option>
|
|
<option value="mac_random">MAC Randomization</option>
|
|
<option value="hide_usb">Hide USB Devices</option>
|
|
<option value="clean_logs">Clean Logs</option>
|
|
<option value="full">Full Stealth</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">Stealth Status</div>
|
|
<div class="row-desc" id="stealthStatus">Inactive</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div>
|
|
<div class="row-label">MAC Randomization</div>
|
|
<div class="row-desc">WiFi + Bluetooth address randomization</div>
|
|
</div>
|
|
<div class="row-value" id="macStatus">—</div>
|
|
</div>
|
|
<div class="btn-row">
|
|
<button class="btn" onclick="purgeTraces()">Purge All Traces</button>
|
|
<button class="btn btn-primary" onclick="applyStealthNow()">Apply Now</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Log -->
|
|
<div class="card">
|
|
<div class="card-title">Log</div>
|
|
<div class="log-box" id="logArea">Initializing...</div>
|
|
<div class="btn-row">
|
|
<button class="btn" onclick="refreshAll()">Refresh</button>
|
|
<button class="btn" onclick="showLog()">Module Log</button>
|
|
<button class="btn btn-primary" onclick="applyAll()">Apply & Restart</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const MODDIR = '/data/adb/modules/driver-manager';
|
|
|
|
function exec(cmd) {
|
|
return new Promise((resolve, reject) => {
|
|
const cb = 'cb_' + Date.now() + '_' + Math.random().toString(36).slice(2);
|
|
window[cb] = (errno, stdout, stderr) => {
|
|
delete window[cb];
|
|
resolve({ errno, stdout: stdout || '', stderr: stderr || '' });
|
|
};
|
|
try { ksu.exec(cmd, '{}', cb); }
|
|
catch (e) { delete window[cb]; reject(e); }
|
|
});
|
|
}
|
|
|
|
function log(msg) {
|
|
const a = document.getElementById('logArea');
|
|
const ts = new Date().toLocaleTimeString('en-US', { hour12: false });
|
|
a.textContent += '\n[' + ts + '] ' + msg;
|
|
a.scrollTop = a.scrollHeight;
|
|
}
|
|
|
|
async function gp(name) {
|
|
try { return (await exec('getprop ' + name)).stdout.trim(); }
|
|
catch (e) { return ''; }
|
|
}
|
|
|
|
async function switchRtlMode(mode) {
|
|
log('Switching RTL mode to: ' + mode);
|
|
await exec('sh ' + MODDIR + '/scripts/rtl_mode_switch.sh ' + mode);
|
|
log('RTL mode switched to: ' + mode);
|
|
await loadRtlStatus();
|
|
}
|
|
|
|
async function setConf(file, value) {
|
|
await exec('echo "' + value + '" > ' + MODDIR + '/config/' + file);
|
|
log('Set ' + file + ' = ' + value);
|
|
}
|
|
|
|
async function loadRtlStatus() {
|
|
const mode = (await exec('cat ' + MODDIR + '/config/rtl_mode 2>/dev/null')).stdout.trim();
|
|
if (mode) document.getElementById('rtlMode').value = mode;
|
|
|
|
const status = (await exec('sh ' + MODDIR + '/scripts/rtl_mode_switch.sh status 2>/dev/null')).stdout.trim();
|
|
document.getElementById('rtlStatus').textContent = status || 'off';
|
|
|
|
const dot = document.getElementById('rtlDot');
|
|
dot.className = 'dot' + (mode && mode !== 'off' ? '' : ' off');
|
|
|
|
const kodiDot = document.getElementById('kodiDot');
|
|
kodiDot.className = 'dot' + (mode === 'dvbt' ? '' : ' off');
|
|
|
|
const fmDot = document.getElementById('fmDot');
|
|
fmDot.className = 'dot' + (mode === 'fm' ? '' : ' off');
|
|
|
|
// Load saved frequencies
|
|
const dvbtFreq = (await exec('cat ' + MODDIR + '/config/dvbt_freq 2>/dev/null')).stdout.trim();
|
|
if (dvbtFreq) document.getElementById('dvbtFreq').value = dvbtFreq;
|
|
|
|
const fmFreq = (await exec('cat ' + MODDIR + '/config/fm_freq 2>/dev/null')).stdout.trim();
|
|
if (fmFreq) document.getElementById('fmFreq').value = fmFreq;
|
|
}
|
|
|
|
async function spoofAction(action) {
|
|
log('Spoof: ' + action);
|
|
const r = await exec('sh ' + MODDIR + '/scripts/driver_spoof.sh ' + action);
|
|
const out = r.stdout.trim();
|
|
if (out) {
|
|
document.getElementById('spoofStatus').textContent = out.split('\n').filter(l => l.includes('ACTIVE') || l.includes('INACTIVE')).join(' | ') || out.substring(0, 80);
|
|
log(out);
|
|
}
|
|
await loadSpoofStatus();
|
|
}
|
|
|
|
async function loadSpoofStatus() {
|
|
const enabled = (await exec('cat ' + MODDIR + '/config/spoof_enabled 2>/dev/null')).stdout.trim();
|
|
document.getElementById('spoofEnabled').value = enabled || '0';
|
|
|
|
const dot = document.getElementById('spoofDot');
|
|
if (enabled === '1') {
|
|
const r = await exec('mount | grep /data/adb/modules/driver-manager/drivers | wc -l');
|
|
const count = r.stdout.trim();
|
|
dot.className = 'dot' + (count > 0 ? '' : ' warn');
|
|
document.getElementById('spoofStatus').textContent = count > 0 ? count + ' active bind mount(s)' : 'Enabled, not yet applied';
|
|
} else {
|
|
dot.className = 'dot off';
|
|
document.getElementById('spoofStatus').textContent = 'Disabled';
|
|
}
|
|
}
|
|
|
|
async function setStealthMode(mode) {
|
|
log('Setting stealth: ' + mode);
|
|
await exec('echo "' + mode + '" > ' + MODDIR + '/config/stealth_mode');
|
|
log('Stealth mode set to: ' + mode + '. Apply or reboot to activate.');
|
|
await loadStealthStatus();
|
|
}
|
|
|
|
async function applyStealthNow() {
|
|
log('Applying stealth...');
|
|
await exec('sh ' + MODDIR + '/service.sh &');
|
|
setTimeout(async () => {
|
|
await loadStealthStatus();
|
|
log('Stealth applied');
|
|
}, 3000);
|
|
}
|
|
|
|
async function purgeTraces() {
|
|
log('Purging all traces...');
|
|
// Clear module logs
|
|
await exec('echo "" > ' + MODDIR + '/driver-manager.log');
|
|
// Clear logcat entries from our tags
|
|
await exec('logcat -b all -c 2>/dev/null');
|
|
// Remove stealth wrappers (recreated on next apply)
|
|
await exec('rm -rf ' + MODDIR + '/.wrappers');
|
|
// Clear run state
|
|
await exec('rm -f ' + MODDIR + '/run/*.pid');
|
|
// Clear stream data
|
|
await exec('rm -f ' + MODDIR + '/streams/*');
|
|
// Clear spectrum/adsb output
|
|
await exec('rm -f ' + MODDIR + '/spectrum_data.csv ' + MODDIR + '/adsb_output.txt');
|
|
document.getElementById('logArea').textContent = 'Traces purged.';
|
|
log('All traces purged');
|
|
await loadStealthStatus();
|
|
}
|
|
|
|
async function loadStealthStatus() {
|
|
const mode = (await exec('cat ' + MODDIR + '/config/stealth_mode 2>/dev/null')).stdout.trim();
|
|
if (mode) document.getElementById('stealthMode').value = mode;
|
|
|
|
const dot = document.getElementById('stealthDot');
|
|
const status = document.getElementById('stealthStatus');
|
|
|
|
if (mode === 'full') {
|
|
dot.className = 'dot';
|
|
status.textContent = 'Full stealth active — logs disabled, processes masked, props hidden';
|
|
} else if (mode && mode !== 'off') {
|
|
dot.className = 'dot warn';
|
|
status.textContent = 'Partial: ' + mode;
|
|
} else {
|
|
dot.className = 'dot off';
|
|
status.textContent = 'Inactive';
|
|
}
|
|
|
|
// MAC randomization status
|
|
const wifiMac = (await exec('settings get global wifi_connected_mac_randomization_enabled 2>/dev/null')).stdout.trim();
|
|
const btMac = (await exec('settings get global bluetooth_addr_randomization_enabled 2>/dev/null')).stdout.trim();
|
|
const macEl = document.getElementById('macStatus');
|
|
if (wifiMac === '1' && btMac === '1') {
|
|
macEl.textContent = 'WiFi + BT';
|
|
} else if (wifiMac === '1') {
|
|
macEl.textContent = 'WiFi only';
|
|
} else if (btMac === '1') {
|
|
macEl.textContent = 'BT only';
|
|
} else {
|
|
macEl.textContent = 'off';
|
|
}
|
|
}
|
|
|
|
async function setMode(file, value) {
|
|
log('Setting ' + file + ' = ' + value);
|
|
await exec('echo "' + value + '" > ' + MODDIR + '/config/' + file);
|
|
log('OK. Changes apply on next boot or click Apply & Restart.');
|
|
}
|
|
|
|
async function applyAll() {
|
|
log('Restarting driver service...');
|
|
await exec('sh ' + MODDIR + '/service.sh &');
|
|
log('Service restarted. Some changes need reboot.');
|
|
setTimeout(refreshAll, 2000);
|
|
}
|
|
|
|
async function loadHardware() {
|
|
document.getElementById('hwDevice').textContent = await gp('ro.product.model');
|
|
document.getElementById('hwSoc').textContent = await gp('ro.soc.model');
|
|
document.getElementById('hwGpu').textContent = await gp('ro.hardware.egl');
|
|
|
|
const wifi = (await exec('ls /sys/module/ | grep -iE "^(bcmdhd|qca|wcn|ath|mt76)" | head -1')).stdout.trim();
|
|
document.getElementById('hwWifi').textContent = wifi || 'unknown';
|
|
|
|
const bt = (await exec('ls /sys/module/ | grep -iE "^(btqca|btusb|btbcm)" | head -1')).stdout.trim();
|
|
document.getElementById('hwBt').textContent = bt || 'unknown';
|
|
|
|
const usb = (await exec('lsusb 2>/dev/null | wc -l || echo 0')).stdout.trim();
|
|
document.getElementById('hwUsb').textContent = usb + ' devices';
|
|
}
|
|
|
|
async function loadModes() {
|
|
const modes = ['gpu_mode', 'wifi_mode', 'bt_mode', 'sdr_mode', 'gamepad_mode', 'decoder_mode'];
|
|
const ids = ['gpuMode', 'wifiMode', 'btMode', 'sdrMode', 'gamepadMode', 'decoderMode'];
|
|
for (let i = 0; i < modes.length; i++) {
|
|
const val = (await exec('cat ' + MODDIR + '/config/' + modes[i] + ' 2>/dev/null')).stdout.trim();
|
|
if (val) document.getElementById(ids[i]).value = val;
|
|
}
|
|
}
|
|
|
|
async function loadGpuInfo() {
|
|
const renderer = await gp('debug.hwui.renderer');
|
|
document.getElementById('gpuRenderer').textContent = renderer || 'default';
|
|
|
|
const vulkan = await gp('ro.hardware.vulkan');
|
|
document.getElementById('gpuVulkan').textContent = vulkan ? vulkan + ' (1.4)' : 'unknown';
|
|
}
|
|
|
|
async function loadWifiInfo() {
|
|
const iface = (await exec('ip link show wlan0 2>/dev/null | head -1')).stdout.trim();
|
|
document.getElementById('wifiIface').textContent = iface ? 'wlan0 (up)' : 'wlan0';
|
|
|
|
const nexmon = (await exec('ls ' + MODDIR + '/firmware/fw_bcm4390_*.bin 2>/dev/null | wc -l')).stdout.trim();
|
|
document.getElementById('wifiNexmon').textContent = nexmon > 0 ? nexmon + ' firmware(s)' : 'not installed';
|
|
}
|
|
|
|
async function loadSdrInfo() {
|
|
// Detect connected USB SDR devices by vendor:product ID
|
|
const usb = (await exec(
|
|
'cat /sys/bus/usb/devices/*/idVendor 2>/dev/null | while read v; do ' +
|
|
'dir=$(grep -rl "$v" /sys/bus/usb/devices/*/idVendor 2>/dev/null | head -1 | xargs dirname); ' +
|
|
'p=$(cat "$dir/idProduct" 2>/dev/null); ' +
|
|
'case "$v:$p" in ' +
|
|
'"0bda:2832"|"0bda:2838") echo "RTL-SDR";; ' +
|
|
'"1d50:6089") echo "HackRF";; ' +
|
|
'"1d50:60a1") echo "Airspy";; ' +
|
|
'"0403:6014"|"04b4:00f3") echo "LimeSDR";; ' +
|
|
'esac; done 2>/dev/null | sort -u | tr "\\n" ", "'
|
|
)).stdout.trim();
|
|
document.getElementById('sdrDevices').textContent = usb || 'none detected';
|
|
}
|
|
|
|
async function loadControllerInfo() {
|
|
const pads = (await exec('cat /proc/bus/input/devices 2>/dev/null | grep -iE "(xbox|playstation|dualsense|pro controller|8bitdo|gamepad)" | wc -l')).stdout.trim();
|
|
document.getElementById('padDevices').textContent = (pads && pads !== '0') ? pads + ' connected' : 'none';
|
|
}
|
|
|
|
async function showLog() {
|
|
const r = await exec('cat ' + MODDIR + '/driver-manager.log 2>/dev/null || echo "No log"');
|
|
document.getElementById('logArea').textContent = '=== Module Log ===\n' + r.stdout;
|
|
}
|
|
|
|
async function refreshAll() {
|
|
log('Refreshing...');
|
|
await loadHardware();
|
|
await loadModes();
|
|
await loadGpuInfo();
|
|
await loadWifiInfo();
|
|
await loadSdrInfo();
|
|
await loadControllerInfo();
|
|
await loadRtlStatus();
|
|
await loadSpoofStatus();
|
|
await loadStealthStatus();
|
|
log('Done');
|
|
}
|
|
|
|
(async () => {
|
|
try { await refreshAll(); log('Ready'); }
|
|
catch (e) { log('Init error: ' + e.message); }
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|