234 lines
7.9 KiB
Bash
234 lines
7.9 KiB
Bash
|
|
#!/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
|