#!/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