Add boot timing and AVB/Play Integrity evasion
Boot timing system defers all modifications until after Play Integrity first attestation completes. Monitors DroidGuard (com.google.android.gms.unstable) CPU activity to detect attestation window. PI watcher daemon auto-hides mods during periodic re-checks — unmounts spoofs, removes non-stock props, ensures boot state reads green/locked/enforcing, then re-applies after check finishes. post-fs-data.sh cleaned to only set stock-safe props during early boot. KernelSU version props hidden. WebUI boot timing panel with hide/unhide controls.
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
295
scripts/boot_timing.sh
Executable file
295
scripts/boot_timing.sh
Executable file
@@ -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
|
||||
31
service.sh
31
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
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -357,6 +357,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Boot Timing -->
|
||||
<div class="card">
|
||||
<div class="card-title"><span class="dot" id="bootDot"></span> Boot Timing / AVB Evasion</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="row-label">Boot Timing</div>
|
||||
<div class="row-desc">Defer mods until after Play Integrity passes</div>
|
||||
</div>
|
||||
<select class="sel" id="bootTiming" onchange="setConf('boot_timing', this.value)">
|
||||
<option value="0">Disabled (apply immediately)</option>
|
||||
<option value="1">Enabled (wait for PI)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="row-label">PI Watcher</div>
|
||||
<div class="row-desc">Auto-hide during re-attestation checks</div>
|
||||
</div>
|
||||
<div class="row-value" id="piWatcherStatus">—</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="row-label">Boot State</div>
|
||||
<div class="row-desc">Verified boot props</div>
|
||||
</div>
|
||||
<div class="row-value" id="bootState">—</div>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button class="btn" onclick="bootAction('hide')">Hide Now</button>
|
||||
<button class="btn" onclick="bootAction('unhide')">Unhide</button>
|
||||
<button class="btn" onclick="bootAction('status')">Check Status</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Driver Spoofing -->
|
||||
<div class="card">
|
||||
<div class="card-title"><span class="dot" id="spoofDot"></span> Driver Spoofing</div>
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user