diff --git a/customize.sh b/customize.sh index 00792e1..af7cde5 100644 --- a/customize.sh +++ b/customize.sh @@ -39,6 +39,8 @@ echo "standard" > "$MODPATH/config/bt_mode" echo "sdr" > "$MODPATH/config/sdr_mode" echo "auto" > "$MODPATH/config/gamepad_mode" echo "off" > "$MODPATH/config/decoder_mode" +echo "off" > "$MODPATH/config/stealth_mode" +echo "0" > "$MODPATH/config/spoof_enabled" echo "100.0M" > "$MODPATH/config/fm_freq" echo "24M:1800M" > "$MODPATH/config/spectrum_range" diff --git a/driver-manager-v1.0.0.zip b/driver-manager-v1.0.0.zip index debe592..cc6b886 100644 Binary files a/driver-manager-v1.0.0.zip and b/driver-manager-v1.0.0.zip differ diff --git a/scripts/driver_spoof.sh b/scripts/driver_spoof.sh new file mode 100755 index 0000000..87df94f --- /dev/null +++ b/scripts/driver_spoof.sh @@ -0,0 +1,298 @@ +#!/system/bin/sh +# Driver Spoofing System +# +# Makes Android think stock drivers are loaded while custom drivers +# are actually running. Uses mount namespace isolation so: +# +# - Verification tools see: stock file (original hash, signature, SELinux context) +# - Driver loader sees: custom file (our patched/modified driver) +# +# How it works: +# 1. Stock driver stays at /vendor/lib64/... untouched (dm-verity happy) +# 2. Custom driver lives in /data/adb/modules/driver-manager/drivers/ +# 3. We clone the stock file's metadata (timestamps, SELinux context, owner, perms) +# onto the custom driver +# 4. We bind-mount the custom driver over the stock path ONLY in the mount +# namespace of the process that loads it (e.g. surfaceflinger, wpa_supplicant) +# 5. All other processes (Play Integrity, banking apps, SafetyNet) see the +# stock file at the original path with the original hash +# +# This is NOT file replacement. The stock file is never modified. +# dm-verity remains intact. Verified boot passes. + +MODDIR="/data/adb/modules/driver-manager" +CONFDIR="$MODDIR/config" +LOGFILE="$MODDIR/driver-manager.log" +DRIVERDIR="$MODDIR/drivers" +STOCKDIR="$MODDIR/drivers/.stock_meta" + +mkdir -p "$DRIVERDIR" "$STOCKDIR" + +mlog() { + STEALTH=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off") + [ "$STEALTH" = "full" ] && return + echo "$(date '+%Y-%m-%d %H:%M:%S') [spoof] $1" >> "$LOGFILE" +} + +# ================================================================= +# Clone stock file metadata onto custom driver +# ================================================================= +# Copies: SELinux context, ownership, permissions, timestamps +# Does NOT copy file contents — that's the whole point +clone_metadata() { + STOCK_PATH="$1" + CUSTOM_PATH="$2" + + if [ ! -f "$STOCK_PATH" ] || [ ! -f "$CUSTOM_PATH" ]; then + mlog "ERROR: clone_metadata missing file: $STOCK_PATH or $CUSTOM_PATH" + return 1 + fi + + # Save stock metadata for verification + METAFILE="$STOCKDIR/$(echo "$STOCK_PATH" | tr '/' '_')" + ls -laZ "$STOCK_PATH" > "$METAFILE" 2>/dev/null + sha256sum "$STOCK_PATH" >> "$METAFILE" 2>/dev/null + + # Clone SELinux context + CONTEXT=$(ls -Z "$STOCK_PATH" 2>/dev/null | awk '{print $1}') + if [ -n "$CONTEXT" ]; then + chcon "$CONTEXT" "$CUSTOM_PATH" 2>/dev/null + mlog "SELinux context: $CONTEXT -> $(basename "$CUSTOM_PATH")" + fi + + # Clone ownership + OWNER=$(stat -c '%u:%g' "$STOCK_PATH" 2>/dev/null) + if [ -n "$OWNER" ]; then + chown "$OWNER" "$CUSTOM_PATH" 2>/dev/null + fi + + # Clone permissions + PERMS=$(stat -c '%a' "$STOCK_PATH" 2>/dev/null) + if [ -n "$PERMS" ]; then + chmod "$PERMS" "$CUSTOM_PATH" 2>/dev/null + fi + + # Clone timestamps (access + modify) + touch -r "$STOCK_PATH" "$CUSTOM_PATH" 2>/dev/null + + mlog "Metadata cloned: $STOCK_PATH -> $CUSTOM_PATH" +} + +# ================================================================= +# Bind mount custom driver over stock path in a specific process +# ================================================================= +# Uses nsenter to enter the target process's mount namespace +# and bind-mount the custom file over the stock path. +# Only that process (and its children) see the custom file. +spoof_for_process() { + PROCESS_NAME="$1" + STOCK_PATH="$2" + CUSTOM_PATH="$3" + + # Find the PID of the target process + PID=$(pidof "$PROCESS_NAME" 2>/dev/null) + if [ -z "$PID" ]; then + mlog "Process not running: $PROCESS_NAME" + return 1 + fi + + # Clone metadata first + clone_metadata "$STOCK_PATH" "$CUSTOM_PATH" + + # Enter the process's mount namespace and bind-mount + nsenter -t "$PID" -m -- mount --bind "$CUSTOM_PATH" "$STOCK_PATH" 2>/dev/null + RET=$? + + if [ $RET -eq 0 ]; then + mlog "Spoofed: $PROCESS_NAME (PID $PID) sees $CUSTOM_PATH at $STOCK_PATH" + else + mlog "nsenter failed for $PROCESS_NAME — falling back to global mount" + # Fallback: global bind mount (less stealthy but works) + mount --bind "$CUSTOM_PATH" "$STOCK_PATH" 2>/dev/null + mlog "Global bind mount: $CUSTOM_PATH -> $STOCK_PATH" + fi +} + +# ================================================================= +# Global spoof — bind mount for ALL processes +# ================================================================= +# Less stealthy but simpler. Use when you don't care about +# hiding from verification tools (e.g. custom ROM, no banking apps) +spoof_global() { + STOCK_PATH="$1" + CUSTOM_PATH="$2" + + clone_metadata "$STOCK_PATH" "$CUSTOM_PATH" + mount --bind "$CUSTOM_PATH" "$STOCK_PATH" 2>/dev/null + + if [ $? -eq 0 ]; then + mlog "Global spoof: $CUSTOM_PATH -> $STOCK_PATH" + else + mlog "ERROR: global mount failed for $STOCK_PATH" + fi +} + +# ================================================================= +# Restore stock driver (unmount bind) +# ================================================================= +restore_stock() { + STOCK_PATH="$1" + umount "$STOCK_PATH" 2>/dev/null + mlog "Restored stock: $STOCK_PATH" +} + +# ================================================================= +# Verify spoof is active +# ================================================================= +# Checks if the file at the stock path is actually our custom driver +verify_spoof() { + STOCK_PATH="$1" + CUSTOM_PATH="$2" + + STOCK_HASH=$(sha256sum "$STOCK_PATH" 2>/dev/null | awk '{print $1}') + CUSTOM_HASH=$(sha256sum "$CUSTOM_PATH" 2>/dev/null | awk '{print $1}') + + if [ "$STOCK_HASH" = "$CUSTOM_HASH" ]; then + echo "ACTIVE — custom driver loaded at $STOCK_PATH" + else + echo "INACTIVE — stock driver at $STOCK_PATH" + fi +} + +# ================================================================= +# Driver mapping configuration +# ================================================================= +# Format: stock_path|custom_filename|target_process +# Custom files go in /data/adb/modules/driver-manager/drivers/ +DRIVER_MAP="$CONFDIR/driver_map.conf" + +load_driver_map() { + if [ ! -f "$DRIVER_MAP" ]; then + cat > "$DRIVER_MAP" << 'MAP' +# Driver Spoof Map +# Format: stock_path|custom_filename|target_process|spoof_type +# spoof_type: process (per-process ns) or global (all processes) +# +# GPU — PowerVR DXT-48-1536 +# Place custom GPU driver as: drivers/libGLES_powervr_custom.so +#/vendor/lib64/egl/libGLES_powervr.so|libGLES_powervr_custom.so|surfaceflinger|process +#/vendor/lib64/libPVROCL.so|libPVROCL_custom.so|surfaceflinger|process +# +# WiFi — BCM4390 firmware +# Place custom firmware as: drivers/fw_bcm4390_custom.bin +#/vendor/firmware/fw_bcmdhd4390.bin|fw_bcm4390_custom.bin|wpa_supplicant|process +# +# Bluetooth — QCA firmware +#/vendor/firmware/qca/nvm_00440200.bin|nvm_custom.bin|bluetooth|process +# +# Uncomment and edit lines above to activate spoofing for each driver. +# The stock file is NEVER modified. Custom files must be placed in: +# /data/adb/modules/driver-manager/drivers/ +MAP + mlog "Created default driver map at $DRIVER_MAP" + fi +} + +# ================================================================= +# Apply all configured spoofs +# ================================================================= +apply_all() { + load_driver_map + + while IFS='|' read -r STOCK_PATH CUSTOM_FILE TARGET_PROC SPOOF_TYPE; do + # Skip comments and empty lines + case "$STOCK_PATH" in + \#*|"") continue ;; + esac + + CUSTOM_PATH="$DRIVERDIR/$CUSTOM_FILE" + + if [ ! -f "$CUSTOM_PATH" ]; then + mlog "SKIP: custom driver not found: $CUSTOM_PATH" + continue + fi + + if [ ! -f "$STOCK_PATH" ]; then + mlog "SKIP: stock path not found: $STOCK_PATH" + continue + fi + + case "$SPOOF_TYPE" in + process) + spoof_for_process "$TARGET_PROC" "$STOCK_PATH" "$CUSTOM_PATH" + ;; + global) + spoof_global "$STOCK_PATH" "$CUSTOM_PATH" + ;; + *) + mlog "Unknown spoof type: $SPOOF_TYPE for $STOCK_PATH" + ;; + esac + done < "$DRIVER_MAP" +} + +# ================================================================= +# Status — show all active spoofs +# ================================================================= +show_status() { + load_driver_map + echo "=== Driver Spoof Status ===" + + while IFS='|' read -r STOCK_PATH CUSTOM_FILE TARGET_PROC SPOOF_TYPE; do + case "$STOCK_PATH" in + \#*|"") continue ;; + esac + CUSTOM_PATH="$DRIVERDIR/$CUSTOM_FILE" + STATUS=$(verify_spoof "$STOCK_PATH" "$CUSTOM_PATH") + echo " $STOCK_PATH -> $CUSTOM_FILE [$TARGET_PROC] $STATUS" + done < "$DRIVER_MAP" + + echo "" + echo "=== Mount binds ===" + mount | grep "$DRIVERDIR" 2>/dev/null || echo " (none active)" +} + +# ================================================================= +# Main +# ================================================================= +case "$1" in + apply) + apply_all + ;; + restore) + if [ -n "$2" ]; then + restore_stock "$2" + else + # Restore all + load_driver_map + while IFS='|' read -r STOCK_PATH CUSTOM_FILE TARGET_PROC SPOOF_TYPE; do + case "$STOCK_PATH" in + \#*|"") continue ;; + esac + restore_stock "$STOCK_PATH" + done < "$DRIVER_MAP" + fi + ;; + status) + show_status + ;; + clone) + # Clone metadata only: driver_spoof.sh clone /vendor/path /custom/path + clone_metadata "$2" "$3" + ;; + verify) + verify_spoof "$2" "$3" + ;; + *) + echo "Usage: driver_spoof.sh {apply|restore|status|clone|verify}" + echo "" + echo " apply Apply all driver spoofs from driver_map.conf" + echo " restore [path] Restore stock driver(s)" + echo " status Show active spoofs" + echo " clone src dst Clone file metadata (SELinux, perms, timestamps)" + echo " verify stock cust Check if spoof is active" + echo "" + echo "Driver map: $DRIVER_MAP" + echo "Custom drivers: $DRIVERDIR/" + ;; +esac diff --git a/scripts/rtl_mode_switch.sh b/scripts/rtl_mode_switch.sh index b4f6eea..f0e91e8 100755 --- a/scripts/rtl_mode_switch.sh +++ b/scripts/rtl_mode_switch.sh @@ -10,13 +10,35 @@ MODDIR="/data/adb/modules/driver-manager" CONFDIR="$MODDIR/config" LOGFILE="$MODDIR/driver-manager.log" PIDDIR="$MODDIR/run" -TERMUX="/data/data/com.termux/files/usr/bin" STREAMDIR="$MODDIR/streams" +# Use stealth wrappers if available, otherwise Termux direct +STEALTH_BIN=$(cat "$CONFDIR/stealth_bin_path" 2>/dev/null) +if [ -n "$STEALTH_BIN" ] && [ -d "$STEALTH_BIN" ]; then + TERMUX="$STEALTH_BIN" + # Map stealth names back to real tool names for this script + RTL_TCP="$STEALTH_BIN/mediastream" + RTL_FM="$STEALTH_BIN/audioservice" + RTL_ADSB="$STEALTH_BIN/locationd" + RTL_POWER="$STEALTH_BIN/powermanager" + HACKRF="$STEALTH_BIN/usb_mtp" +else + TERMUX="/data/data/com.termux/files/usr/bin" + RTL_TCP="$TERMUX/rtl_tcp" + RTL_FM="$TERMUX/rtl_fm" + RTL_ADSB="$TERMUX/rtl_adsb" + RTL_POWER="$TERMUX/rtl_power" + HACKRF="$TERMUX/hackrf_transfer" +fi + mkdir -p "$PIDDIR" "$STREAMDIR" +# Stealth-aware logging — skip logcat in stealth mode +STEALTH_MODE=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off") mlog() { + [ "$STEALTH_MODE" = "full" ] && return echo "$(date '+%Y-%m-%d %H:%M:%S') [rtl_switch] $1" >> "$LOGFILE" + [ "$STEALTH_MODE" = "off" ] && log -t DriverManager "$1" 2>/dev/null } # Kill any running RTL process that holds the dongle @@ -32,13 +54,18 @@ kill_rtl() { fi rm -f "$pidfile" done - # Also catch any strays + # Also catch any strays — both real and stealth names pkill -f rtl_tcp 2>/dev/null pkill -f rtl_fm 2>/dev/null pkill -f rtl_adsb 2>/dev/null pkill -f rtl_power 2>/dev/null pkill -f dvbt_rx 2>/dev/null pkill -f sdr_tv 2>/dev/null + pkill -f mediastream 2>/dev/null + pkill -f audioservice 2>/dev/null + pkill -f locationd 2>/dev/null + pkill -f powermanager 2>/dev/null + pkill -f usb_mtp 2>/dev/null sleep 1 } @@ -50,8 +77,8 @@ start_rtl_tcp() { SRATE=$(cat "$CONFDIR/rtl_samplerate" 2>/dev/null || echo "2048000") FREQ=$(cat "$CONFDIR/rtl_freq" 2>/dev/null || echo "100000000") - if [ -x "$TERMUX/rtl_tcp" ]; then - "$TERMUX/rtl_tcp" -a 127.0.0.1 -p "$PORT" -f "$FREQ" -s "$SRATE" -g "$GAIN" & + if [ -x "$RTL_TCP" ]; then + "$RTL_TCP" -a 127.0.0.1 -p "$PORT" -f "$FREQ" -s "$SRATE" -g "$GAIN" & echo $! > "$PIDDIR/rtl_tcp.pid" mlog "rtl_tcp started on port $PORT (freq=$FREQ srate=$SRATE gain=$GAIN)" else diff --git a/service.sh b/service.sh index df117c6..1404c3f 100755 --- a/service.sh +++ b/service.sh @@ -324,4 +324,157 @@ case "$GAMEPAD_MODE" in ;; esac +# ============================================================ +# DRIVER SPOOFING — Stock files visible, custom code loaded +# ============================================================ +# Per-process mount namespace isolation: verification tools see +# stock drivers (hash/sig intact), but the actual loader process +# (surfaceflinger, wpa_supplicant, etc.) gets our custom binary. +# dm-verity stays intact. Verified boot passes. + +SPOOF_ENABLED=$(cat "$CONFDIR/spoof_enabled" 2>/dev/null || echo "0") + +if [ "$SPOOF_ENABLED" = "1" ]; then + # Wait for target processes to be running + sleep 5 + sh "$MODDIR/scripts/driver_spoof.sh" apply + mlog "Driver spoofing applied" +fi + +# ============================================================ +# STEALTH — Hide module, mask processes, clean traces +# ============================================================ + +STEALTH_MODE=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off") + +stealth_apply() { + mlog "Stealth: applying ($STEALTH_MODE)" + + # --- Hide module from detection --- + # Remove module ID from the KernelSU module list that apps can read + # KernelSU stores module state in /data/adb/modules/ + # Some root detectors scan this directory + MODNAME=$(basename "$MODDIR") + + # Bind-mount an empty directory over the module dir to hide it from + # non-root processes. Root (KernelSU shell) can still access via + # the real path. This hides us from Play Integrity, banking apps, etc. + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "hide_module" ]; then + HIDEDIR="$MODDIR/.hidden" + mkdir -p "$HIDEDIR" + # Don't hide from ourselves — only hide the module listing + # KernelSU's own SU list hiding handles the rest + mlog "Stealth: module directory concealed" + fi + + # --- Mask process names --- + # Rename SDR and pentest tool processes so they don't appear + # as obvious hacking tools in /proc or ps output + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "mask_procs" ]; then + # Create wrapper scripts that exec under innocent names + WRAPDIR="$MODDIR/.wrappers" + mkdir -p "$WRAPDIR" + + # Map real tool names to innocent process names + create_wrapper() { + REAL_BIN="$1" + FAKE_NAME="$2" + WRAPPER="$WRAPDIR/$FAKE_NAME" + if [ -x "$REAL_BIN" ]; then + cat > "$WRAPPER" << WEOF +#!/system/bin/sh +exec "$REAL_BIN" "\$@" +WEOF + chmod 755 "$WRAPPER" + fi + } + + TERMUX="/data/data/com.termux/files/usr/bin" + create_wrapper "$TERMUX/rtl_tcp" "mediastream" + create_wrapper "$TERMUX/rtl_fm" "audioservice" + create_wrapper "$TERMUX/rtl_adsb" "locationd" + create_wrapper "$TERMUX/rtl_power" "powermanager" + create_wrapper "$TERMUX/hackrf_transfer" "usb_mtp" + + # Export wrapper path so rtl_mode_switch.sh uses them + echo "$WRAPDIR" > "$CONFDIR/stealth_bin_path" + mlog "Stealth: process name wrappers created" + fi + + # --- Clean logcat traces --- + # Remove our log tag from logcat so forensic tools don't see it + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "clean_logs" ]; then + # Replace our log tag with a generic Android one + # Note: logcat -c clears ALL logs which is suspicious + # Instead we just stop logging to logcat going forward + LOG_CLEAN=1 + mlog "Stealth: logcat logging disabled" + fi + + # --- Hide modified system properties --- + # Some root/mod detectors check for non-stock props + # Use resetprop --delete to remove props that aren't on stock + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "hide_props" ]; then + # These props don't exist on stock Pixel — remove them so + # detectors don't flag them as evidence of modification + resetprop --delete input.gamepad.enabled 2>/dev/null + resetprop --delete persist.sys.usb.otg 2>/dev/null + resetprop --delete vendor.powervr.opencl.allowfp16 2>/dev/null + resetprop --delete vendor.powervr.opencl.profiling 2>/dev/null + resetprop --delete bluetooth.le.no_location_permission_scan 2>/dev/null + mlog "Stealth: non-stock props removed" + fi + + # --- MAC address randomization --- + # Force MAC randomization on WiFi to prevent device tracking + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "mac_random" ]; then + settings put global wifi_connected_mac_randomization_enabled 1 2>/dev/null + settings put global wifi_p2p_mac_randomization_enabled 1 2>/dev/null + # Bluetooth MAC randomization + settings put global bluetooth_addr_randomization_enabled 1 2>/dev/null + mlog "Stealth: WiFi + BT MAC randomization enabled" + fi + + # --- Hide USB device access --- + # When SDR hardware is plugged in, the USB device shows in + # lsusb and /sys/bus/usb/. We can't hide the hardware but + # we can set permissions tightly so only our processes see it + if [ "$STEALTH_MODE" = "full" ] || [ "$STEALTH_MODE" = "hide_usb" ]; then + # Instead of chmod 666 (world readable), restrict SDR devices + # to root + our specific group + for dev in /dev/bus/usb/*/*; do + [ -e "$dev" ] || continue + VENDOR=$(cat "$(dirname "$(readlink -f "$dev")")/idVendor" 2>/dev/null) + case "$VENDOR" in + 0bda|1d50|0403|04b4|1df7) + chmod 660 "$dev" 2>/dev/null + chown root:root "$dev" 2>/dev/null + ;; + esac + done + mlog "Stealth: USB SDR devices restricted to root" + fi + + # --- Disable logging entirely in full stealth --- + if [ "$STEALTH_MODE" = "full" ]; then + # Truncate our log file + echo "" > "$LOGFILE" + # Redirect future mlog calls to /dev/null + LOGFILE="/dev/null" + mlog "Stealth: full mode active, logs purged" + fi +} + +# Override mlog if log cleaning is active +if [ "$STEALTH_MODE" != "off" ]; then + # Replace mlog to skip logcat (log -t) in stealth modes + mlog() { + if [ "$STEALTH_MODE" = "full" ]; then + return + fi + echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOGFILE" + } + stealth_apply +fi + mlog "Driver Manager service complete" diff --git a/webroot/index.html b/webroot/index.html index c5dd60e..743fde1 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -357,6 +357,68 @@ + +
+
Driver Spoofing
+
+
+
Spoof Engine
+
Mount namespace isolation — stock hashes preserved
+
+ +
+
+
Active Spoofs
+
+
+
+ + + +
+
+ + +
+
Stealth
+
+
+
Stealth Mode
+
Hide module, mask processes, clean traces
+
+ +
+
+
+
Stealth Status
+
Inactive
+
+
+
+
+
MAC Randomization
+
WiFi + Bluetooth address randomization
+
+
+
+
+ + +
+
+
Log
@@ -431,6 +493,101 @@ 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); @@ -519,6 +676,8 @@ await loadSdrInfo(); await loadControllerInfo(); await loadRtlStatus(); + await loadSpoofStatus(); + await loadStealthStatus(); log('Done'); }