diff --git a/README.md b/README.md index 920ddf9..2d59e0c 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,12 @@ boot=no system=2025-10-01 ``` +### boot_props_mode + +Controls global `ro.boot.*` property spoofing. Values: `auto` (default), `force`, or `disable`. + +In `auto`, Oplus-family devices (OnePlus/OPPO/realme/Oplus) skip boot-state prop spoofing to avoid conflicts with vendor TEE services such as ultrasonic fingerprint calibration. Create `/data/adb/tricky_store/boot_props_mode` with `force` to restore the old behavior, or `disable` to turn it off on any device. + ## Building from source You need JDK 21, the Android SDK and NDK 29, Rust (stable) with the `aarch64-linux-android` target, and `cargo-ndk`. diff --git a/app/src/main/java/org/matrix/TEESimulator/config/BootStateManager.kt b/app/src/main/java/org/matrix/TEESimulator/config/BootStateManager.kt index caf3843..5123ea9 100644 --- a/app/src/main/java/org/matrix/TEESimulator/config/BootStateManager.kt +++ b/app/src/main/java/org/matrix/TEESimulator/config/BootStateManager.kt @@ -1,10 +1,20 @@ package org.matrix.TEESimulator.config import android.os.SystemProperties +import java.io.File import org.matrix.TEESimulator.logging.SystemLogger import org.matrix.TEESimulator.util.AndroidDeviceUtils object BootStateManager { + private const val CONFIG_PATH = "/data/adb/tricky_store" + private const val BOOT_PROPS_MODE_FILE = "boot_props_mode" + + private enum class BootPropsMode { + AUTO, + FORCE, + DISABLE, + } + private val targets = linkedMapOf( "ro.boot.verifiedbootstate" to "green", @@ -22,6 +32,25 @@ object BootStateManager { ) fun apply() { + val mode = readBootPropsMode() + when (mode) { + BootPropsMode.DISABLE -> { + SystemLogger.info("BootStateManager: disabled by $BOOT_PROPS_MODE_FILE") + return + } + BootPropsMode.AUTO -> { + if (isOplusFamilyDevice()) { + SystemLogger.warning( + "BootStateManager: skipping boot-state prop spoofing on Oplus-family device in auto mode" + ) + return + } + } + BootPropsMode.FORCE -> { + SystemLogger.info("BootStateManager: force-enabled by $BOOT_PROPS_MODE_FILE") + } + } + for ((name, target) in targets) { val current = SystemProperties.get(name, "") if (current.isEmpty()) { @@ -45,4 +74,50 @@ object BootStateManager { AndroidDeviceUtils.setProperty(name, value) } } + + fun shouldSpoofBootProps(): Boolean = + when (readBootPropsMode()) { + BootPropsMode.DISABLE -> false + BootPropsMode.AUTO -> !isOplusFamilyDevice() + BootPropsMode.FORCE -> true + } + + private fun readBootPropsMode(): BootPropsMode { + val file = File(CONFIG_PATH, BOOT_PROPS_MODE_FILE) + if (!file.exists()) return BootPropsMode.AUTO + + val raw = + runCatching { file.readText().trim().lowercase() } + .getOrElse { + SystemLogger.warning("BootStateManager: failed to read ${file.absolutePath}", it) + return BootPropsMode.AUTO + } + + return when (raw) { + "1", "true", "on", "enable", "enabled", "force" -> BootPropsMode.FORCE + "0", "false", "off", "disable", "disabled", "none" -> BootPropsMode.DISABLE + else -> BootPropsMode.AUTO + } + } + + private fun isOplusFamilyDevice(): Boolean { + val props = + listOf( + "ro.product.manufacturer", + "ro.product.brand", + "ro.product.vendor.manufacturer", + "ro.product.vendor.brand", + "ro.product.odm.manufacturer", + "ro.product.odm.brand", + "ro.boot.hardware.sku", + "ro.boot.project_name", + ) + + val joined = + props.joinToString(separator = " ") { name -> + SystemProperties.get(name, "") + }.lowercase() + + return listOf("oneplus", "oplus", "oppo", "realme").any { joined.contains(it) } + } } diff --git a/app/src/main/java/org/matrix/TEESimulator/util/AndroidDeviceUtils.kt b/app/src/main/java/org/matrix/TEESimulator/util/AndroidDeviceUtils.kt index 5b4f149..a493500 100644 --- a/app/src/main/java/org/matrix/TEESimulator/util/AndroidDeviceUtils.kt +++ b/app/src/main/java/org/matrix/TEESimulator/util/AndroidDeviceUtils.kt @@ -14,6 +14,7 @@ import org.bouncycastle.asn1.ASN1Integer import org.bouncycastle.asn1.DEROctetString import org.bouncycastle.asn1.DERSequence import org.matrix.TEESimulator.attestation.DeviceAttestationService +import org.matrix.TEESimulator.config.BootStateManager import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.logging.SystemLogger import org.w3c.dom.Element @@ -112,7 +113,7 @@ object AndroidDeviceUtils { attestationValueProvider()?.let { SystemLogger.debug("Using $propertyName from TEE attestation: ${it.toHex()}") recordSource("tee-attestation") - setProperty(propertyName, it) + setBootProperty(propertyName, it) persistToFile(propertyName, it) return it } @@ -123,14 +124,14 @@ object AndroidDeviceUtils { readFromFile(propertyName, expectedSize)?.let { SystemLogger.debug("Using $propertyName from persistent file: ${it.toHex()}") recordSource("persistent-file") - setProperty(propertyName, it) + setBootProperty(propertyName, it) return it } return generateRandomBytes(expectedSize).also { SystemLogger.debug("Using randomly generated $propertyName: ${it.toHex()}") recordSource("random-fallback") - setProperty(propertyName, it) + setBootProperty(propertyName, it) persistToFile(propertyName, it) } } @@ -178,6 +179,14 @@ object AndroidDeviceUtils { } } + private fun setBootProperty(name: String, bytes: ByteArray) { + if (!BootStateManager.shouldSpoofBootProps()) { + SystemLogger.info("Skipping system property '$name' because boot prop spoofing is disabled") + return + } + setProperty(name, bytes) + } + internal fun setProperty(name: String, value: String) { try { SystemLogger.debug("Setting system property '$name' to: $value")