From 128783dfd4dff3c6d6b041b8011804ed50dce130 Mon Sep 17 00:00:00 2001 From: Enginex0 Date: Tue, 19 May 2026 03:59:28 +0100 Subject: [PATCH] feat(spoof): PatchLevelManager with PIF resolution PatchLevelManager resolves the active security patch from PlayIntegrityFix via the same six-path override chain as Tricky-Addon's get_extra.sh (pif.json/pif.prop/custom.pif.*, later entries override earlier ones). Falls back to live ro.build.version.security_patch when no PIF source is present. updateTo() validates YYYY-MM-DD format, rejects dates below 2020-01-01 or more than one year older than today, then atomically stages security_patch.txt with explicit system/boot/vendor dates and resetprops ro.build.version.security_patch plus ro.vendor.build.security_patch. Cert tags 706/718/719 then encode consistent dates via AndroidDeviceUtils.parsePatchLevelValue (YYYYMM for OS, YYYYMMDD for VENDOR/BOOT per AOSP Tag.aidl). Wired from App.main after BootStateManager.apply(). --- .../main/java/org/matrix/TEESimulator/App.kt | 2 + .../TEESimulator/config/PatchLevelManager.kt | 97 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt diff --git a/app/src/main/java/org/matrix/TEESimulator/App.kt b/app/src/main/java/org/matrix/TEESimulator/App.kt index b694cbe..d8230e0 100644 --- a/app/src/main/java/org/matrix/TEESimulator/App.kt +++ b/app/src/main/java/org/matrix/TEESimulator/App.kt @@ -10,6 +10,7 @@ import java.security.Security import org.bouncycastle.jce.provider.BouncyCastleProvider import org.matrix.TEESimulator.config.BootStateManager import org.matrix.TEESimulator.config.ConfigurationManager +import org.matrix.TEESimulator.config.PatchLevelManager import org.matrix.TEESimulator.interception.keystore.AbstractKeystoreInterceptor import org.matrix.TEESimulator.interception.keystore.Keystore2Interceptor import org.matrix.TEESimulator.interception.keystore.KeystoreInterceptor @@ -46,6 +47,7 @@ object App { // Load the package configuration. ConfigurationManager.initialize() BootStateManager.apply() + PatchLevelManager.initialize() // Set up the device's boot key and hash, which are crucial for attestation. AndroidDeviceUtils.setupBootKeyAndHash() diff --git a/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt b/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt new file mode 100644 index 0000000..6a7a215 --- /dev/null +++ b/app/src/main/java/org/matrix/TEESimulator/config/PatchLevelManager.kt @@ -0,0 +1,97 @@ +package org.matrix.TEESimulator.config + +import android.os.Build +import android.os.SystemProperties +import java.io.File +import java.nio.file.Files +import java.nio.file.StandardCopyOption +import java.time.LocalDate +import org.json.JSONObject +import org.matrix.TEESimulator.logging.SystemLogger +import org.matrix.TEESimulator.util.AndroidDeviceUtils + +object PatchLevelManager { + private const val PATCH_FILE = "/data/adb/tricky_store/security_patch.txt" + private const val STAGING_FILE = "/data/adb/tricky_store/security_patch.txt.next" + private const val FLOOR_YYYYMMDD = 20200101 + private const val MAX_PAST_OFFSET = 10000 + + private val DATE_PATTERN = Regex("^\\d{4}-\\d{2}-\\d{2}$") + private val PROP_PATTERN = Regex("^SECURITY_PATCH=(.+)$", RegexOption.MULTILINE) + + private val PIF_SOURCES = + listOf( + "/data/adb/modules/playintegrityfix/pif.json", + "/data/adb/pif.json", + "/data/adb/modules/playintegrityfix/pif.prop", + "/data/adb/pif.prop", + "/data/adb/modules/playintegrityfix/custom.pif.json", + "/data/adb/modules/playintegrityfix/custom.pif.prop", + ) + + fun initialize() { + val date = + resolvePifPatch() + ?: SystemProperties.get( + "ro.build.version.security_patch", + Build.VERSION.SECURITY_PATCH, + ) + SystemLogger.info("PatchLevelManager: resolved patch date = $date") + updateTo(date) + } + + fun updateTo(date: String) { + if (!DATE_PATTERN.matches(date)) { + SystemLogger.warning("PatchLevelManager: invalid date format: $date") + return + } + val dateInt = date.replace("-", "").toInt() + if (dateInt < FLOOR_YYYYMMDD) { + SystemLogger.warning("PatchLevelManager: $date below floor $FLOOR_YYYYMMDD") + return + } + val today = + LocalDate.now().let { it.year * 10000 + it.monthValue * 100 + it.dayOfMonth } + if (today >= dateInt + MAX_PAST_OFFSET) { + SystemLogger.warning( + "PatchLevelManager: $date more than 1y older than today ($today)" + ) + return + } + atomicWrite(date) + AndroidDeviceUtils.setProperty("ro.build.version.security_patch", date) + AndroidDeviceUtils.setProperty("ro.vendor.build.security_patch", date) + SystemLogger.info("PatchLevelManager: applied patch date $date") + } + + private fun resolvePifPatch(): String? { + val source = PIF_SOURCES.map(::File).lastOrNull { it.exists() } ?: return null + return try { + val text = source.readText() + val parsed = + if (source.name.endsWith(".json")) { + JSONObject(text).optString("SECURITY_PATCH", "") + } else { + PROP_PATTERN.find(text)?.groupValues?.get(1)?.trim().orEmpty() + } + parsed.takeIf { it.isNotBlank() } + } catch (e: Exception) { + SystemLogger.warning( + "PatchLevelManager: failed to parse ${source.path}: ${e.message}" + ) + null + } + } + + private fun atomicWrite(date: String) { + val target = File(PATCH_FILE) + val staging = File(STAGING_FILE) + staging.writeText("system=$date\nboot=$date\nvendor=$date\n") + Files.move( + staging.toPath(), + target.toPath(), + StandardCopyOption.ATOMIC_MOVE, + StandardCopyOption.REPLACE_EXISTING, + ) + } +}