Add driver spoofing + stealth system

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.
This commit is contained in:
sssnake
2026-03-31 09:45:35 -07:00
parent 6e027b2c1b
commit b81de56601
6 changed files with 643 additions and 4 deletions

View File

@@ -39,6 +39,8 @@ echo "standard" > "$MODPATH/config/bt_mode"
echo "sdr" > "$MODPATH/config/sdr_mode" echo "sdr" > "$MODPATH/config/sdr_mode"
echo "auto" > "$MODPATH/config/gamepad_mode" echo "auto" > "$MODPATH/config/gamepad_mode"
echo "off" > "$MODPATH/config/decoder_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 "100.0M" > "$MODPATH/config/fm_freq"
echo "24M:1800M" > "$MODPATH/config/spectrum_range" echo "24M:1800M" > "$MODPATH/config/spectrum_range"

Binary file not shown.

298
scripts/driver_spoof.sh Executable file
View File

@@ -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

View File

@@ -10,13 +10,35 @@ MODDIR="/data/adb/modules/driver-manager"
CONFDIR="$MODDIR/config" CONFDIR="$MODDIR/config"
LOGFILE="$MODDIR/driver-manager.log" LOGFILE="$MODDIR/driver-manager.log"
PIDDIR="$MODDIR/run" PIDDIR="$MODDIR/run"
TERMUX="/data/data/com.termux/files/usr/bin"
STREAMDIR="$MODDIR/streams" 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" mkdir -p "$PIDDIR" "$STREAMDIR"
# Stealth-aware logging — skip logcat in stealth mode
STEALTH_MODE=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off")
mlog() { mlog() {
[ "$STEALTH_MODE" = "full" ] && return
echo "$(date '+%Y-%m-%d %H:%M:%S') [rtl_switch] $1" >> "$LOGFILE" 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 # Kill any running RTL process that holds the dongle
@@ -32,13 +54,18 @@ kill_rtl() {
fi fi
rm -f "$pidfile" rm -f "$pidfile"
done done
# Also catch any strays # Also catch any strays — both real and stealth names
pkill -f rtl_tcp 2>/dev/null pkill -f rtl_tcp 2>/dev/null
pkill -f rtl_fm 2>/dev/null pkill -f rtl_fm 2>/dev/null
pkill -f rtl_adsb 2>/dev/null pkill -f rtl_adsb 2>/dev/null
pkill -f rtl_power 2>/dev/null pkill -f rtl_power 2>/dev/null
pkill -f dvbt_rx 2>/dev/null pkill -f dvbt_rx 2>/dev/null
pkill -f sdr_tv 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 sleep 1
} }
@@ -50,8 +77,8 @@ start_rtl_tcp() {
SRATE=$(cat "$CONFDIR/rtl_samplerate" 2>/dev/null || echo "2048000") SRATE=$(cat "$CONFDIR/rtl_samplerate" 2>/dev/null || echo "2048000")
FREQ=$(cat "$CONFDIR/rtl_freq" 2>/dev/null || echo "100000000") FREQ=$(cat "$CONFDIR/rtl_freq" 2>/dev/null || echo "100000000")
if [ -x "$TERMUX/rtl_tcp" ]; then if [ -x "$RTL_TCP" ]; then
"$TERMUX/rtl_tcp" -a 127.0.0.1 -p "$PORT" -f "$FREQ" -s "$SRATE" -g "$GAIN" & "$RTL_TCP" -a 127.0.0.1 -p "$PORT" -f "$FREQ" -s "$SRATE" -g "$GAIN" &
echo $! > "$PIDDIR/rtl_tcp.pid" echo $! > "$PIDDIR/rtl_tcp.pid"
mlog "rtl_tcp started on port $PORT (freq=$FREQ srate=$SRATE gain=$GAIN)" mlog "rtl_tcp started on port $PORT (freq=$FREQ srate=$SRATE gain=$GAIN)"
else else

View File

@@ -324,4 +324,157 @@ case "$GAMEPAD_MODE" in
;; ;;
esac 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" mlog "Driver Manager service complete"

View File

@@ -357,6 +357,68 @@
</div> </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 --> <!-- Log -->
<div class="card"> <div class="card">
<div class="card-title">Log</div> <div class="card-title">Log</div>
@@ -431,6 +493,101 @@
if (fmFreq) document.getElementById('fmFreq').value = fmFreq; 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) { async function setMode(file, value) {
log('Setting ' + file + ' = ' + value); log('Setting ' + file + ' = ' + value);
await exec('echo "' + value + '" > ' + MODDIR + '/config/' + file); await exec('echo "' + value + '" > ' + MODDIR + '/config/' + file);
@@ -519,6 +676,8 @@
await loadSdrInfo(); await loadSdrInfo();
await loadControllerInfo(); await loadControllerInfo();
await loadRtlStatus(); await loadRtlStatus();
await loadSpoofStatus();
await loadStealthStatus();
log('Done'); log('Done');
} }