diff --git a/customize.sh b/customize.sh new file mode 100644 index 0000000..69c8975 --- /dev/null +++ b/customize.sh @@ -0,0 +1,101 @@ +#!/system/bin/sh +# Driver Manager v2 - Installation Script +# Runs during KernelSU module installation + +MODDIR=${MODPATH:-/data/adb/modules/driver-manager} +CONFIGDIR="$MODDIR/config" +MODULESDIR="$MODDIR/modules" +DRIVERSDIR="$MODDIR/drivers" +BACKUPDIR="$MODDIR/backup" +LOGFILE="$MODDIR/install.log" + +log() { echo "[driver-manager] $1" >> "$LOGFILE" 2>/dev/null; ui_print "$1"; } + +ui_print "================================================" +ui_print " Driver Manager v2.0.0" +ui_print " LSPosed-style driver management" +ui_print "================================================" + +# --- Detect Hardware --- +DEVICE=$(getprop ro.product.device 2>/dev/null || echo "unknown") +MODEL=$(getprop ro.product.model 2>/dev/null || echo "unknown") +SOC=$(getprop ro.hardware.chipname 2>/dev/null || getprop ro.board.platform 2>/dev/null || echo "unknown") +ARCH=$(getprop ro.product.cpu.abi 2>/dev/null || echo "arm64-v8a") +API=$(getprop ro.build.version.sdk 2>/dev/null || echo "35") +KERNEL=$(uname -r 2>/dev/null || echo "unknown") + +ui_print " Device: $MODEL ($DEVICE)" +ui_print " SoC: $SOC" +ui_print " Arch: $ARCH" +ui_print " API: $API" +ui_print " Kernel: $KERNEL" +ui_print "------------------------------------------------" + +# --- Create directories --- +mkdir -p "$CONFIGDIR" +mkdir -p "$MODULESDIR" +mkdir -p "$DRIVERSDIR" +mkdir -p "$BACKUPDIR" +mkdir -p "$MODDIR/run" +mkdir -p "$MODDIR/logs" + +# --- Device profile --- +cat > "$CONFIGDIR/device.json" << DEVEOF +{ + "device": "$DEVICE", + "model": "$MODEL", + "soc": "$SOC", + "arch": "$ARCH", + "api": $API, + "kernel": "$KERNEL", + "install_time": "$(date -Iseconds 2>/dev/null || date)" +} +DEVEOF + +# --- Default configuration --- +cat > "$CONFIGDIR/settings.json" << SETEOF +{ + "protection_mode": "monitor", + "protection_interval": 300, + "scope_mode": "app", + "log_level": "info", + "auto_apply_scopes": true, + "monitor_zygote": true +} +SETEOF + +# --- Initialize empty scopes --- +[ ! -f "$CONFIGDIR/scopes.json" ] && echo '{"scopes":[]}' > "$CONFIGDIR/scopes.json" + +# --- Initialize empty autoload --- +[ ! -f "$CONFIGDIR/autoload.conf" ] && touch "$CONFIGDIR/autoload.conf" + +# --- Scan and register system drivers --- +ui_print " Scanning system drivers..." +if [ -f "$MODDIR/scripts/driver_registry.sh" ]; then + sh "$MODDIR/scripts/driver_registry.sh" scan 2>> "$LOGFILE" + DRIVER_COUNT=$(sh "$MODDIR/scripts/driver_registry.sh" count 2>/dev/null || echo "0") + ui_print " Found $DRIVER_COUNT driver categories" +else + ui_print " [WARN] driver_registry.sh not found, will scan on first boot" +fi + +# --- Create protection baseline --- +ui_print " Creating driver protection baseline..." +if [ -f "$MODDIR/scripts/protect.sh" ]; then + sh "$MODDIR/scripts/protect.sh" baseline 2>> "$LOGFILE" + ui_print " Protection baseline created" +else + ui_print " [WARN] protect.sh not found, will baseline on first boot" +fi + +# --- Set permissions --- +set_perm_recursive $MODDIR/scripts 0 0 0755 0755 +set_perm $MODDIR/service.sh 0 0 0755 +set_perm $MODDIR/post-fs-data.sh 0 0 0755 +set_perm $MODDIR/uninstall.sh 0 0 0755 + +ui_print "------------------------------------------------" +ui_print " Installation complete!" +ui_print " Open KernelSU WebUI to manage drivers" +ui_print "================================================" diff --git a/.gitkeep b/drivers/.gitkeep similarity index 100% rename from .gitkeep rename to drivers/.gitkeep diff --git a/module.prop b/module.prop new file mode 100644 index 0000000..8bfa405 --- /dev/null +++ b/module.prop @@ -0,0 +1,6 @@ +id=driver-manager +name=Driver Manager +version=v2.0.0 +versionCode=200 +author=sssnake +description=LSPosed-style driver manager with per-app scoping, .ko module management, and driver protection for Pixel devices diff --git a/post-fs-data.sh b/post-fs-data.sh new file mode 100644 index 0000000..378f100 --- /dev/null +++ b/post-fs-data.sh @@ -0,0 +1,25 @@ +#!/system/bin/sh +# Driver Manager v2 - Early Boot Script +# Runs before most system services start +# Keep minimal to avoid boot delays + +MODDIR=${0%/*} +LOGFILE="$MODDIR/logs/boot.log" + +echo "[$(date)] post-fs-data: starting" > "$LOGFILE" + +# Load any kernel modules marked for early loading +if [ -f "$MODDIR/config/autoload.conf" ]; then + while IFS= read -r mod; do + [ -z "$mod" ] && continue + [ "${mod#\#}" != "$mod" ] && continue + MODFILE="$MODDIR/modules/$mod" + if [ -f "$MODFILE" ]; then + insmod "$MODFILE" 2>> "$LOGFILE" && \ + echo "[$(date)] Loaded early module: $mod" >> "$LOGFILE" || \ + echo "[$(date)] FAILED to load early module: $mod" >> "$LOGFILE" + fi + done < "$MODDIR/config/autoload.conf" +fi + +echo "[$(date)] post-fs-data: complete" >> "$LOGFILE" diff --git a/scripts/api.sh b/scripts/api.sh new file mode 100644 index 0000000..2109d9f --- /dev/null +++ b/scripts/api.sh @@ -0,0 +1,148 @@ +#!/system/bin/sh +# Driver Manager v2 - WebUI API Handler +# Processes commands from KernelSU WebUI via ksu.exec() + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +. "$MODDIR/scripts/core.sh" + +CONFIGDIR="$MODDIR/config" + +# Route: system +api_system() { + case "$1" in + info) + local device=$(cat "$CONFIGDIR/device.json" 2>/dev/null || echo '{}') + local kernel=$(uname -r 2>/dev/null) + local uptime=$(cat /proc/uptime 2>/dev/null | awk '{print $1}') + echo "{\"device\": $device, \"kernel\": \"$kernel\", \"uptime\": \"$uptime\"}" + ;; + logs) + local logfile="${2:-service}" + local lines="${3:-100}" + local f="$LOGDIR/${logfile}.log" + if [ -f "$f" ]; then + local content=$(tail -n "$lines" "$f" | sed 's/"/\\"/g' | tr '\n' '|') + echo "{\"log\": \"$content\", \"file\": \"$logfile\"}" + else + echo '{"log": "", "file": "'"$logfile"'"}' + fi + ;; + *) + echo '{"error": "Unknown system command"}' + ;; + esac +} + +# Route: drivers +api_drivers() { + case "$1" in + list) sh "$MODDIR/scripts/driver_registry.sh" list ;; + scan) sh "$MODDIR/scripts/driver_registry.sh" scan ;; + get) sh "$MODDIR/scripts/driver_registry.sh" get "$2" ;; + count) echo "{\"count\": $(sh "$MODDIR/scripts/driver_registry.sh" count)}" ;; + verify) sh "$MODDIR/scripts/driver_registry.sh" verify "$2" ;; + add_variant) sh "$MODDIR/scripts/driver_registry.sh" add_variant "$2" "$3" "$4" ;; + *) echo '{"error": "Unknown drivers command"}' ;; + esac +} + +# Route: scope +api_scope() { + case "$1" in + list) sh "$MODDIR/scripts/scope_manager.sh" list ;; + set) sh "$MODDIR/scripts/scope_manager.sh" set "$2" "$3" "$4" "$5" "$6" ;; + remove) sh "$MODDIR/scripts/scope_manager.sh" remove "$2" ;; + apply) sh "$MODDIR/scripts/scope_manager.sh" apply ;; + unmount) sh "$MODDIR/scripts/scope_manager.sh" unmount_all ;; + *) echo '{"error": "Unknown scope command"}' ;; + esac +} + +# Route: ko (kernel modules) +api_ko() { + case "$1" in + list) sh "$MODDIR/scripts/ko_manager.sh" list ;; + load) sh "$MODDIR/scripts/ko_manager.sh" load "$2" "$3" ;; + unload) sh "$MODDIR/scripts/ko_manager.sh" unload "$2" ;; + unload_all) sh "$MODDIR/scripts/ko_manager.sh" unload_all ;; + info) sh "$MODDIR/scripts/ko_manager.sh" info "$2" ;; + autoload) sh "$MODDIR/scripts/ko_manager.sh" set_autoload "$2" "$3" ;; + *) echo '{"error": "Unknown ko command"}' ;; + esac +} + +# Route: protect +api_protect() { + case "$1" in + status) sh "$MODDIR/scripts/protect.sh" status ;; + check) sh "$MODDIR/scripts/protect.sh" check ;; + baseline) sh "$MODDIR/scripts/protect.sh" baseline ;; + mode) sh "$MODDIR/scripts/protect.sh" mode "$2" ;; + driver) sh "$MODDIR/scripts/protect.sh" protect "$2" "$3" ;; + changelog) sh "$MODDIR/scripts/protect.sh" changelog "$2" ;; + *) echo '{"error": "Unknown protect command"}' ;; + esac +} + +# Route: apps +api_apps() { + case "$1" in + list) + echo '{"apps":[' + local first=true + # List installed packages with labels + pm list packages -f 2>/dev/null | while IFS= read -r line; do + local apk=$(echo "$line" | sed 's/package:\(.*\)=\(.*\)/\1/') + local pkg=$(echo "$line" | sed 's/package:\(.*\)=\(.*\)/\2/') + [ -z "$pkg" ] && continue + + # Get app label via dumpsys (fast path) + local label=$(dumpsys package "$pkg" 2>/dev/null | grep -m1 'applicationInfo' -A5 | grep -o 'label=[^ ]*' | cut -d= -f2) + [ -z "$label" ] && label="$pkg" + + # Check if system app + local system="false" + echo "$apk" | grep -q '/system/' && system="true" + + [ "$first" = true ] && first=false || echo "," + echo " {\"package\": \"$pkg\", \"label\": \"$label\", \"system\": $system}" + done + echo ']}' + ;; + *) + echo '{"error": "Unknown apps command"}' + ;; + esac +} + +# Route: settings +api_settings() { + case "$1" in + get) + cat "$CONFIGDIR/settings.json" 2>/dev/null || echo '{}' + ;; + set) + local key="$2" value="$3" + json_set "$CONFIGDIR/settings.json" "$key" "$value" + echo "{\"status\": \"ok\", \"$key\": \"$value\"}" + ;; + *) + echo '{"error": "Unknown settings command"}' + ;; + esac +} + +# --- Main router --- +ROUTE="$1" +shift + +case "$ROUTE" in + system) api_system "$@" ;; + drivers) api_drivers "$@" ;; + scope) api_scope "$@" ;; + ko) api_ko "$@" ;; + protect) api_protect "$@" ;; + apps) api_apps "$@" ;; + settings) api_settings "$@" ;; + *) echo '{"error": "Unknown route. Routes: system, drivers, scope, ko, protect, apps, settings"}' ;; +esac diff --git a/scripts/core.sh b/scripts/core.sh new file mode 100644 index 0000000..c34f8ea --- /dev/null +++ b/scripts/core.sh @@ -0,0 +1,85 @@ +#!/system/bin/sh +# Driver Manager v2 - Core Utilities +# Shared functions for all scripts + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +CONFIGDIR="$MODDIR/config" +LOGDIR="$MODDIR/logs" +RUNDIR="$MODDIR/run" + +# --- Logging --- +_LOGFILE="" + +log_init() { + _LOGFILE="$1" + mkdir -p "$(dirname "$_LOGFILE")" 2>/dev/null +} + +log() { + local msg="[$(date '+%H:%M:%S')] $1" + [ -n "$_LOGFILE" ] && echo "$msg" >> "$_LOGFILE" +} + +log_error() { log "ERROR: $1"; } + +# --- JSON helpers (minimal, no jq dependency) --- +# Read a string value from a JSON file +json_get() { + local file="$1" key="$2" + grep -o "\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" "$file" 2>/dev/null | \ + head -1 | sed 's/.*:[[:space:]]*"\([^"]*\)"/\1/' +} + +# Read a number/bool value from a JSON file +json_get_raw() { + local file="$1" key="$2" + grep -o "\"$key\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null | \ + head -1 | sed 's/.*:[[:space:]]*//' | tr -d ' ' +} + +# Set a string value in a JSON file +json_set() { + local file="$1" key="$2" value="$3" + if grep -q "\"$key\"" "$file" 2>/dev/null; then + sed -i "s|\"$key\"[[:space:]]*:[[:space:]]*\"[^\"]*\"|\"$key\": \"$value\"|" "$file" + fi +} + +# --- File helpers --- +sha256() { + sha256sum "$1" 2>/dev/null | cut -d' ' -f1 +} + +get_selinux_context() { + ls -Z "$1" 2>/dev/null | awk '{print $1}' +} + +file_size() { + stat -c%s "$1" 2>/dev/null || wc -c < "$1" 2>/dev/null +} + +# --- Process helpers --- +pid_of() { + pidof "$1" 2>/dev/null || pgrep -f "$1" 2>/dev/null +} + +is_running() { + [ -f "$RUNDIR/$1.pid" ] && kill -0 $(cat "$RUNDIR/$1.pid") 2>/dev/null +} + +kill_by_pidfile() { + local pidfile="$RUNDIR/$1.pid" + if [ -f "$pidfile" ]; then + kill $(cat "$pidfile") 2>/dev/null + rm -f "$pidfile" + fi +} + +# --- Settings helpers --- +get_setting() { + json_get "$CONFIGDIR/settings.json" "$1" +} + +get_setting_raw() { + json_get_raw "$CONFIGDIR/settings.json" "$1" +} diff --git a/scripts/driver_registry.sh b/scripts/driver_registry.sh new file mode 100644 index 0000000..eb908a1 --- /dev/null +++ b/scripts/driver_registry.sh @@ -0,0 +1,309 @@ +#!/system/bin/sh +# Driver Manager v2 - Driver Registry +# Discovers, catalogs, and manages system drivers + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +. "$MODDIR/scripts/core.sh" +log_init "$LOGDIR/registry.log" + +REGISTRY="$CONFIGDIR/drivers.json" + +# --- Driver discovery paths by category --- +scan_drivers() { + log "Starting driver scan" + local tmp="$CONFIGDIR/.drivers_tmp.json" + + cat > "$tmp" << 'HEADER' +{"drivers":[ +HEADER + + local first=true + + # GPU drivers + for path in /vendor/lib64/egl /vendor/lib/egl /system/lib64/egl; do + [ -d "$path" ] || continue + for f in "$path"/lib*.so; do + [ -f "$f" ] || continue + local name=$(basename "$f" .so) + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "gpu-$name", + "name": "$name", + "category": "gpu", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": true, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Vulkan ICD + for path in /vendor/lib64/hw /vendor/lib/hw; do + [ -d "$path" ] || continue + for f in "$path"/vulkan.*.so; do + [ -f "$f" ] || continue + local name=$(basename "$f" .so) + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "vulkan-$name", + "name": "$name", + "category": "gpu", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": true, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # WiFi firmware + for path in /vendor/firmware /vendor/etc/wifi /lib/firmware; do + [ -d "$path" ] || continue + for f in "$path"/fw_bcm*.bin "$path"/fw_wcn*.bin "$path"/wlan_*.bin; do + [ -f "$f" ] || continue + local name=$(basename "$f") + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "wifi-$name", + "name": "$name", + "category": "wifi", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": true, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Bluetooth firmware + for path in /vendor/firmware /vendor/etc/bluetooth /lib/firmware; do + [ -d "$path" ] || continue + for f in "$path"/*.hcd "$path"/bt_fw*.bin "$path"/BCM*.hcd; do + [ -f "$f" ] || continue + local name=$(basename "$f") + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "bt-$name", + "name": "$name", + "category": "bluetooth", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": true, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Audio HAL + for path in /vendor/lib64/hw /vendor/lib/hw; do + [ -d "$path" ] || continue + for f in "$path"/audio*.so; do + [ -f "$f" ] || continue + local name=$(basename "$f" .so) + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "audio-$name", + "name": "$name", + "category": "audio", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": true, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Camera HAL + for path in /vendor/lib64/hw /vendor/lib/hw; do + [ -d "$path" ] || continue + for f in "$path"/camera*.so; do + [ -f "$f" ] || continue + local name=$(basename "$f" .so) + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "camera-$name", + "name": "$name", + "category": "camera", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": false, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Sensor HAL + for path in /vendor/lib64/hw /vendor/lib/hw; do + [ -d "$path" ] || continue + for f in "$path"/sensors*.so; do + [ -f "$f" ] || continue + local name=$(basename "$f" .so) + local hash=$(sha256 "$f") + local ctx=$(get_selinux_context "$f") + local sz=$(file_size "$f") + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "sensor-$name", + "name": "$name", + "category": "sensor", + "path": "$f", + "hash": "$hash", + "selinux": "$ctx", + "size": $sz, + "protected": false, + "variants": [{"name": "stock", "path": "$f", "hash": "$hash", "active": true}] + } +ENTRY + done + done + + # Kernel modules currently loaded + if [ -f /proc/modules ]; then + while read -r name rest; do + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "id": "kmod-$name", + "name": "$name", + "category": "kernel", + "path": "/proc/modules", + "hash": "", + "selinux": "", + "size": 0, + "protected": false, + "variants": [{"name": "loaded", "path": "", "hash": "", "active": true}] + } +ENTRY + done < /proc/modules + fi + + echo "" >> "$tmp" + echo "]}" >> "$tmp" + + mv "$tmp" "$REGISTRY" + log "Driver scan complete" +} + +# --- Count drivers --- +count_drivers() { + if [ -f "$REGISTRY" ]; then + grep -c '"id"' "$REGISTRY" 2>/dev/null || echo "0" + else + echo "0" + fi +} + +# --- List drivers as JSON --- +list_drivers() { + [ -f "$REGISTRY" ] && cat "$REGISTRY" || echo '{"drivers":[]}' +} + +# --- Get single driver info --- +get_driver() { + local id="$1" + if [ -f "$REGISTRY" ]; then + # Extract the driver block matching the id + awk -v id="$id" ' + /"id".*:.*"/ { found = ($0 ~ "\"" id "\"") } + found && /\{/ { depth++ } + found && depth > 0 { print } + found && /\}/ { depth--; if (depth == 0) { found=0 } } + ' "$REGISTRY" + fi +} + +# --- Add a custom variant to a driver --- +add_variant() { + local driver_id="$1" + local variant_name="$2" + local variant_path="$3" + + if [ ! -f "$variant_path" ]; then + echo '{"error": "Variant file not found"}' + return 1 + fi + + local hash=$(sha256 "$variant_path") + log "Adding variant '$variant_name' to driver '$driver_id': $variant_path (hash: $hash)" + + # Copy variant to drivers directory + local dest="$MODDIR/drivers/${driver_id}_${variant_name}_$(basename "$variant_path")" + cp "$variant_path" "$dest" + chmod 644 "$dest" + + echo "{\"status\": \"ok\", \"driver\": \"$driver_id\", \"variant\": \"$variant_name\", \"path\": \"$dest\", \"hash\": \"$hash\"}" +} + +# --- Verify a driver's current hash matches registry --- +verify_driver() { + local id="$1" + local reg_path=$(grep -A5 "\"$id\"" "$REGISTRY" 2>/dev/null | grep '"path"' | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/') + local reg_hash=$(grep -A5 "\"$id\"" "$REGISTRY" 2>/dev/null | grep '"hash"' | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/') + + if [ -z "$reg_path" ] || [ -z "$reg_hash" ]; then + echo '{"status": "unknown", "reason": "driver not in registry"}' + return 1 + fi + + local cur_hash=$(sha256 "$reg_path") + if [ "$cur_hash" = "$reg_hash" ]; then + echo "{\"status\": \"ok\", \"driver\": \"$id\", \"hash\": \"$cur_hash\"}" + else + echo "{\"status\": \"changed\", \"driver\": \"$id\", \"expected\": \"$reg_hash\", \"actual\": \"$cur_hash\"}" + fi +} + +# --- Main --- +case "$1" in + scan) scan_drivers ;; + count) count_drivers ;; + list) list_drivers ;; + get) get_driver "$2" ;; + add_variant) add_variant "$2" "$3" "$4" ;; + verify) verify_driver "$2" ;; + *) echo "Usage: driver_registry.sh {scan|count|list|get|add_variant|verify} [args]" ;; +esac diff --git a/scripts/ko_manager.sh b/scripts/ko_manager.sh new file mode 100644 index 0000000..03b6bf7 --- /dev/null +++ b/scripts/ko_manager.sh @@ -0,0 +1,228 @@ +#!/system/bin/sh +# Driver Manager v2 - Kernel Module (.ko) Manager +# Load, unload, and manage kernel modules + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +. "$MODDIR/scripts/core.sh" +log_init "$LOGDIR/ko.log" + +MODULESDIR="$MODDIR/modules" +AUTOLOAD="$CONFIGDIR/autoload.conf" +KERNEL_VER=$(uname -r 2>/dev/null) + +# --- List available .ko files and their status --- +list_modules() { + echo '{"modules":[' + local first=true + + # List .ko files in our modules directory + for ko in "$MODULESDIR"/*.ko; do + [ -f "$ko" ] || continue + local name=$(basename "$ko" .ko) + local modname=$(modinfo -F name "$ko" 2>/dev/null || echo "$name") + local desc=$(modinfo -F description "$ko" 2>/dev/null || echo "") + local ver=$(modinfo -F version "$ko" 2>/dev/null || echo "") + local author=$(modinfo -F author "$ko" 2>/dev/null || echo "") + local depends=$(modinfo -F depends "$ko" 2>/dev/null || echo "") + local vermagic=$(modinfo -F vermagic "$ko" 2>/dev/null || echo "") + local sz=$(file_size "$ko") + + # Check if loaded + local loaded="false" + lsmod 2>/dev/null | grep -q "^${modname} " && loaded="true" + + # Check if in autoload + local autoload="false" + grep -qx "$name.ko" "$AUTOLOAD" 2>/dev/null && autoload="true" + + # Check kernel version compatibility + local compatible="unknown" + if [ -n "$vermagic" ]; then + local mod_kver=$(echo "$vermagic" | awk '{print $1}') + [ "$mod_kver" = "$KERNEL_VER" ] && compatible="true" || compatible="false" + fi + + [ "$first" = true ] && first=false || echo "," + cat << ENTRY + { + "filename": "$(basename "$ko")", + "name": "$modname", + "description": "$desc", + "version": "$ver", + "author": "$author", + "depends": "$depends", + "vermagic": "$vermagic", + "size": $sz, + "loaded": $loaded, + "autoload": $autoload, + "compatible": $compatible + } +ENTRY + done + + echo ']}' +} + +# --- Load a kernel module --- +load_module() { + local ko="$1" + local params="$2" + + # Accept either filename or path + if [ ! -f "$ko" ]; then + ko="$MODULESDIR/$ko" + fi + if [ ! -f "$ko" ]; then + echo '{"status": "error", "message": "Module file not found"}' + return 1 + fi + + local name=$(basename "$ko" .ko) + local modname=$(modinfo -F name "$ko" 2>/dev/null || echo "$name") + + # Check if already loaded + if lsmod 2>/dev/null | grep -q "^${modname} "; then + echo "{\"status\": \"already_loaded\", \"module\": \"$modname\"}" + return 0 + fi + + # Check kernel version + local vermagic=$(modinfo -F vermagic "$ko" 2>/dev/null) + if [ -n "$vermagic" ]; then + local mod_kver=$(echo "$vermagic" | awk '{print $1}') + if [ "$mod_kver" != "$KERNEL_VER" ]; then + log_error "Kernel mismatch: module=$mod_kver running=$KERNEL_VER" + echo "{\"status\": \"error\", \"message\": \"Kernel version mismatch: module built for $mod_kver, running $KERNEL_VER\"}" + return 1 + fi + fi + + # Load dependencies first + local depends=$(modinfo -F depends "$ko" 2>/dev/null) + if [ -n "$depends" ]; then + local IFS=',' + for dep in $depends; do + if ! lsmod 2>/dev/null | grep -q "^${dep} "; then + local dep_ko="$MODULESDIR/${dep}.ko" + if [ -f "$dep_ko" ]; then + log "Loading dependency: $dep" + insmod "$dep_ko" 2>> "$LOGDIR/ko.log" + else + log "Dependency $dep not in modules dir, trying modprobe" + modprobe "$dep" 2>> "$LOGDIR/ko.log" + fi + fi + done + fi + + # Load the module + log "Loading module: $ko $params" + local output + output=$(insmod "$ko" $params 2>&1) + if [ $? -eq 0 ]; then + log "Loaded: $modname" + # Capture relevant dmesg + local dmesg_out=$(dmesg | tail -5 2>/dev/null) + echo "{\"status\": \"ok\", \"module\": \"$modname\", \"dmesg\": \"$(echo "$dmesg_out" | tr '\n' '|' | sed 's/"/\\"/g')\"}" + else + log_error "Failed to load $modname: $output" + echo "{\"status\": \"error\", \"module\": \"$modname\", \"message\": \"$(echo "$output" | tr '\n' ' ' | sed 's/"/\\"/g')\"}" + return 1 + fi +} + +# --- Unload a kernel module --- +unload_module() { + local name="$1" + name=$(echo "$name" | sed 's/\.ko$//') + + if ! lsmod 2>/dev/null | grep -q "^${name} "; then + echo "{\"status\": \"not_loaded\", \"module\": \"$name\"}" + return 0 + fi + + log "Unloading module: $name" + local output + output=$(rmmod "$name" 2>&1) + if [ $? -eq 0 ]; then + log "Unloaded: $name" + echo "{\"status\": \"ok\", \"module\": \"$name\"}" + else + log_error "Failed to unload $name: $output" + echo "{\"status\": \"error\", \"module\": \"$name\", \"message\": \"$(echo "$output" | tr '\n' ' ' | sed 's/"/\\"/g')\"}" + return 1 + fi +} + +# --- Unload all managed modules --- +unload_all() { + log "Unloading all managed modules" + for ko in "$MODULESDIR"/*.ko; do + [ -f "$ko" ] || continue + local name=$(basename "$ko" .ko) + local modname=$(modinfo -F name "$ko" 2>/dev/null || echo "$name") + if lsmod 2>/dev/null | grep -q "^${modname} "; then + rmmod "$modname" 2>/dev/null + log "Unloaded: $modname" + fi + done + echo '{"status": "ok"}' +} + +# --- Get detailed info for a module --- +info_module() { + local ko="$1" + if [ ! -f "$ko" ]; then + ko="$MODULESDIR/$ko" + fi + [ ! -f "$ko" ] && { echo '{"error": "not found"}'; return 1; } + + modinfo "$ko" 2>/dev/null +} + +# --- Toggle autoload for a module --- +set_autoload() { + local ko_filename="$1" + local enabled="$2" + + if [ "$enabled" = "true" ] || [ "$enabled" = "1" ]; then + if ! grep -qx "$ko_filename" "$AUTOLOAD" 2>/dev/null; then + echo "$ko_filename" >> "$AUTOLOAD" + log "Autoload enabled: $ko_filename" + fi + echo "{\"status\": \"ok\", \"autoload\": true, \"module\": \"$ko_filename\"}" + else + sed -i "/^${ko_filename}$/d" "$AUTOLOAD" 2>/dev/null + log "Autoload disabled: $ko_filename" + echo "{\"status\": \"ok\", \"autoload\": false, \"module\": \"$ko_filename\"}" + fi +} + +# --- Load all autoload modules --- +autoload_modules() { + [ ! -f "$AUTOLOAD" ] && return 0 + log "Loading autoload modules" + + while IFS= read -r ko; do + [ -z "$ko" ] && continue + [ "${ko#\#}" != "$ko" ] && continue + local kofile="$MODULESDIR/$ko" + if [ -f "$kofile" ]; then + load_module "$kofile" >/dev/null 2>&1 + else + log_error "Autoload module not found: $ko" + fi + done < "$AUTOLOAD" +} + +# --- Main --- +case "$1" in + list) list_modules ;; + load) load_module "$2" "$3" ;; + unload) unload_module "$2" ;; + unload_all) unload_all ;; + info) info_module "$2" ;; + autoload) autoload_modules ;; + set_autoload) set_autoload "$2" "$3" ;; + *) echo "Usage: ko_manager.sh {list|load|unload|unload_all|info|autoload|set_autoload} [args]" ;; +esac diff --git a/scripts/protect.sh b/scripts/protect.sh new file mode 100644 index 0000000..841b192 --- /dev/null +++ b/scripts/protect.sh @@ -0,0 +1,233 @@ +#!/system/bin/sh +# Driver Manager v2 - Driver Integrity Protection +# Monitor system drivers for unauthorized changes + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +. "$MODDIR/scripts/core.sh" +log_init "$LOGDIR/protect.log" + +BASELINE="$CONFIGDIR/baseline.json" +REGISTRY="$CONFIGDIR/drivers.json" +BACKUPDIR="$MODDIR/backup" +CHANGELOG="$LOGDIR/changes.log" + +# --- Create SHA256 baseline of all protected drivers --- +create_baseline() { + log "Creating protection baseline" + [ ! -f "$REGISTRY" ] && { echo '{"error": "No driver registry. Run scan first."}'; return 1; } + + local tmp="$CONFIGDIR/.baseline_tmp.json" + echo '{"baseline":[' > "$tmp" + local first=true + + # Extract protected driver paths from registry + local in_driver=false protected=false path="" hash="" + + while IFS= read -r line; do + case "$line" in + *'"path"'*) + path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"protected"'*true*) + protected=true + ;; + *'"protected"'*false*) + protected=false + ;; + *\}*) + if [ "$protected" = true ] && [ -n "$path" ] && [ -f "$path" ]; then + local cur_hash=$(sha256 "$path") + local sz=$(file_size "$path") + local ctx=$(get_selinux_context "$path") + + [ "$first" = true ] && first=false || echo "," >> "$tmp" + cat >> "$tmp" << ENTRY + { + "path": "$path", + "hash": "$cur_hash", + "size": $sz, + "selinux": "$ctx", + "time": "$(date -Iseconds 2>/dev/null || date)" + } +ENTRY + + # Backup the file + local bkdir="$BACKUPDIR/$(dirname "$path" | sed 's|^/||')" + mkdir -p "$bkdir" + cp -p "$path" "$bkdir/" 2>/dev/null + fi + path="" protected=false + ;; + esac + done < "$REGISTRY" + + echo "" >> "$tmp" + echo '], "created": "'"$(date -Iseconds 2>/dev/null || date)"'"}' >> "$tmp" + mv "$tmp" "$BASELINE" + + local count=$(grep -c '"path"' "$BASELINE" 2>/dev/null || echo 0) + log "Baseline created: $count drivers protected" + echo "{\"status\": \"ok\", \"protected_count\": $count}" +} + +# --- Check all protected drivers against baseline --- +check_integrity() { + [ ! -f "$BASELINE" ] && { echo '{"error": "No baseline. Run baseline first."}'; return 1; } + + log "Running integrity check" + local changes=0 checked=0 ok=0 + local results='{"results":[' + local first=true + + local path="" expected_hash="" + + while IFS= read -r line; do + case "$line" in + *'"path"'*) + path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"hash"'*) + expected_hash=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *\}*) + if [ -n "$path" ] && [ -n "$expected_hash" ]; then + checked=$((checked + 1)) + local status="ok" + + if [ ! -f "$path" ]; then + status="missing" + changes=$((changes + 1)) + echo "[$(date)] MISSING: $path" >> "$CHANGELOG" + log_error "Protected driver missing: $path" + else + local cur_hash=$(sha256 "$path") + if [ "$cur_hash" != "$expected_hash" ]; then + status="changed" + changes=$((changes + 1)) + echo "[$(date)] CHANGED: $path (expected: $expected_hash, got: $cur_hash)" >> "$CHANGELOG" + log_error "Protected driver changed: $path" + + # Auto-restore in enforce mode + local mode=$(get_setting "protection_mode") + if [ "$mode" = "enforce" ]; then + local backup="$BACKUPDIR/${path#/}" + if [ -f "$backup" ]; then + cp -p "$backup" "$path" 2>/dev/null + log "Auto-restored: $path" + echo "[$(date)] RESTORED: $path" >> "$CHANGELOG" + status="restored" + else + log_error "Cannot restore $path: no backup" + fi + fi + else + ok=$((ok + 1)) + fi + fi + + [ "$first" = true ] && first=false || results="$results," + results="$results{\"path\":\"$path\",\"status\":\"$status\"}" + fi + path="" expected_hash="" + ;; + esac + done < "$BASELINE" + + results="$results],\"checked\":$checked,\"ok\":$ok,\"changes\":$changes,\"time\":\"$(date -Iseconds 2>/dev/null || date)\"}" + log "Integrity check: $checked checked, $ok ok, $changes changes" + echo "$results" +} + +# --- Get protection status --- +get_status() { + local mode=$(get_setting "protection_mode") + local protected=0 last_check="never" + + [ -f "$BASELINE" ] && protected=$(grep -c '"path"' "$BASELINE" 2>/dev/null || echo 0) + + # Get last check time from changelog + if [ -f "$CHANGELOG" ]; then + last_check=$(tail -1 "$CHANGELOG" 2>/dev/null | grep -o '^\[[^]]*\]' | tr -d '[]') + fi + + local watcher_running="false" + is_running "protect" && watcher_running="true" + + echo "{\"mode\": \"$mode\", \"protected_count\": $protected, \"last_check\": \"$last_check\", \"watcher_running\": $watcher_running}" +} + +# --- Set protection mode --- +set_mode() { + local mode="$1" + case "$mode" in + off|monitor|enforce) + json_set "$CONFIGDIR/settings.json" "protection_mode" "$mode" + log "Protection mode set to: $mode" + + if [ "$mode" = "off" ]; then + kill_by_pidfile "protect" + fi + + echo "{\"status\": \"ok\", \"mode\": \"$mode\"}" + ;; + *) + echo '{"error": "Invalid mode. Use: off, monitor, enforce"}' + ;; + esac +} + +# --- Toggle protection for a specific driver --- +set_driver_protection() { + local driver_id="$1" + local protected="$2" + + if [ -f "$REGISTRY" ]; then + if [ "$protected" = "true" ]; then + sed -i "/$driver_id/,/protected/{s/\"protected\": false/\"protected\": true/}" "$REGISTRY" + else + sed -i "/$driver_id/,/protected/{s/\"protected\": true/\"protected\": false/}" "$REGISTRY" + fi + log "Driver $driver_id protection: $protected" + echo "{\"status\": \"ok\", \"driver\": \"$driver_id\", \"protected\": $protected}" + else + echo '{"error": "No driver registry"}' + fi +} + +# --- Watch loop (runs as background daemon) --- +watch_loop() { + log "Protection watcher started" + while true; do + local interval=$(get_setting_raw "protection_interval") + [ -z "$interval" ] && interval=300 + + sleep "$interval" + + local mode=$(get_setting "protection_mode") + [ "$mode" = "off" ] && { log "Protection disabled, watcher exiting"; break; } + + check_integrity > /dev/null 2>&1 + done +} + +# --- Get change log --- +get_changelog() { + local lines="${2:-50}" + if [ -f "$CHANGELOG" ]; then + tail -n "$lines" "$CHANGELOG" + else + echo "No changes detected" + fi +} + +# --- Main --- +case "$1" in + baseline) create_baseline ;; + check) check_integrity ;; + status) get_status ;; + mode) set_mode "$2" ;; + protect) set_driver_protection "$2" "$3" ;; + watch) watch_loop ;; + changelog) get_changelog "$2" ;; + *) echo "Usage: protect.sh {baseline|check|status|mode|protect|watch|changelog} [args]" ;; +esac diff --git a/scripts/scope_manager.sh b/scripts/scope_manager.sh new file mode 100644 index 0000000..faeb3f3 --- /dev/null +++ b/scripts/scope_manager.sh @@ -0,0 +1,342 @@ +#!/system/bin/sh +# Driver Manager v2 - Per-App & System-Wide Scope Manager +# LSPosed-style driver scoping via mount namespace isolation + +MODDIR=${MODDIR:-/data/adb/modules/driver-manager} +. "$MODDIR/scripts/core.sh" +log_init "$LOGDIR/scope.log" + +SCOPES_FILE="$CONFIGDIR/scopes.json" +REGISTRY="$CONFIGDIR/drivers.json" + +# --- Clone file metadata from stock to custom --- +clone_metadata() { + local stock="$1" custom="$2" + # Copy ownership + local owner=$(stat -c '%u:%g' "$stock" 2>/dev/null) + [ -n "$owner" ] && chown "$owner" "$custom" 2>/dev/null + # Copy permissions + local perms=$(stat -c '%a' "$stock" 2>/dev/null) + [ -n "$perms" ] && chmod "$perms" "$custom" 2>/dev/null + # Copy SELinux context + local ctx=$(get_selinux_context "$stock") + [ -n "$ctx" ] && chcon "$ctx" "$custom" 2>/dev/null + # Copy timestamps + touch -r "$stock" "$custom" 2>/dev/null +} + +# --- Apply a bind mount for a specific process (per-app scope) --- +scope_for_process() { + local pkg="$1" + local stock_path="$2" + local custom_path="$3" + + if [ ! -f "$custom_path" ]; then + log_error "Custom driver not found: $custom_path" + return 1 + fi + + clone_metadata "$stock_path" "$custom_path" + + # Find all PIDs for this package + local pids=$(pgrep -f "$pkg" 2>/dev/null) + if [ -z "$pids" ]; then + log "No running processes for $pkg, scope will apply on next launch" + return 0 + fi + + for pid in $pids; do + nsenter --mount=/proc/$pid/ns/mnt -- mount --bind "$custom_path" "$stock_path" 2>/dev/null + if [ $? -eq 0 ]; then + log "Scoped $stock_path -> $custom_path for PID $pid ($pkg)" + else + log_error "Failed to scope for PID $pid ($pkg)" + fi + done +} + +# --- Apply a system-wide bind mount --- +scope_system() { + local stock_path="$1" + local custom_path="$2" + + if [ ! -f "$custom_path" ]; then + log_error "Custom driver not found: $custom_path" + return 1 + fi + + clone_metadata "$stock_path" "$custom_path" + mount --bind "$custom_path" "$stock_path" 2>/dev/null + if [ $? -eq 0 ]; then + log "System-wide scope: $stock_path -> $custom_path" + else + log_error "Failed system-wide scope: $stock_path" + return 1 + fi +} + +# --- Unmount a scope --- +unscope() { + local stock_path="$1" + local pkg="$2" + + if [ -n "$pkg" ] && [ "$pkg" != "system" ]; then + local pids=$(pgrep -f "$pkg" 2>/dev/null) + for pid in $pids; do + nsenter --mount=/proc/$pid/ns/mnt -- umount "$stock_path" 2>/dev/null + done + log "Unscoped $stock_path for $pkg" + else + umount "$stock_path" 2>/dev/null + log "Unscoped system-wide: $stock_path" + fi +} + +# --- Apply all configured scopes --- +apply_all() { + [ ! -f "$SCOPES_FILE" ] && return 0 + + log "Applying all scopes" + # Parse scopes.json - each scope: { driver_id, variant, mode, apps[] } + local count=0 + + # Use a simple line-by-line parser for the scopes array + local in_scope=false + local driver_id="" variant="" mode="" stock_path="" custom_path="" + local apps="" + + while IFS= read -r line; do + case "$line" in + *'"driver_id"'*) + driver_id=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"variant_path"'*) + custom_path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"stock_path"'*) + stock_path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"mode"'*) + mode=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"apps"'*\[*) + apps="" + ;; + *'"'*'"'*) + if [ -n "$stock_path" ] && [ -z "$mode" -o "$mode" = "app" ]; then + local app=$(echo "$line" | sed 's/.*"\([^"]*\)".*/\1/') + [ -n "$app" ] && apps="$apps $app" + fi + ;; + *\}*) + if [ -n "$driver_id" ] && [ -n "$stock_path" ] && [ -n "$custom_path" ]; then + if [ "$mode" = "system" ]; then + scope_system "$stock_path" "$custom_path" + count=$((count + 1)) + elif [ -n "$apps" ]; then + for app in $apps; do + scope_for_process "$app" "$stock_path" "$custom_path" + count=$((count + 1)) + done + fi + fi + driver_id="" variant="" mode="" stock_path="" custom_path="" apps="" + ;; + esac + done < "$SCOPES_FILE" + + log "Applied $count scopes" +} + +# --- Unmount all active scopes --- +unmount_all() { + log "Unmounting all scopes" + [ ! -f "$SCOPES_FILE" ] && return 0 + + while IFS= read -r line; do + case "$line" in + *'"stock_path"'*) + local path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + [ -n "$path" ] && umount "$path" 2>/dev/null + ;; + esac + done < "$SCOPES_FILE" + + log "All scopes unmounted" +} + +# --- Set a scope (called from API) --- +# Usage: scope_manager.sh set [app1,app2,...] +set_scope() { + local driver_id="$1" + local stock_path="$2" + local variant_path="$3" + local mode="$4" + local apps_csv="$5" + + # Build apps JSON array + local apps_json="[]" + if [ -n "$apps_csv" ] && [ "$mode" = "app" ]; then + apps_json="[$(echo "$apps_csv" | sed 's/,/","/g' | sed 's/^/"/;s/$/"/' )]" + fi + + # Check if scope already exists for this driver, update it + if grep -q "\"$driver_id\"" "$SCOPES_FILE" 2>/dev/null; then + # Remove existing scope for this driver and re-add + remove_scope "$driver_id" + fi + + # Append to scopes file + local tmp="$CONFIGDIR/.scopes_tmp.json" + if [ -f "$SCOPES_FILE" ] && [ -s "$SCOPES_FILE" ]; then + # Insert before the closing ]} + sed '$ s/\]}//' "$SCOPES_FILE" > "$tmp" + # Add comma if there are existing scopes + if grep -q '"driver_id"' "$SCOPES_FILE" 2>/dev/null; then + echo "," >> "$tmp" + fi + else + echo '{"scopes":[' > "$tmp" + fi + + cat >> "$tmp" << SCOPE + { + "driver_id": "$driver_id", + "stock_path": "$stock_path", + "variant_path": "$variant_path", + "mode": "$mode", + "apps": $apps_json + } +]} +SCOPE + + mv "$tmp" "$SCOPES_FILE" + log "Set scope for $driver_id (mode: $mode)" + + # Apply immediately + if [ "$mode" = "system" ]; then + scope_system "$stock_path" "$variant_path" + else + local IFS=',' + for app in $apps_csv; do + scope_for_process "$app" "$stock_path" "$variant_path" + done + fi + + echo "{\"status\": \"ok\", \"driver\": \"$driver_id\", \"mode\": \"$mode\"}" +} + +# --- Remove a scope --- +remove_scope() { + local driver_id="$1" + [ ! -f "$SCOPES_FILE" ] && return 0 + + # Get stock_path before removing so we can unmount + local stock_path=$(grep -A3 "\"$driver_id\"" "$SCOPES_FILE" | grep '"stock_path"' | sed 's/.*: *"\([^"]*\)".*/\1/') + [ -n "$stock_path" ] && umount "$stock_path" 2>/dev/null + + # Rebuild scopes file without this driver + local tmp="$CONFIGDIR/.scopes_tmp.json" + echo '{"scopes":[' > "$tmp" + local first=true skip=false + + while IFS= read -r line; do + case "$line" in + *"\"$driver_id\""*) + skip=true + ;; + *\}*) + if [ "$skip" = true ]; then + skip=false + continue + fi + ;; + esac + [ "$skip" = true ] && continue + # Skip the scopes header/footer + echo "$line" | grep -q '"scopes"' && continue + echo "$line" | grep -qE '^\[|^\]' && continue + echo "$line" >> "$tmp" + done < "$SCOPES_FILE" + + echo "]}" >> "$tmp" + mv "$tmp" "$SCOPES_FILE" + log "Removed scope for $driver_id" + echo "{\"status\": \"ok\", \"removed\": \"$driver_id\"}" +} + +# --- List active scopes --- +list_scopes() { + [ -f "$SCOPES_FILE" ] && cat "$SCOPES_FILE" || echo '{"scopes":[]}' +} + +# --- Monitor Zygote for new app launches --- +# Watches for new processes and applies per-app scopes +monitor_zygote() { + log "Zygote monitor started" + local known_pids="" + + while true; do + sleep 5 + [ ! -f "$SCOPES_FILE" ] && continue + + # Get list of scoped apps + local scoped_apps=$(grep '"apps"' -A50 "$SCOPES_FILE" 2>/dev/null | grep -o '"[a-z][a-zA-Z0-9.]*"' | tr -d '"' | sort -u) + [ -z "$scoped_apps" ] && continue + + for app in $scoped_apps; do + local pids=$(pgrep -f "$app" 2>/dev/null) + [ -z "$pids" ] && continue + + for pid in $pids; do + # Skip if we already applied scope to this PID + echo "$known_pids" | grep -q " $pid " && continue + + # Find the scope config for this app + local stock_path="" custom_path="" + local in_scope=false app_match=false + + while IFS= read -r line; do + case "$line" in + *'"stock_path"'*) + stock_path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *'"variant_path"'*) + custom_path=$(echo "$line" | sed 's/.*: *"\([^"]*\)".*/\1/') + ;; + *"\"$app\""*) + app_match=true + ;; + *\}*) + if [ "$app_match" = true ] && [ -n "$stock_path" ] && [ -n "$custom_path" ]; then + nsenter --mount=/proc/$pid/ns/mnt -- mount --bind "$custom_path" "$stock_path" 2>/dev/null + [ $? -eq 0 ] && log "Auto-scoped $app PID $pid" + fi + stock_path="" custom_path="" app_match=false + ;; + esac + done < "$SCOPES_FILE" + + known_pids="$known_pids $pid " + done + done + + # Clean dead PIDs from known list periodically + local new_known="" + for pid in $known_pids; do + [ -d "/proc/$pid" ] && new_known="$new_known $pid " + done + known_pids="$new_known" + done +} + +# --- Main --- +case "$1" in + apply) apply_all ;; + set) set_scope "$2" "$3" "$4" "$5" "$6" ;; + remove) remove_scope "$2" ;; + list) list_scopes ;; + unmount_all) unmount_all ;; + monitor) monitor_zygote ;; + *) echo "Usage: scope_manager.sh {apply|set|remove|list|unmount_all|monitor} [args]" ;; +esac diff --git a/service.sh b/service.sh new file mode 100644 index 0000000..57c321b --- /dev/null +++ b/service.sh @@ -0,0 +1,63 @@ +#!/system/bin/sh +# Driver Manager v2 - Main Service +# Runs after boot_completed + +MODDIR=${0%/*} +LOGFILE="$MODDIR/logs/service.log" +CONFIGDIR="$MODDIR/config" + +. "$MODDIR/scripts/core.sh" + +log_init "$LOGFILE" +log "Service starting" + +# Wait for boot to complete +while [ "$(getprop sys.boot_completed)" != "1" ]; do + sleep 1 +done +sleep 3 +log "Boot completed, initializing" + +# --- Run driver scan if registry doesn't exist --- +if [ ! -f "$CONFIGDIR/drivers.json" ] || [ ! -s "$CONFIGDIR/drivers.json" ]; then + log "No driver registry found, running initial scan" + sh "$MODDIR/scripts/driver_registry.sh" scan +fi + +# --- Create protection baseline if missing --- +if [ ! -f "$CONFIGDIR/baseline.json" ] || [ ! -s "$CONFIGDIR/baseline.json" ]; then + log "No protection baseline found, creating" + sh "$MODDIR/scripts/protect.sh" baseline +fi + +# --- Load kernel modules from autoload list --- +log "Loading kernel modules" +sh "$MODDIR/scripts/ko_manager.sh" autoload + +# --- Apply driver scopes --- +log "Applying driver scopes" +sh "$MODDIR/scripts/scope_manager.sh" apply + +# --- Start protection monitor --- +SETTINGS=$(cat "$CONFIGDIR/settings.json" 2>/dev/null) +PROT_MODE=$(echo "$SETTINGS" | grep -o '"protection_mode"[[:space:]]*:[[:space:]]*"[^"]*"' | grep -o '"[^"]*"$' | tr -d '"') +if [ "$PROT_MODE" = "monitor" ] || [ "$PROT_MODE" = "enforce" ]; then + log "Starting protection monitor (mode: $PROT_MODE)" + sh "$MODDIR/scripts/protect.sh" watch & + echo $! > "$MODDIR/run/protect.pid" +fi + +# --- Monitor Zygote for new app launches (apply per-app scopes) --- +MONITOR_ZYGOTE=$(echo "$SETTINGS" | grep -o '"monitor_zygote"[[:space:]]*:[[:space:]]*[a-z]*' | grep -o '[a-z]*$') +if [ "$MONITOR_ZYGOTE" = "true" ]; then + log "Starting Zygote process monitor for per-app scoping" + sh "$MODDIR/scripts/scope_manager.sh" monitor & + echo $! > "$MODDIR/run/scope_monitor.pid" +fi + +log "Service initialization complete" + +# --- Keep service alive for API calls --- +while true; do + sleep 86400 +done diff --git a/system.prop b/system.prop new file mode 100644 index 0000000..43639a4 --- /dev/null +++ b/system.prop @@ -0,0 +1,9 @@ +# Driver Manager v2 - System Properties +# Minimal properties set; drivers managed dynamically via scoping + +# USB OTG support for external drivers (SDR dongles, controllers) +persist.sys.usb.otg=1 + +# Bluetooth profiles +persist.bluetooth.a2dp_offload.disabled=false +persist.bluetooth.enablenewavrcp=true diff --git a/system/etc/permissions/driver-manager.xml b/system/etc/permissions/driver-manager.xml new file mode 100644 index 0000000..412f477 --- /dev/null +++ b/system/etc/permissions/driver-manager.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/system/etc/sysconfig/driver-manager.xml b/system/etc/sysconfig/driver-manager.xml new file mode 100644 index 0000000..941a7c1 --- /dev/null +++ b/system/etc/sysconfig/driver-manager.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..e974538 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,23 @@ +#!/system/bin/sh +# Driver Manager v2 - Uninstall Script +# Runs when module is removed via KernelSU + +MODDIR=${0%/*} + +# Kill running processes +for pidfile in "$MODDIR"/run/*.pid; do + [ -f "$pidfile" ] && kill $(cat "$pidfile") 2>/dev/null +done + +# Unmount any active driver scopes +if [ -f "$MODDIR/scripts/scope_manager.sh" ]; then + sh "$MODDIR/scripts/scope_manager.sh" unmount_all 2>/dev/null +fi + +# Unload managed kernel modules +if [ -f "$MODDIR/scripts/ko_manager.sh" ]; then + sh "$MODDIR/scripts/ko_manager.sh" unload_all 2>/dev/null +fi + +# Config and data stay in /data/adb/modules/driver-manager/ +# which KernelSU cleans up automatically diff --git a/webroot/index.html b/webroot/index.html new file mode 100644 index 0000000..0483df4 --- /dev/null +++ b/webroot/index.html @@ -0,0 +1,1213 @@ + + + + + +Driver Manager + + + + + +
+
DM
+

Driver Manager

+ v2.0.0 +
+ + + + + +
+
+
+
--
+
Drivers
+
+
+
--
+
Active Scopes
+
+
+
--
+
Kernel Modules
+
+
+
--
+
Protected
+
+
+ +
+
+ Device Info +
+
+
Loading...
+
+
+ +
+
+ Quick Actions +
+
+ + + +
+
+
+ + +
+
+ All + GPU + WiFi + Bluetooth + Audio + Camera + Sensor + Kernel +
+
+
+
Loading drivers...
+
+
+
+ + +
+
+ + +
+ +
+
+
+
+
Select a driver above
+
Then choose which apps should use it
+
+
+
+
+ + +
+
+
+ Kernel Modules (.ko) + +
+
+
Loading modules...
+
+
+
+
+
+
+
Place .ko files in modules/
+
/data/adb/modules/driver-manager/modules/
+
+
+
+
+ + +
+
+
+ Protection Status +
+
+
+
+
+
Loading...
+
--
+
+
+
+
+ +
+
+ Protection Mode +
+
+
+ Off + Monitor + Enforce +
+
+
+ +
+
+ Actions +
+
+ + +
+
+ +
+
+ Change Log +
+
+
No changes detected
+
+
+
+ + +
+
+ Service + Boot + Scope + Modules + Protect + Registry +
+
+
+ Service Log + +
+
+
Loading...
+
+
+
+ + +
+
+
+ Driver Details + +
+
+
+
+ + +
+ + + +