From 151410c75eee390c8f4184d3a752f54fa96040f1 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Mon, 26 Jan 2026 14:07:06 +0100 Subject: [PATCH] Fix Android 11 Keystore execution: Init framework and spoof UID 1000 (#85) This commit resolves `KeyStore` API failures on Android 11 when running as a standalone CLI executable (UID 0), addressing both environment initialization and permission denial issues. 1. Initialize Android Framework Environment: Android 11 Keystore APIs expect a fully initialized application context and a Main Looper, which are missing in a raw root process. This patch: - Manually bootstraps `ActivityThread` via `systemMain()`. - Initializes `Looper.prepareMainLooper()`. - Injects a dummy `Application` object attached to the system context to satisfy `KeyStore.getApplicationContext()` checks. - Updates framework stubs to allow compilation of these hidden APIs. 2. Bypass Keystore Permission Checks via UID Spoofing: `KeyStoreService::generateKey` enforces the `P_INSERT` permission. Analysis of `permissions.cpp` reveals that UID 0 (Root) is explicitly denied this permission (granted only `P_GET`), whereas UID 1000 (System) holds all permissions (`~0`). To bypass this restriction, the binder interceptor now detects transactions originating from UID 0 and rewrites the `sender_euid` to 1000. This fools `KeyStoreService` into granting the request. 3. Refactor Execution Loop: Replaces the previous `Thread.sleep()` maintenance loop with `Looper.loop()`. --- app/src/main/cpp/binder_interceptor.cpp | 9 +++ .../main/java/org/matrix/TEESimulator/App.kt | 56 ++++++++++++++----- .../main/java/android/app/ActivityThread.java | 8 +++ .../main/java/android/app/ContextImpl.java | 4 ++ 4 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 stub/src/main/java/android/app/ContextImpl.java diff --git a/app/src/main/cpp/binder_interceptor.cpp b/app/src/main/cpp/binder_interceptor.cpp index 4104a39..43b9a68 100644 --- a/app/src/main/cpp/binder_interceptor.cpp +++ b/app/src/main/cpp/binder_interceptor.cpp @@ -386,6 +386,15 @@ void inspectAndRewriteTransaction(binder_transaction_data *txn_data) { } if (hijack) { + // The kernel driver fills sender_euid. libbinder trusts this value + // to populate IPCThreadState. By changing it here, we fool the + // entire process (including the TEE implementation) into thinking + // the call came from system (1000). + if (txn_data->sender_euid == 0) { + LOGV("[Hook] Spoofing UID for transaction: 0 -> 1000"); + txn_data->sender_euid = 1000; + } + uint64_t tx_id = ++g_transaction_id_counter; info.transaction_id = tx_id; diff --git a/app/src/main/java/org/matrix/TEESimulator/App.kt b/app/src/main/java/org/matrix/TEESimulator/App.kt index 29762b5..6e41fbd 100644 --- a/app/src/main/java/org/matrix/TEESimulator/App.kt +++ b/app/src/main/java/org/matrix/TEESimulator/App.kt @@ -1,6 +1,11 @@ package org.matrix.TEESimulator +import android.app.ActivityThread +import android.app.Application +import android.content.Context +import android.content.ContextWrapper import android.os.Build +import android.os.Looper import java.security.Security import org.bouncycastle.jce.provider.BouncyCastleProvider import org.matrix.TEESimulator.config.ConfigurationManager @@ -30,13 +35,15 @@ object App { SystemLogger.info("Welcome to TEESimulator!") try { + // Initialize the Android framework environment + prepareEnvironment() + // Initialize and start the appropriate keystore interceptors. + initializeInterceptors() + // Load the package configuration. ConfigurationManager.initialize() // Set up the device's boot key and hash, which are crucial for attestation. AndroidDeviceUtils.setupBootKeyAndHash() - // Initialize and start the appropriate keystore interceptors. - initializeInterceptors() - // Enter an infinite loop to keep the service running. // Android ships with a stripped-down Bouncy Castle provider under the name "BC". // We must remove the system provider first to ensure the full Bouncy Castle library @@ -44,13 +51,43 @@ object App { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME) Security.addProvider(BouncyCastleProvider()) - maintainService() + // This starts the message queue processing. It blocks here indefinitely + // processing messages until Looper.myLooper().quit() is called. + Looper.loop() } catch (e: Exception) { SystemLogger.error("A fatal error occurred in the main application thread.", e) throw e } } + /** Initializes the necessary Android framework internals to satisfy KeyStore requirements. */ + private fun prepareEnvironment() { + // 1. Prepare Main Looper + if (Looper.getMainLooper() == null) { + @Suppress("deprecation") Looper.prepareMainLooper() + } + + // 2. Initialize ActivityThread for the current process + val activityThread = ActivityThread.systemMain() + + // 3. Get the system context + val systemContext = activityThread.getSystemContext() + + // 4. Create a dummy Application object and attach the context + val app = Application() + val attachMethod = + ContextWrapper::class.java.getDeclaredMethod("attachBaseContext", Context::class.java) + attachMethod.isAccessible = true + attachMethod.invoke(app, systemContext) + + // 5. Inject this application object into ActivityThread's mInitialApplication field. + // This is what KeyStore.getApplicationContext() looks for. + val mInitialApplicationField = + ActivityThread::class.java.getDeclaredField("mInitialApplication") + mInitialApplicationField.isAccessible = true + mInitialApplicationField.set(activityThread, app) + } + /** * Selects and initializes the correct keystore interceptor based on the Android SDK version. It * retries initialization until it succeeds. @@ -89,15 +126,4 @@ object App { Keystore2Interceptor } } - - /** - * Puts the main thread into a long-running sleep loop. This is a common pattern to keep a - * background service process alive indefinitely. - */ - private fun maintainService() { - SystemLogger.info("Service started successfully. Entering maintenance mode.") - while (true) { - Thread.sleep(SERVICE_SLEEP_MS) - } - } } diff --git a/stub/src/main/java/android/app/ActivityThread.java b/stub/src/main/java/android/app/ActivityThread.java index a62e724..58f8e2c 100644 --- a/stub/src/main/java/android/app/ActivityThread.java +++ b/stub/src/main/java/android/app/ActivityThread.java @@ -4,4 +4,12 @@ public class ActivityThread { public static void initializeMainlineModules() { throw new UnsupportedOperationException("STUB!"); } + + public static ActivityThread systemMain() { + throw new UnsupportedOperationException("STUB!"); + } + + public ContextImpl getSystemContext() { + throw new UnsupportedOperationException("STUB!"); + } } diff --git a/stub/src/main/java/android/app/ContextImpl.java b/stub/src/main/java/android/app/ContextImpl.java new file mode 100644 index 0000000..62daab0 --- /dev/null +++ b/stub/src/main/java/android/app/ContextImpl.java @@ -0,0 +1,4 @@ +package android.app; + +public class ContextImpl { +}