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:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user