#!/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() {
    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 — Pixel 10 Pro Fold (rango)
# Format: stock_path|custom_filename|target_process|spoof_type
# spoof_type: process (per-process ns) or global (all processes)
#
# Stock file is NEVER modified. Place custom files in:
#   /data/adb/modules/driver-manager/drivers/
# Uncomment lines to activate.
#
# ---- GPU — PowerVR DXT-48-1536 ----
# SELinux: u:object_r:same_process_hal_file:s0
#/vendor/lib64/egl/libEGL_powervr.so|libEGL_powervr_custom.so|surfaceflinger|process
#/vendor/lib64/egl/libGLESv1_CM_powervr.so|libGLESv1_CM_powervr_custom.so|surfaceflinger|process
#/vendor/lib64/egl/libGLESv2_powervr.so|libGLESv2_powervr_custom.so|surfaceflinger|process
#/vendor/lib64/hw/vulkan.powervr.so|vulkan.powervr_custom.so|surfaceflinger|process
#/vendor/lib64/libPVROCL.so|libPVROCL_custom.so|surfaceflinger|process
#/vendor/lib64/hw/gralloc.default.so|gralloc.default_custom.so|surfaceflinger|process
#
# ---- WiFi — Broadcom BCM4390 firmware ----
# SELinux: u:object_r:vendor_fw_file:s0
# Active firmware: fw_bcmdhd.bin (symlink/copy of _4390_b1 variant)
# Revisions: _4390_a0 (first), _4390_b0, _4390_b1 (current)
#/vendor/firmware/fw_bcmdhd.bin|fw_bcm4390_custom.bin|wpa_supplicant|process
#/vendor/firmware/fw_bcmdhd.bin_4390_b1|fw_bcm4390_b1_custom.bin|wpa_supplicant|process
#/vendor/firmware/bcmdhd_clm.blob_4390_b1|bcmdhd_clm_custom.blob|wpa_supplicant|process
#/vendor/firmware/bcmdhd.cal_4390_b1|bcmdhd_cal_custom.cal|wpa_supplicant|process
#
# ---- Bluetooth — Broadcom (brcm) ----
# SELinux: u:object_r:vendor_fw_file:s0
# BCM.hcd = main BT firmware, BTFW_B.hcd = variant
#/vendor/firmware/brcm/BCM.hcd|BCM_custom.hcd|bluetooth|process
#/vendor/firmware/brcm/BTFW_B.hcd|BTFW_B_custom.hcd|bluetooth|process
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
