Driver Manager v2.0.0 - LSPosed-style rewrite

Complete rewrite with:
- Per-app driver scoping via mount namespace isolation (LSPosed-style)
- System-wide driver mode selection
- .ko kernel module manager with autoload and dependency tracking
- Driver integrity protection (monitor/enforce modes)
- Driver registry with auto-discovery and SHA256 hashing
- LSPosed-style dark WebUI with 6 tabs: Dashboard, Drivers, Apps, Modules, Protection, Logs
- RESTful API handler for WebUI communication
- Zygote process monitor for auto-applying scopes to new app launches

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
sssnake
2026-04-03 06:01:07 -07:00
parent 2c57618987
commit b88141a9c5
16 changed files with 2802 additions and 0 deletions

101
customize.sh Normal file
View File

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

6
module.prop Normal file
View File

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

25
post-fs-data.sh Normal file
View File

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

148
scripts/api.sh Normal file
View File

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

85
scripts/core.sh Normal file
View File

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

309
scripts/driver_registry.sh Normal file
View File

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

228
scripts/ko_manager.sh Normal file
View File

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

233
scripts/protect.sh Normal file
View File

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

342
scripts/scope_manager.sh Normal file
View File

@@ -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 <driver_id> <stock_path> <variant_path> <mode> [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

63
service.sh Normal file
View File

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

9
system.prop Normal file
View File

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

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<feature name="android.hardware.usb.host" />
<feature name="android.hardware.usb.accessory" />
<feature name="android.hardware.bluetooth" />
<feature name="android.hardware.bluetooth_le" />
<feature name="android.hardware.vulkan.level" version="1" />
<feature name="android.hardware.opengles.aep" />
</permissions>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<feature name="android.hardware.usb.host" />
<feature name="android.hardware.usb.accessory" />
<feature name="android.hardware.bluetooth_le" />
<feature name="android.hardware.wifi.direct" />
<feature name="android.hardware.gamepad" />
</config>

23
uninstall.sh Normal file
View File

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

1213
webroot/index.html Normal file

File diff suppressed because it is too large Load Diff