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()`.
This commit is contained in:
JingMatrix
2026-01-26 14:07:06 +01:00
committed by GitHub
parent ab4fe643a3
commit 9f77771e7b
4 changed files with 62 additions and 15 deletions
+9
View File
@@ -386,6 +386,15 @@ void inspectAndRewriteTransaction(binder_transaction_data *txn_data) {
} }
if (hijack) { 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; uint64_t tx_id = ++g_transaction_id_counter;
info.transaction_id = tx_id; info.transaction_id = tx_id;
@@ -1,6 +1,11 @@
package org.matrix.TEESimulator 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.Build
import android.os.Looper
import java.security.Security import java.security.Security
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.matrix.TEESimulator.config.ConfigurationManager import org.matrix.TEESimulator.config.ConfigurationManager
@@ -30,13 +35,15 @@ object App {
SystemLogger.info("Welcome to TEESimulator!") SystemLogger.info("Welcome to TEESimulator!")
try { try {
// Initialize the Android framework environment
prepareEnvironment()
// Initialize and start the appropriate keystore interceptors.
initializeInterceptors()
// Load the package configuration. // Load the package configuration.
ConfigurationManager.initialize() ConfigurationManager.initialize()
// Set up the device's boot key and hash, which are crucial for attestation. // Set up the device's boot key and hash, which are crucial for attestation.
AndroidDeviceUtils.setupBootKeyAndHash() 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". // 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 // 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.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(BouncyCastleProvider()) 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) { } catch (e: Exception) {
SystemLogger.error("A fatal error occurred in the main application thread.", e) SystemLogger.error("A fatal error occurred in the main application thread.", e)
throw 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 * Selects and initializes the correct keystore interceptor based on the Android SDK version. It
* retries initialization until it succeeds. * retries initialization until it succeeds.
@@ -89,15 +126,4 @@ object App {
Keystore2Interceptor 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)
}
}
} }
@@ -4,4 +4,12 @@ public class ActivityThread {
public static void initializeMainlineModules() { public static void initializeMainlineModules() {
throw new UnsupportedOperationException("STUB!"); throw new UnsupportedOperationException("STUB!");
} }
public static ActivityThread systemMain() {
throw new UnsupportedOperationException("STUB!");
}
public ContextImpl getSystemContext() {
throw new UnsupportedOperationException("STUB!");
}
} }
@@ -0,0 +1,4 @@
package android.app;
public class ContextImpl {
}