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:
@@ -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
298
scripts/driver_spoof.sh
Executable 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
|
||||||
@@ -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
|
||||||
|
|||||||
153
service.sh
153
service.sh
@@ -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"
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user