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

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