feat(spoof): add boot_props_mode Oplus carve-out

Add a boot_props_mode control so global ro.boot.* property spoofing
can be tuned per device.

BootStateManager reads /data/adb/tricky_store/boot_props_mode
(auto/force/disable). In auto, Oplus-family devices
(OnePlus/OPPO/realme/Oplus) skip the global ro.boot.* spoof so vendor
TEE services such as ultrasonic fingerprint calibration keep working.
AndroidDeviceUtils routes the bootKey/bootHash resetprop writes
through a setBootProperty() gate honoring the same mode; the forged
value is still persisted and returned, so attestation is unaffected.

Tradeoff: in auto, skipping ro.boot.* leaves direct system-property
boot-state checks truthful on Oplus devices (compatibility over
stealth). Latent, not a reported issue.
This commit is contained in:
Enginex0
2026-07-11 12:50:07 +01:00
parent a811919d0c
commit dc2d894647
3 changed files with 93 additions and 3 deletions
+6
View File
@@ -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`.
@@ -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) }
}
}
@@ -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")