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.
299 lines
10 KiB
Bash
Executable File
299 lines
10 KiB
Bash
Executable File
#!/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
|