diff --git a/customize.sh b/customize.sh index af7cde5..d6e1b72 100644 --- a/customize.sh +++ b/customize.sh @@ -41,6 +41,7 @@ echo "auto" > "$MODPATH/config/gamepad_mode" echo "off" > "$MODPATH/config/decoder_mode" echo "off" > "$MODPATH/config/stealth_mode" echo "0" > "$MODPATH/config/spoof_enabled" +echo "0" > "$MODPATH/config/boot_timing" echo "100.0M" > "$MODPATH/config/fm_freq" echo "24M:1800M" > "$MODPATH/config/spectrum_range" diff --git a/driver-manager-v1.0.0.zip b/driver-manager-v1.0.0.zip index cc6b886..6036a64 100644 Binary files a/driver-manager-v1.0.0.zip and b/driver-manager-v1.0.0.zip differ diff --git a/post-fs-data.sh b/post-fs-data.sh index 5eaa9e9..546a281 100755 --- a/post-fs-data.sh +++ b/post-fs-data.sh @@ -1,17 +1,37 @@ #!/system/bin/sh # Driver Manager - post-fs-data -# Early boot: set core driver properties before services start +# EARLY BOOT — runs before verification checks +# +# IMPORTANT: Keep this CLEAN. Only set props that already exist +# on stock or that won't trigger root/mod detection. +# All custom/non-stock props are deferred to service.sh (after PI). MODDIR=${0%/*} +CONFDIR="$MODDIR/config" -# GPU — PowerVR latest driver hints +# GPU — these are stock props, safe to set early resetprop ro.hardware.egl powervr resetprop ro.hardware.vulkan powervr -resetprop graphics.gpu.profiler.support true -# OpenCL compute -resetprop vendor.powervr.opencl.allowfp16 1 -resetprop vendor.powervr.opencl.profiling 1 +# Boot state — ensure these look locked/verified from the start +# These are checked by early-running verification services +BOOT_TIMING=$(cat "$CONFDIR/boot_timing" 2>/dev/null || echo "0") +if [ "$BOOT_TIMING" = "1" ]; then + resetprop ro.boot.verifiedbootstate green + resetprop ro.boot.flash.locked 1 + resetprop ro.boot.vbmeta.device_state locked + resetprop ro.boot.veritymode enforcing + resetprop ro.build.selinux 1 -# USB OTG — required for SDR dongles, HackRF, game controllers -resetprop persist.sys.usb.otg 1 + # Hide KernelSU props if they exist + resetprop --delete ro.kernelsu.version 2>/dev/null + resetprop --delete ro.kernelsu.versionCode 2>/dev/null +fi + +# DO NOT set these during early boot — they're non-stock: +# vendor.powervr.opencl.allowfp16 +# vendor.powervr.opencl.profiling +# persist.sys.usb.otg +# input.gamepad.enabled +# bluetooth.le.no_location_permission_scan +# These are applied later by service.sh or boot_timing.sh diff --git a/scripts/boot_timing.sh b/scripts/boot_timing.sh new file mode 100755 index 0000000..73b6b89 --- /dev/null +++ b/scripts/boot_timing.sh @@ -0,0 +1,295 @@ +#!/system/bin/sh +# Boot Timing & Verification Evasion +# +# Controls WHEN custom drivers and modifications are applied relative +# to Android's verification checkpoints: +# +# 1. AVB (Android Verified Boot) — bootloader, before kernel +# KernelSU handles this. We don't touch partitions. PASS. +# +# 2. dm-verity — block-level hash verification +# KernelSU overlays are above dm-verity. Stock blocks untouched. PASS. +# +# 3. Play Integrity / SafetyNet — after boot, runs in GMS process +# This is what we need to time around. First check is ~10-30s +# after boot_completed. Periodic re-checks every few hours. +# +# 4. Banking apps / root detectors — check on app launch +# These read props, scan /proc, check mount points. +# +# Strategy: +# - post-fs-data: ONLY stock-safe props. No mods. No spoofs. +# - boot_completed: Wait for first PI attestation to finish +# - Apply mods AFTER attestation window closes +# - Run a watcher that detects PI re-checks and temporarily +# unmounts spoofs + restores stock props during the check + +MODDIR="/data/adb/modules/driver-manager" +CONFDIR="$MODDIR/config" +LOGFILE="$MODDIR/driver-manager.log" + +mlog() { + STEALTH=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off") + [ "$STEALTH" = "full" ] && return + echo "$(date '+%Y-%m-%d %H:%M:%S') [boot_timing] $1" >> "$LOGFILE" +} + +# ================================================================= +# Phase 1: Detect when Play Integrity has completed first check +# ================================================================= +# GMS runs attestation via com.google.android.gms.unstable +# DroidGuard collects device state in this window +# We watch for this process to start and finish + +wait_for_pi_pass() { + mlog "Waiting for Play Integrity first attestation..." + + # Wait for GMS to be fully running + TIMEOUT=120 + ELAPSED=0 + while [ $ELAPSED -lt $TIMEOUT ]; do + # Check if GMS unstable (DroidGuard) has started + GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null) + if [ -n "$GMS_PID" ]; then + mlog "DroidGuard detected (PID $GMS_PID), waiting for completion..." + + # Wait for the DroidGuard process to finish its attestation + # It typically runs for 5-15 seconds during initial check + DROID_WAIT=0 + while [ $DROID_WAIT -lt 30 ]; do + sleep 1 + DROID_WAIT=$((DROID_WAIT + 1)) + + # Check if it's still actively doing attestation + # DroidGuard CPU usage drops after attestation completes + if [ -d "/proc/$GMS_PID" ]; then + CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}') + if [ -n "$CPU" ] && [ "$CPU" -lt 5 ] && [ $DROID_WAIT -gt 10 ]; then + mlog "DroidGuard idle after ${DROID_WAIT}s — attestation likely complete" + break + fi + else + mlog "DroidGuard process exited after ${DROID_WAIT}s" + break + fi + done + + # Extra safety margin + sleep 5 + mlog "PI attestation window closed" + return 0 + fi + + sleep 2 + ELAPSED=$((ELAPSED + 2)) + done + + # If GMS never started (unlikely), just wait a safe fixed time + mlog "GMS not detected after ${TIMEOUT}s — using fixed delay" + sleep 30 + return 0 +} + +# ================================================================= +# Phase 2: Apply modifications after PI passes +# ================================================================= +apply_after_pi() { + mlog "Applying modifications..." + + # Apply driver spoofs + SPOOF_ENABLED=$(cat "$CONFDIR/spoof_enabled" 2>/dev/null || echo "0") + if [ "$SPOOF_ENABLED" = "1" ]; then + sh "$MODDIR/scripts/driver_spoof.sh" apply + mlog "Driver spoofs applied" + fi + + # Apply stealth + STEALTH_MODE=$(cat "$CONFDIR/stealth_mode" 2>/dev/null || echo "off") + if [ "$STEALTH_MODE" != "off" ]; then + mlog "Stealth mode active: $STEALTH_MODE" + fi + + # Set non-stock props that we held back during PI window + # These are the props that root detectors look for + resetprop input.gamepad.enabled true + resetprop persist.sys.usb.otg 1 + + mlog "All modifications applied" +} + +# ================================================================= +# Phase 3: PI Watcher — monitor for re-attestation and hide +# ================================================================= +# Play Integrity can re-check periodically (every few hours) or +# when an app requests it (banking app launch, Google Pay, etc.) +# We watch for DroidGuard activity and temporarily go clean. + +PI_WATCHER_PID="" + +start_pi_watcher() { + mlog "Starting PI watcher daemon..." + + ( + while true; do + sleep 10 + + # Check if DroidGuard is doing a new attestation + GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null) + if [ -z "$GMS_PID" ]; then + continue + fi + + # Check if it's actively using CPU (attestation in progress) + CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}') + PREV_CPU="$CPU" + sleep 2 + CPU2=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}') + + if [ -n "$CPU" ] && [ -n "$CPU2" ]; then + DELTA=$((CPU2 - CPU)) + if [ "$DELTA" -gt 10 ]; then + # DroidGuard is active — temporarily hide + mlog "PI re-check detected! Temporarily restoring stock..." + hide_for_pi + fi + fi + done + ) & + PI_WATCHER_PID=$! + echo "$PI_WATCHER_PID" > "$MODDIR/run/pi_watcher.pid" + mlog "PI watcher started (PID $PI_WATCHER_PID)" +} + +hide_for_pi() { + # Temporarily unmount all driver spoofs + sh "$MODDIR/scripts/driver_spoof.sh" restore 2>/dev/null + + # Remove non-stock props + resetprop --delete input.gamepad.enabled 2>/dev/null + resetprop --delete persist.sys.usb.otg 2>/dev/null + resetprop --delete vendor.powervr.opencl.allowfp16 2>/dev/null + resetprop --delete vendor.powervr.opencl.profiling 2>/dev/null + resetprop --delete bluetooth.le.no_location_permission_scan 2>/dev/null + + # Ensure boot state looks clean + resetprop ro.boot.verifiedbootstate green 2>/dev/null + resetprop ro.boot.flash.locked 1 2>/dev/null + resetprop ro.boot.vbmeta.device_state locked 2>/dev/null + + mlog "Hiding for PI check..." + + # Wait for DroidGuard to finish + WAIT=0 + while [ $WAIT -lt 60 ]; do + sleep 2 + WAIT=$((WAIT + 2)) + + GMS_PID=$(pidof com.google.android.gms.unstable 2>/dev/null) + if [ -z "$GMS_PID" ]; then + break + fi + + CPU=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}') + sleep 1 + CPU2=$(cat /proc/$GMS_PID/stat 2>/dev/null | awk '{print $14+$15}') + DELTA=$((CPU2 - CPU)) + + if [ "$DELTA" -lt 5 ]; then + break + fi + done + + # Extra safety margin + sleep 5 + + mlog "PI check finished, re-applying modifications..." + apply_after_pi +} + +# ================================================================= +# Boot prop spoofing — make boot state look locked/verified +# ================================================================= +spoof_boot_props() { + # These props are checked by Play Integrity and root detectors + # They should reflect a locked, verified device + + # Verified boot state + resetprop ro.boot.verifiedbootstate green + resetprop ro.boot.flash.locked 1 + resetprop ro.boot.vbmeta.device_state locked + resetprop ro.boot.veritymode enforcing + + # Remove KernelSU traces from props + resetprop --delete ro.kernelsu.version 2>/dev/null + resetprop --delete ro.kernelsu.versionCode 2>/dev/null + + # Stock build fingerprint (don't modify — just ensure it's not overridden) + # resetprop ro.build.fingerprint should already be stock + + # Ensure SELinux appears enforcing + resetprop ro.build.selinux 1 + + mlog "Boot props spoofed (green/locked/enforcing)" +} + +# ================================================================= +# Main entry point — called from service.sh +# ================================================================= +case "$1" in + run) + # Full boot timing sequence + spoof_boot_props + wait_for_pi_pass + apply_after_pi + start_pi_watcher + mlog "Boot timing sequence complete" + ;; + hide) + # Manual hide trigger + hide_for_pi + ;; + unhide) + # Manual re-apply + apply_after_pi + ;; + stop) + # Stop PI watcher + PID=$(cat "$MODDIR/run/pi_watcher.pid" 2>/dev/null) + if [ -n "$PID" ]; then + kill "$PID" 2>/dev/null + rm -f "$MODDIR/run/pi_watcher.pid" + mlog "PI watcher stopped" + fi + ;; + status) + PID=$(cat "$MODDIR/run/pi_watcher.pid" 2>/dev/null) + if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then + echo "PI watcher: running (PID $PID)" + else + echo "PI watcher: not running" + fi + + # Check boot props + echo "Boot state: $(getprop ro.boot.verifiedbootstate)" + echo "Flash lock: $(getprop ro.boot.flash.locked)" + echo "VBMeta: $(getprop ro.boot.vbmeta.device_state)" + echo "SELinux: $(getenforce 2>/dev/null || echo unknown)" + + # Check for KSU prop leaks + KSU_VER=$(getprop ro.kernelsu.version 2>/dev/null) + if [ -n "$KSU_VER" ]; then + echo "WARNING: KernelSU version visible: $KSU_VER" + else + echo "KernelSU props: hidden" + fi + ;; + *) + echo "Usage: boot_timing.sh {run|hide|unhide|stop|status}" + echo "" + echo " run Full boot sequence (wait for PI, apply mods, start watcher)" + echo " hide Temporarily hide all mods (for manual PI trigger)" + echo " unhide Re-apply all mods" + echo " stop Stop PI watcher daemon" + echo " status Show current state" + ;; +esac diff --git a/service.sh b/service.sh index 1404c3f..fb3de02 100755 --- a/service.sh +++ b/service.sh @@ -39,6 +39,30 @@ GPU=$(getprop ro.hardware.egl) mlog "Boot complete. Device=$DEVICE SoC=$SOC Platform=$PLATFORM API=$API GPU=$GPU" +# ================================================================= +# BOOT TIMING — Wait for Play Integrity before applying mods +# ================================================================= +# If boot timing is enabled, we hand off to boot_timing.sh which: +# 1. Spoofs boot props (green/locked/enforcing) +# 2. Waits for first Play Integrity attestation to finish +# 3. THEN applies all our modifications +# 4. Starts a watcher that hides mods during future PI checks +# +# If disabled, mods apply immediately (faster but less stealthy) + +BOOT_TIMING=$(cat "$CONFDIR/boot_timing" 2>/dev/null || echo "0") + +if [ "$BOOT_TIMING" = "1" ]; then + mlog "Boot timing enabled — deferring mods until after PI attestation" + # Spoof boot props NOW (safe, just props) + sh "$MODDIR/scripts/boot_timing.sh" run & + TIMING_PID=$! + echo "$TIMING_PID" > "$MODDIR/run/boot_timing.pid" + mlog "Boot timing daemon started (PID $TIMING_PID)" + # The rest of this script still runs to set up configs, + # but the actual driver spoofs are deferred to boot_timing.sh +fi + # ============================================================ # GPU — PowerVR DXT-48-1536 (pvrsrvkm, native) # ============================================================ @@ -334,11 +358,12 @@ esac SPOOF_ENABLED=$(cat "$CONFDIR/spoof_enabled" 2>/dev/null || echo "0") -if [ "$SPOOF_ENABLED" = "1" ]; then - # Wait for target processes to be running +if [ "$SPOOF_ENABLED" = "1" ] && [ "$BOOT_TIMING" != "1" ]; then + # Apply immediately if boot timing is NOT handling it + # (boot_timing.sh applies spoofs after PI passes) sleep 5 sh "$MODDIR/scripts/driver_spoof.sh" apply - mlog "Driver spoofing applied" + mlog "Driver spoofing applied (immediate mode)" fi # ============================================================ diff --git a/webroot/index.html b/webroot/index.html index 743fde1..d1a6067 100644 --- a/webroot/index.html +++ b/webroot/index.html @@ -357,6 +357,40 @@ + +
+
Boot Timing / AVB Evasion
+
+
+
Boot Timing
+
Defer mods until after Play Integrity passes
+
+ +
+
+
+
PI Watcher
+
Auto-hide during re-attestation checks
+
+
+
+
+
+
Boot State
+
Verified boot props
+
+
+
+
+ + + +
+
+
Driver Spoofing
@@ -493,6 +527,37 @@ if (fmFreq) document.getElementById('fmFreq').value = fmFreq; } + async function bootAction(action) { + log('Boot timing: ' + action); + const r = await exec('sh ' + MODDIR + '/scripts/boot_timing.sh ' + action); + log(r.stdout.trim() || action + ' done'); + await loadBootStatus(); + } + + async function loadBootStatus() { + const timing = (await exec('cat ' + MODDIR + '/config/boot_timing 2>/dev/null')).stdout.trim(); + document.getElementById('bootTiming').value = timing || '0'; + + const dot = document.getElementById('bootDot'); + dot.className = 'dot' + (timing === '1' ? '' : ' off'); + + // PI watcher + const watcherPid = (await exec('cat ' + MODDIR + '/run/pi_watcher.pid 2>/dev/null')).stdout.trim(); + if (watcherPid) { + const alive = (await exec('kill -0 ' + watcherPid + ' 2>/dev/null && echo running || echo dead')).stdout.trim(); + document.getElementById('piWatcherStatus').textContent = alive === 'running' ? 'Active (PID ' + watcherPid + ')' : 'Dead'; + } else { + document.getElementById('piWatcherStatus').textContent = timing === '1' ? 'Will start on boot' : 'Disabled'; + } + + // Boot state + const vb = await gp('ro.boot.verifiedbootstate'); + const fl = await gp('ro.boot.flash.locked'); + const vbm = await gp('ro.boot.vbmeta.device_state'); + document.getElementById('bootState').textContent = + 'vboot=' + (vb || '?') + ' flash=' + (fl || '?') + ' vbmeta=' + (vbm || '?'); + } + async function spoofAction(action) { log('Spoof: ' + action); const r = await exec('sh ' + MODDIR + '/scripts/driver_spoof.sh ' + action); @@ -676,6 +741,7 @@ await loadSdrInfo(); await loadControllerInfo(); await loadRtlStatus(); + await loadBootStatus(); await loadSpoofStatus(); await loadStealthStatus(); log('Done');