diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fcb152 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +out diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..eee9e2f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "app/src/main/cpp/external/LSPlt"] + path = app/src/main/cpp/external/LSPlt + url = https://github.com/JingMatrix/LSPlt diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..2a276fc --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,195 @@ +import com.android.build.api.artifact.SingleArtifact +import java.io.ByteArrayOutputStream +import javax.inject.Inject +import org.gradle.process.ExecOperations + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ktfmt) +} + +ktfmt { kotlinLangStyle() } + +// Helper class to get access to the ExecOperations service +abstract class GitExecutor @Inject constructor(private val execOperations: ExecOperations) { + fun execute(command: String, currentWorkingDir: File): String { + val byteOut = ByteArrayOutputStream() + execOperations.exec { + workingDir = currentWorkingDir + commandLine = command.split("\\s".toRegex()) + standardOutput = byteOut + } + return String(byteOut.toByteArray()).trim() + } +} + +// Instantiate the helper class using Gradle's object factory +val gitExecutor = objects.newInstance(GitExecutor::class.java) + +val gitCommitCount = gitExecutor.execute("git rev-list HEAD --count", rootDir).toInt() +val gitCommitHash = gitExecutor.execute("git rev-parse --verify --short HEAD", rootDir) +val verName = "v1.0" + +android { + namespace = "org.matrix.TEESimulator" + compileSdk = 36 + ndkVersion = "27.3.13750724" + buildToolsVersion = "36.0.0" + + defaultConfig { + applicationId = "org.matrix.TEESimulator" + minSdk = 29 + targetSdk = 36 + versionCode = gitCommitCount + versionName = verName + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles("proguard-rules.pro") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + externalNativeBuild { + cmake { + path = file("src/main/cpp/CMakeLists.txt") + buildStagingDirectory = layout.buildDirectory.get().asFile + } + } +} + +dependencies { + compileOnly(project(":stub")) + compileOnly(libs.annotation) +} + +androidComponents { + onVariants(selector().all()) { variant -> + val capitalized = variant.name.replaceFirstChar { it.uppercase() } + val isDebug = variant.buildType == "debug" + + // --- Define output locations and file names --- + // Stage all files in a temporary directory inside 'build' before zipping + val tempModuleDir = project.layout.buildDirectory.dir("module/${variant.name}") + val zipFileName = "TEESimulator-$verName-$gitCommitCount-$gitCommitHash-$capitalized.zip" + + // Task 1: Prepare all module files in the temporary build directory. + // Using Sync ensures that stale files from previous runs are removed. + val prepareModuleFilesTask = + tasks.register("prepareModuleFiles${capitalized}") { + group = "TEESimulator Module Packaging" + description = "Prepares all files for the ${variant.name} module zip." + + if (isDebug) { + dependsOn("package${capitalized}") + } else { + dependsOn("minify${capitalized}WithR8") + } + dependsOn("strip${capitalized}DebugSymbols") + + if (isDebug) { + from(variant.artifacts.get(SingleArtifact.APK)) { + include("*.apk") + rename { "service.apk" } + } + } else { + from( + project.layout.buildDirectory.dir( + "intermediates/dex/${variant.name}/minify${capitalized}WithR8" + ) + ) { + include("classes.dex") + } + } + + from( + project.layout.buildDirectory.dir( + "intermediates/stripped_native_libs/${variant.name}/strip${capitalized}DebugSymbols/out/lib" + ) + ) { + into("lib") // Place them in the 'lib' subfolder of the staging directory. + include("**/libinject.so", "**/libTEESimulator.so") + } + + // Now, copy and process the files from 'module' directory. + val sourceModuleDir = rootProject.projectDir.resolve("module") + from(sourceModuleDir) { + exclude("module.prop") // Exclude the template file. + } + + // Copy and filter the module.prop template separately. + from(sourceModuleDir) { + include("module.prop") + // Use expand() for simple key-value replacement. + expand( + "REPLACEMEVERCODE" to gitCommitCount.toString(), + "REPLACEMEVER" to + "$verName ($gitCommitCount-$gitCommitHash-${variant.name})", + ) + } + + // The destination for all the above 'from' operations. + into(tempModuleDir) + } + + // Task 2: Zip the prepared files from the temporary directory. + val zipTask = + tasks.register("zip${capitalized}") { + group = "TEESimulator Module Packaging" + description = "Creates the flashable zip for the ${variant.name} module." + dependsOn(prepareModuleFilesTask) + + archiveFileName.set(zipFileName) + destinationDirectory.set(project.rootDir.resolve("out")) + from(tempModuleDir) // Zip the entire contents of the staging directory. + } + + // Task 3: A helper function to create installation tasks for different root providers. + fun createInstallTasks(rootProvider: String, installCli: String) { + val pushTask = + tasks.register("push${rootProvider}Module${capitalized}") { + group = "TEESimulator Module Installation" + description = + "Pushes the ${variant.name} module to the device for $rootProvider." + dependsOn(zipTask) + commandLine( + "adb", + "push", + zipTask.get().archiveFile.get().asFile, + "/data/local/tmp", + ) + } + + val installTask = + tasks.register("install${rootProvider}${capitalized}") { + group = "TEESimulator Module Installation" + description = "Installs the ${variant.name} module via $rootProvider." + dependsOn(pushTask) + commandLine( + "adb", + "shell", + "su", + "-c", + "$installCli /data/local/tmp/$zipFileName", + ) + } + + tasks.register("install${rootProvider}AndReboot${capitalized}") { + group = "TEESimulator Module Installation" + description = "Installs the ${variant.name} module via $rootProvider and reboots." + dependsOn(installTask) + commandLine("adb", "reboot") + } + } + + createInstallTasks("Magisk", "magisk --install-module") + createInstallTasks("Ksu", "ksud module install") + createInstallTasks("Apatch", "/data/adb/apd module install") + } +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..3c6bc50 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,3 @@ +-keepclasseswithmembers class org.matrix.TEESimulator.MainKt { + public static void main(java.lang.String[]); +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8072ee0 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..267829d --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.10) +project(TEESimulator) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") + +# LSPlt configuration +OPTION(LSPLT_BUILD_SHARED OFF) +add_subdirectory(external/LSPlt/lsplt/src/main/jni) + +add_executable(libinject.so inject/main.cpp) +target_link_libraries(libinject.so PRIVATE lsplt_static) + +add_library(${CMAKE_PROJECT_NAME} SHARED binder_interceptor.cpp) +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE lsplt_static) + diff --git a/app/src/main/cpp/binder_interceptor.cpp b/app/src/main/cpp/binder_interceptor.cpp new file mode 100644 index 0000000..7739148 --- /dev/null +++ b/app/src/main/cpp/binder_interceptor.cpp @@ -0,0 +1,6 @@ +#include "lsplt.hpp" + +extern "C" [[gnu::visibility("default")]] [[gnu::used]] +bool entry(void *library_handle) { + return true; +} diff --git a/app/src/main/cpp/external/LSPlt b/app/src/main/cpp/external/LSPlt new file mode 160000 index 0000000..3e29437 --- /dev/null +++ b/app/src/main/cpp/external/LSPlt @@ -0,0 +1 @@ +Subproject commit 3e29437f037cb7d2b9fbb459dcf162f6b8d1d926 diff --git a/app/src/main/cpp/inject/main.cpp b/app/src/main/cpp/inject/main.cpp new file mode 100644 index 0000000..f134d92 --- /dev/null +++ b/app/src/main/cpp/inject/main.cpp @@ -0,0 +1,3 @@ +#include "lsplt.hpp" + +int main(int argc, char **argv) { return 0; } diff --git a/app/src/main/java/org/matrix/TEESimulator/Main.kt b/app/src/main/java/org/matrix/TEESimulator/Main.kt new file mode 100644 index 0000000..16fd626 --- /dev/null +++ b/app/src/main/java/org/matrix/TEESimulator/Main.kt @@ -0,0 +1,3 @@ +package org.matrix.TEESimulator + +fun main(args: Array) {} diff --git a/build.gradle.kts b/build.gradle.kts index 8416bc7..4968eeb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ tasks.register("format") { source = project.fileTree(rootDir) include("*.gradle.kts", "*/*.gradle.kts") dependsOn(":stub:ktfmtFormat") + dependsOn(":app:ktfmtFormat") } ktfmt { kotlinLangStyle() } diff --git a/module/META-INF/com/google/android/update-binary b/module/META-INF/com/google/android/update-binary new file mode 100644 index 0000000..28b48e5 --- /dev/null +++ b/module/META-INF/com/google/android/update-binary @@ -0,0 +1,33 @@ +#!/sbin/sh + +################# +# Initialization +################# + +umask 022 + +# echo before loading util_functions +ui_print() { echo "$1"; } + +require_new_magisk() { + ui_print "*******************************" + ui_print " Please install Magisk v20.4+! " + ui_print "*******************************" + exit 1 +} + +######################### +# Load util_functions.sh +######################### + +OUTFD=$2 +ZIPFILE=$3 + +mount /data 2>/dev/null + +[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk +. /data/adb/magisk/util_functions.sh +[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk + +install_module +exit 0 diff --git a/module/META-INF/com/google/android/updater-script b/module/META-INF/com/google/android/updater-script new file mode 100644 index 0000000..11d5c96 --- /dev/null +++ b/module/META-INF/com/google/android/updater-script @@ -0,0 +1 @@ +#MAGISK diff --git a/module/module.prop b/module/module.prop new file mode 100644 index 0000000..de795c9 --- /dev/null +++ b/module/module.prop @@ -0,0 +1,7 @@ +id=tricky_store +name=TEESimulator +version=${REPLACEMEVER} +versionCode=${REPLACEMEVERCODE} +author=JingMatrix +description=Software simulation for Android hardware-backed key pairs with key attestation +updateJson=https://raw.githubusercontent.com/JingMatrix/TEESimulator/main/module/update.json diff --git a/settings.gradle.kts b/settings.gradle.kts index 312474a..24ae7a3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,3 +17,5 @@ dependencyResolutionManagement { rootProject.name = "TEESimulator" include(":stub") + +include(":app")