diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index ae19985d..3e50c673 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -55,7 +55,7 @@ jobs: with: path: | ~/.ccache - core/build/.lto-cache + magisk-loader/build/.lto-cache daemon/build/.lto-cache key: native-cache-${{ github.sha }} restore-keys: native-cache- @@ -83,14 +83,14 @@ jobs: if: success() id: prepareArtifact run: | - riruReleaseName=`ls core/release/LSPosed-v*-riru-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruReleaseName::$riruReleaseName" - riruDebugName=`ls core/release/LSPosed-v*-riru-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruDebugName::$riruDebugName" - zygiskReleaseName=`ls core/release/LSPosed-v*-zygisk-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskReleaseName::$zygiskReleaseName" - zygiskDebugName=`ls core/release/LSPosed-v*-zygisk-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskDebugName::$zygiskDebugName" - unzip core/release/LSPosed-v*-riru-release.zip -d LSPosed-riru-release - unzip core/release/LSPosed-v*-riru-debug.zip -d LSPosed-riru-debug - unzip core/release/LSPosed-v*-zygisk-release.zip -d LSPosed-zygisk-release - unzip core/release/LSPosed-v*-zygisk-debug.zip -d LSPosed-zygisk-debug + riruReleaseName=`ls magisk-loader/release/LSPosed-v*-riru-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruReleaseName::$riruReleaseName" + riruDebugName=`ls magisk-loader/release/LSPosed-v*-riru-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruDebugName::$riruDebugName" + zygiskReleaseName=`ls magisk-loader/release/LSPosed-v*-zygisk-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskReleaseName::$zygiskReleaseName" + zygiskDebugName=`ls magisk-loader/release/LSPosed-v*-zygisk-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskDebugName::$zygiskDebugName" + unzip magisk-loader/release/LSPosed-v*-riru-release.zip -d LSPosed-riru-release + unzip magisk-loader/release/LSPosed-v*-riru-debug.zip -d LSPosed-riru-debug + unzip magisk-loader/release/LSPosed-v*-zygisk-release.zip -d LSPosed-zygisk-release + unzip magisk-loader/release/LSPosed-v*-zygisk-debug.zip -d LSPosed-zygisk-debug - name: Upload riru release uses: actions/upload-artifact@v2 with: @@ -116,14 +116,14 @@ jobs: with: name: mappings path: | - core/build/outputs/mapping + magisk-loader/build/outputs/mapping app/build/outputs/mapping - name: Upload symbols uses: actions/upload-artifact@v2 with: name: symbols path: | - core/build/symbols + magisk-loader/build/symbols daemon/build/symbols - name: Post to channel if: ${{ github.event_name != 'pull_request' && success() && github.ref == 'refs/heads/master' }} @@ -134,7 +134,7 @@ jobs: COMMIT_URL: ${{ github.event.head_commit.url }} run: | if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then - OUTPUT="core/release/" + OUTPUT="magisk-loader/release/" export riruRelease=$(find $OUTPUT -name "LSPosed-v*-riru-release.zip") export riruDebug=$(find $OUTPUT -name "LSPosed-v*-riru-debug.zip") export zygiskRelease=$(find $OUTPUT -name "LSPosed-v*-zygisk-release.zip") diff --git a/build.gradle.kts b/build.gradle.kts index bf2fcde0..6d157aff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,8 +14,9 @@ * You should have received a copy of the GNU General Public License * along with LSPosed. If not, see . * - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ + import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.gradle.BaseExtension @@ -77,6 +78,7 @@ fun Project.configureBaseExtension() { externalNativeBuild { cmake { arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "external")}" + arguments += "-DCORE_ROOT=${File(rootDir.absolutePath, "core/src/main/jni")}" abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64") val flags = arrayOf( "-Wall", diff --git a/core/.gitignore b/core/.gitignore index 99ef122d..9ff2a377 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,4 +1,2 @@ /build -/release -/src/main/cpp/api/config.cpp /.cxx diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e454798d..d488f2d9 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -14,56 +14,26 @@ * You should have received a copy of the GNU General Public License * along with LSPosed. If not, see . * - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ -import org.apache.commons.codec.binary.Hex -import org.apache.tools.ant.filters.FixCrLfFilter -import org.apache.tools.ant.filters.ReplaceTokens -import java.io.ByteArrayOutputStream -import java.security.MessageDigest -import java.util.* +val apiCode: Int by rootProject.extra plugins { - id("com.android.application") + id("com.android.library") } -val moduleName = "LSPosed" -val moduleBaseId = "lsposed" -val authors = "LSPosed Developers" - -val riruModuleId = "lsposed" -val moduleMinRiruApiVersion = 25 -val moduleMinRiruVersionName = "25.0.1" -val moduleMaxRiruApiVersion = 25 - -val injectedPackageName: String by rootProject.extra -val injectedPackageUid: Int by rootProject.extra - -val defaultManagerPackageName: String by rootProject.extra -val apiCode: Int by rootProject.extra -val verCode: Int by rootProject.extra -val verName: String by rootProject.extra - android { - flavorDimensions += "api" + namespace = "org.lsposed.lspd.core" buildFeatures { - prefab = true + androidResources = false } defaultConfig { - applicationId = "org.lsposed.lspd" - multiDexEnabled = false + consumerProguardFiles("proguard-rules.pro") buildConfigField("int", "API_CODE", "$apiCode") - buildConfigField( - "String", - "DEFAULT_MANAGER_PACKAGE_NAME", - """"$defaultManagerPackageName"""" - ) - buildConfigField("String", "MANAGER_INJECTED_PKG_NAME", """"$injectedPackageName"""") - buildConfigField("int", "MANAGER_INJECTED_UID", """$injectedPackageUid""") } buildTypes { @@ -72,253 +42,14 @@ android { proguardFiles("proguard-rules.pro") } } - - externalNativeBuild { - cmake { - path("src/main/jni/CMakeLists.txt") - } - } - - productFlavors { - all { - externalNativeBuild { - cmake { - arguments += "-DMODULE_NAME=${name.toLowerCase()}_$moduleBaseId" - arguments += "-DAPI=${name.toLowerCase()}" - } - } - buildConfigField("String", "API", """"$name"""") - } - - create("Riru") { - dimension = "api" - externalNativeBuild { - cmake { - arguments += "-DAPI_VERSION=$moduleMaxRiruApiVersion" - } - } - } - - create("Zygisk") { - dimension = "api" - externalNativeBuild { - cmake { - arguments += "-DAPI_VERSION=1" - } - } - } - } - } - dependencies { implementation("org.apache.commons:commons-lang3:3.12.0") implementation("de.upb.cs.swt:axml:2.1.2") compileOnly("androidx.annotation:annotation:1.3.0") compileOnly(projects.hiddenapi.stubs) implementation(projects.hiddenapi.bridge) - implementation(projects.services.managerService) implementation(projects.services.daemonService) + implementation(projects.services.managerService) } - -val zipAll = task("zipAll") { - -} - -val apkDir: String - get() = if (rootProject.extra.properties["android.injected.invoked.from.ide"] == "true") "intermediates" else "outputs" - -fun afterEval() = android.applicationVariants.forEach { variant -> - val variantCapped = variant.name.capitalize(Locale.ROOT) - val variantLowered = variant.name.toLowerCase(Locale.ROOT) - val buildTypeCapped = variant.buildType.name.capitalize(Locale.ROOT) - val buildTypeLowered = variant.buildType.name.toLowerCase(Locale.ROOT) - val flavorCapped = variant.flavorName!!.capitalize(Locale.ROOT) - val flavorLowered = variant.flavorName!!.toLowerCase(Locale.ROOT) - - val magiskDir = "$buildDir/magisk/$variantLowered" - - val moduleId = "${flavorLowered}_$moduleBaseId" - val zipFileName = "$moduleName-v$verName-$verCode-${flavorLowered}-$buildTypeLowered.zip" - - val prepareMagiskFilesTask = task("prepareMagiskFiles$variantCapped") { - dependsOn( - "assemble$variantCapped", - ":app:package$buildTypeCapped", - ":daemon:package$buildTypeCapped" - ) - into(magiskDir) - from("${rootProject.projectDir}/README.md") - from("$projectDir/magisk_module") { - exclude("riru.sh", "module.prop", "customize.sh", "sepolicy.rule", "post-fs-data.sh") - } - from("$projectDir/magisk_module") { - include("module.prop") - expand( - "moduleId" to moduleId, - "versionName" to "v$verName", - "versionCode" to verCode, - "authorList" to authors, - "updateJson" to "https://lsposed.github.io/LSPosed/release/${flavorLowered}.json", - "requirement" to when (flavorLowered) { - "riru" -> "Requires Riru $moduleMinRiruVersionName or above installed" - "zygisk" -> "Requires Magisk 24.0+ and Zygisk enabled" - else -> "No further requirements" - }, - "api" to flavorCapped - ) - filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) - } - from("$projectDir/magisk_module") { - include("customize.sh", "post-fs-data.sh") - val tokens = mapOf("FLAVOR" to flavorLowered) - filter("tokens" to tokens) - filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) - } - if (flavorLowered == "riru") { - from("${projectDir}/magisk_module") { - include("riru.sh", "sepolicy.rule") - val tokens = mapOf( - "RIRU_MODULE_LIB_NAME" to "lspd", - "RIRU_MODULE_API_VERSION" to moduleMaxRiruApiVersion.toString(), - "RIRU_MODULE_MIN_API_VERSION" to moduleMinRiruApiVersion.toString(), - "RIRU_MODULE_MIN_RIRU_VERSION_NAME" to moduleMinRiruVersionName, - "RIRU_MODULE_DEBUG" to if (buildTypeLowered == "debug") "true" else "false", - ) - filter("tokens" to tokens) - filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) - } - } - from(project(":app").tasks.getByName("package$buildTypeCapped").outputs) { - include("*.apk") - rename(".*\\.apk", "manager.apk") - } - from(project(":daemon").tasks.getByName("package$buildTypeCapped").outputs) { - include("*.apk") - rename(".*\\.apk", "daemon.apk") - } - into("lib") { - from("${buildDir}/intermediates/cmake/$variantCapped/obj") { - include("**/liblspd.so") - } - from("${project(":daemon").buildDir}/intermediates/cmake/$buildTypeLowered/obj") { - include("**/libdaemon.so") - } - } - val dexOutPath = if (buildTypeLowered == "release") - "$buildDir/intermediates/dex/$variantCapped/minify${variantCapped}WithR8" else - "$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped" - into("framework") { - from(dexOutPath) - rename("classes.dex", "lspd.dex") - } - doLast { - fileTree(magiskDir).visit { - if (isDirectory) return@visit - val md = MessageDigest.getInstance("SHA-256") - file.forEachBlock(4096) { bytes, size -> - md.update(bytes, 0, size) - } - file(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest())) - } - } - } - - val zipTask = task("zip${variantCapped}") { - dependsOn(prepareMagiskFilesTask) - archiveFileName.set(zipFileName) - destinationDirectory.set(file("$projectDir/release")) - from(magiskDir) - } - - zipAll.dependsOn(zipTask) - - val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath - val pushTask = task("push${variantCapped}") { - dependsOn(zipTask) - workingDir("${projectDir}/release") - commandLine(adb, "push", zipFileName, "/data/local/tmp/") - } - val flashTask = task("flash${variantCapped}") { - dependsOn(pushTask) - commandLine( - adb, "shell", "su", "-c", - "magisk --install-module /data/local/tmp/${zipFileName}" - ) - } - task("flashAndReboot${variantCapped}") { - dependsOn(flashTask) - commandLine(adb, "shell", "reboot") - } -} - -afterEvaluate { - afterEval() -} - -val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath -val killLspd = task("killLspd") { - commandLine(adb, "shell", "su", "-c", "killall", "lspd") - isIgnoreExitValue = true -} -val pushDaemon = task("pushDaemon") { - dependsOn(":daemon:assembleDebug") - workingDir("${project(":daemon").buildDir}/$apkDir/apk/debug") - commandLine(adb, "push", "daemon-debug.apk", "/data/local/tmp/daemon.apk") -} -val pushDaemonNative = task("pushDaemonNative") { - dependsOn(":daemon:assembleDebug") - doFirst { - val abi: String = ByteArrayOutputStream().use { outputStream -> - exec { - commandLine(adb, "shell", "getprop", "ro.product.cpu.abi") - standardOutput = outputStream - } - outputStream.toString().trim() - } - workingDir("${project(":daemon").buildDir}/intermediates/ndkBuild/debug/obj/local/$abi") - } - commandLine(adb, "push", "libdaemon.so", "/data/local/tmp/libdaemon.so") -} -val reRunDaemon = task("reRunDaemon") { - dependsOn(pushDaemon, pushDaemonNative, killLspd) - // tricky to pass a minus number to avoid the injection warning - commandLine( - adb, - "shell", - "ASH_STANDALONE=1", - "su", - "-pc", - "/data/adb/magisk/busybox sh /data/adb/modules/*_lsposed/service.sh --system-server-max-retry=-1&" - ) - isIgnoreExitValue = true -} -val tmpApk = "/data/local/tmp/lsp.apk" -val pushApk = task("pushApk") { - dependsOn(":app:assembleDebug") - workingDir("${project(":app").buildDir}/$apkDir/apk/debug") - commandLine(adb, "push", "app-debug.apk", tmpApk) -} -val openApp = task("openApp") { - commandLine( - adb, - "shell", - "am", - "start", - "-a", - "android.intent.action.MAIN", - "-c", - "org.lsposed.manager.LAUNCH_MANAGER", - "com.android.shell/.BugreportWarningActivity" - ) -} -task("reRunApp") { - dependsOn(pushApk) - commandLine(adb, "shell", "su", "-c", "mv -f $tmpApk /data/adb/lspd/manager.apk") - isIgnoreExitValue = true - finalizedBy(reRunDaemon) -} - -evaluationDependsOn(":app") -evaluationDependsOn(":daemon") diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro index f92b4bc8..5e7ede50 100644 --- a/core/proguard-rules.pro +++ b/core/proguard-rules.pro @@ -1,15 +1,8 @@ -keep class de.robv.android.xposed.** {*;} -keep class android.** { *; } --keepclasseswithmembers class org.lsposed.lspd.core.Main { - public static void forkSystemServerPost(android.os.IBinder); - public static void forkAndSpecializePost(java.lang.String, java.lang.String, android.os.IBinder); -} -keepclasseswithmembers,includedescriptorclasses class * { native ; } --keepclasseswithmembers class org.lsposed.lspd.service.BridgeService { - public static boolean *(android.os.IBinder, int, long, long, int); -} -assumenosideeffects class android.util.Log { public static *** v(...); diff --git a/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java b/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java index fe7ab025..7a77f9e8 100644 --- a/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java +++ b/core/src/main/java/de/robv/android/xposed/XSharedPreferences.java @@ -15,7 +15,7 @@ * along with LSPosed. If not, see . * * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ package de.robv.android.xposed; @@ -31,7 +31,7 @@ import android.util.Log; import com.android.internal.util.XmlUtils; -import org.lsposed.lspd.BuildConfig; +import org.lsposed.lspd.core.BuildConfig; import org.lsposed.lspd.util.MetaDataReader; import org.xmlpull.v1.XmlPullParserException; diff --git a/core/src/main/java/de/robv/android/xposed/XposedBridge.java b/core/src/main/java/de/robv/android/xposed/XposedBridge.java index 723000af..3b813c8e 100644 --- a/core/src/main/java/de/robv/android/xposed/XposedBridge.java +++ b/core/src/main/java/de/robv/android/xposed/XposedBridge.java @@ -15,7 +15,7 @@ * along with LSPosed. If not, see . * * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ package de.robv.android.xposed; @@ -27,7 +27,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.util.Log; -import org.lsposed.lspd.BuildConfig; +import org.lsposed.lspd.core.BuildConfig; import org.lsposed.lspd.nativebridge.HookBridge; import org.lsposed.lspd.nativebridge.ResourcesHook; diff --git a/core/src/main/java/org/lsposed/lspd/core/Main.java b/core/src/main/java/org/lsposed/lspd/core/Startup.java similarity index 62% rename from core/src/main/java/org/lsposed/lspd/core/Main.java rename to core/src/main/java/org/lsposed/lspd/core/Startup.java index 4fcc3c05..5702f770 100644 --- a/core/src/main/java/org/lsposed/lspd/core/Main.java +++ b/core/src/main/java/org/lsposed/lspd/core/Startup.java @@ -15,7 +15,7 @@ * along with LSPosed. If not, see . * * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ package org.lsposed.lspd.core; @@ -24,30 +24,23 @@ import android.app.ActivityThread; import android.app.LoadedApk; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; -import android.os.Environment; -import android.os.IBinder; import android.os.Process; import com.android.internal.os.ZygoteInit; -import org.lsposed.lspd.BuildConfig; -import org.lsposed.lspd.config.LSPApplicationServiceClient; import org.lsposed.lspd.deopt.PrebuiltMethodsDeopter; import org.lsposed.lspd.hooker.CrashDumpHooker; import org.lsposed.lspd.hooker.HandleBindAppHooker; -import org.lsposed.lspd.hooker.LoadedApkCstrHooker; import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker; -import org.lsposed.lspd.util.ParasiticManagerHooker; +import org.lsposed.lspd.hooker.LoadedApkCstrHooker; import org.lsposed.lspd.util.Utils; -import java.io.File; - import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.XposedInit; -public class Main { - public static void startBootstrapHook(boolean isSystem, String appDataDir) { +public class Startup { + private static void startBootstrapHook(boolean isSystem) { Utils.logD("startBootstrapHook starts: isSystem = " + isSystem); XposedHelpers.findAndHookMethod(Thread.class, "dispatchUncaughtException", Throwable.class, new CrashDumpHooker()); @@ -58,45 +51,28 @@ public class Main { XposedHelpers.findAndHookMethod(ActivityThread.class, "handleBindApplication", "android.app.ActivityThread$AppBindData", - new HandleBindAppHooker(appDataDir)); + new HandleBindAppHooker()); XposedHelpers.findAndHookConstructor(LoadedApk.class, ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class, ClassLoader.class, boolean.class, boolean.class, boolean.class, new LoadedApkCstrHooker()); } - private static void installBootstrapHooks(boolean isSystem, String appDataDir) { + public static void bootstrapXposed(String niceName) { // Initialize the Xposed framework try { - startBootstrapHook(isSystem, appDataDir); + startBootstrapHook(XposedInit.startsSystemServer); + Utils.logI("Loading modules for " + niceName + "/" + Process.myUid()); + XposedInit.loadModules(); } catch (Throwable t) { Utils.logE("error during Xposed initialization", t); } } - public static void forkPostCommon(boolean isSystem, String appDataDir, String niceName) { + public static void initXposed(boolean isSystem) { // init logger XposedBridge.initXResources(); XposedInit.startsSystemServer = isSystem; PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote - if ((niceName.equals(BuildConfig.MANAGER_INJECTED_PKG_NAME) || niceName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME)) - && ParasiticManagerHooker.start()) { - Utils.logI("Loaded manager, skipping next steps"); - return; - } - installBootstrapHooks(isSystem, appDataDir); - Utils.logI("Loading modules for " + niceName + "/" + Process.myUid()); - XposedInit.loadModules(); - } - - public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) { - LSPApplicationServiceClient.Init(binder, niceName); - forkPostCommon(false, appDataDir, niceName); - } - - public static void forkSystemServerPost(IBinder binder) { - LSPApplicationServiceClient.Init(binder, "android"); - forkPostCommon(true, - new File(Environment.getDataDirectory(), "android").toString(), "system_server"); } } diff --git a/core/src/main/java/org/lsposed/lspd/hooker/HandleBindAppHooker.java b/core/src/main/java/org/lsposed/lspd/hooker/HandleBindAppHooker.java index 00f17763..686ce0e6 100644 --- a/core/src/main/java/org/lsposed/lspd/hooker/HandleBindAppHooker.java +++ b/core/src/main/java/org/lsposed/lspd/hooker/HandleBindAppHooker.java @@ -15,7 +15,7 @@ * along with LSPosed. If not, see . * * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ package org.lsposed.lspd.hooker; @@ -35,11 +35,6 @@ import de.robv.android.xposed.XposedInit; // normal process initialization (for new Activity, Service, BroadcastReceiver etc.) public class HandleBindAppHooker extends XC_MethodHook { - String appDataDir; - - public HandleBindAppHooker(String appDataDir) { - this.appDataDir = appDataDir; - } @Override protected void beforeHookedMethod(MethodHookParam param) { @@ -54,8 +49,7 @@ public class HandleBindAppHooker extends XC_MethodHook { // Note: packageName="android" -> system_server process, ams pms etc; // packageName="system" -> android pkg, system dialogues. - Utils.logD("processName=" + appProcessName + - ", packageName=" + reportedPackageName + ", appDataDir=" + appDataDir); + Utils.logD("processName=" + appProcessName + ", packageName=" + reportedPackageName); CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(bindData, "compatInfo"); if (appInfo.sourceDir == null) { diff --git a/core/src/main/jni/CMakeLists.txt b/core/src/main/jni/CMakeLists.txt index 30c2202a..8dfa79b8 100644 --- a/core/src/main/jni/CMakeLists.txt +++ b/core/src/main/jni/CMakeLists.txt @@ -1,4 +1,4 @@ -project(lspd) +project(core) cmake_minimum_required(VERSION 3.4.1) add_subdirectory(${EXTERNAL_ROOT} external) @@ -7,27 +7,11 @@ configure_file(template/config.cpp src/config.cpp) aux_source_directory(src SRC_LIST) aux_source_directory(src/jni SRC_LIST) -if (${API} STREQUAL "riru") - set(SRC_LIST ${SRC_LIST} api/riru_main.cpp) -elseif (${API} STREQUAL "zygisk") - set(SRC_LIST ${SRC_LIST} api/zygisk_main.cpp) -endif() -add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/src/config.cpp) +add_library(${PROJECT_NAME} STATIC ${SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/src/config.cpp) target_include_directories(${PROJECT_NAME} PUBLIC include) target_include_directories(${PROJECT_NAME} PRIVATE src) -target_link_libraries(${PROJECT_NAME} dobby dex_builder_static log lsplant_static) - -if (DEFINED DEBUG_SYMBOLS_PATH) - set(DEBUG_SYMBOLS_PATH ${DEBUG_SYMBOLS_PATH}/${API}) - message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}") - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI} - COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $ - ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug - COMMAND ${CMAKE_STRIP} --strip-all $ - COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug - $) -endif() +target_link_libraries(${PROJECT_NAME} PUBLIC dobby lsplant_static log) +target_link_libraries(${PROJECT_NAME} PRIVATE dex_builder_static) diff --git a/core/src/main/jni/include/config.h b/core/src/main/jni/include/config.h index 5ab07278..4c5eff54 100644 --- a/core/src/main/jni/include/config.h +++ b/core/src/main/jni/include/config.h @@ -15,7 +15,7 @@ * along with LSPosed. If not, see . * * Copyright (C) 2020 EdXposed Contributors - * Copyright (C) 2021 LSPosed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors */ #pragma once @@ -59,9 +59,6 @@ namespace lspd { # define LP_SELECT(lp32, lp64) lp32 #endif - inline static constexpr auto kEntryClassName = "org.lsposed.lspd.core.Main"_tstr; - inline static constexpr auto kBridgeServiceClassName = "org.lsposed.lspd.service.BridgeService"_tstr; - inline static constexpr auto kLibArtName = "libart.so"_tstr; inline static constexpr auto kLibFwName = "libandroidfw.so"_tstr; @@ -70,7 +67,5 @@ namespace lspd { } extern const int versionCode; - extern const int apiVersion; extern const char* const versionName; - extern const char* const moduleName; } diff --git a/core/src/main/jni/src/context.h b/core/src/main/jni/include/context.h similarity index 71% rename from core/src/main/jni/src/context.h rename to core/src/main/jni/include/context.h index 2e66db3c..030ddf2a 100644 --- a/core/src/main/jni/src/context.h +++ b/core/src/main/jni/include/context.h @@ -49,22 +49,12 @@ namespace lspd { return FindClassFromLoader(env, GetCurrentClassLoader(), className); }; - void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring nice_name, - jboolean is_child_zygote, jstring app_data_dir); + virtual ~Context() = default; - void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_data_dir); - - void OnNativeForkSystemServerPost(JNIEnv *env); - - void OnNativeForkSystemServerPre(JNIEnv *env); - - void Init(); - - private: - inline static std::unique_ptr instance_ = std::make_unique(); + protected: + inline static std::unique_ptr instance_; jobject inject_class_loader_ = nullptr; jclass entry_class_ = nullptr; - bool skip_ = false; struct PreloadedDex { @@ -90,7 +80,7 @@ namespace lspd { }; // Use with caution! - PreloadedDex(void* addr, size_t size) : addr_(addr), size_(size) {}; + PreloadedDex(void *addr, size_t size) : addr_(addr), size_(size) {}; operator bool() const { return addr_ && size_; } @@ -107,19 +97,31 @@ namespace lspd { Context() {} - void LoadDex(JNIEnv *env, int fd, size_t size); - - void Init(JNIEnv *env, const lsplant::InitInfo& initInfo); - static lsplant::ScopedLocalRef FindClassFromLoader(JNIEnv *env, jobject class_loader, - std::string_view class_name); - - static void setAllowUnload(bool unload); + std::string_view class_name); template - void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, - Args &&... args) const; + inline void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, + Args &&... args) const { + if (!entry_class_) [[unlikely]] { + LOGE("cannot call method %s, entry class is null", method_name.data()); + return; + } + jmethodID mid = lsplant::JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig); + if (mid) [[likely]] { + lsplant::JNI_CallStaticVoidMethod(env, entry_class_, mid, std::forward(args)...); + } else { + LOGE("method %s id is null", method_name.data()); + } + } + virtual void InitHooks(JNIEnv *env, const lsplant::InitInfo &initInfo); + + virtual void LoadDex(JNIEnv *env, PreloadedDex &&dex) = 0; + + virtual void SetupEntryClass(JNIEnv *env) = 0; + + private: friend std::unique_ptr std::make_unique(); }; diff --git a/core/src/main/jni/src/elf_util.h b/core/src/main/jni/include/elf_util.h similarity index 100% rename from core/src/main/jni/src/elf_util.h rename to core/src/main/jni/include/elf_util.h diff --git a/core/src/main/jni/src/native_hook.h b/core/src/main/jni/include/native_hook.h similarity index 100% rename from core/src/main/jni/src/native_hook.h rename to core/src/main/jni/include/native_hook.h diff --git a/core/src/main/jni/src/symbol_cache.h b/core/src/main/jni/include/symbol_cache.h similarity index 100% rename from core/src/main/jni/src/symbol_cache.h rename to core/src/main/jni/include/symbol_cache.h diff --git a/core/src/main/jni/src/context.cpp b/core/src/main/jni/src/context.cpp index 1c4b47c5..c19e937c 100644 --- a/core/src/main/jni/src/context.cpp +++ b/core/src/main/jni/src/context.cpp @@ -19,39 +19,19 @@ */ #include + #include "config.h" -#include "utils/jni_helper.hpp" -#include "jni/resources_hook.h" #include "context.h" #include "native_hook.h" -#include "elf_util.h" +#include "native_util.h" #include "jni/hook_bridge.h" #include "jni/native_api.h" -#include "service.h" -#include +#include "jni/resources_hook.h" #include "symbol_cache.h" -#include -#include -#include - using namespace lsplant; -static_assert(FS_IOC_SETFLAGS == LP_SELECT(0x40046602, 0x40086602)); - namespace lspd { - extern int *allowUnload; - - constexpr int FIRST_ISOLATED_UID = 99000; - constexpr int LAST_ISOLATED_UID = 99999; - constexpr int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; - constexpr int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; - constexpr int SHARED_RELRO_UID = 1037; - constexpr int PER_USER_RANGE = 100000; - - static constexpr uid_t kAidInjected = INJECTED_AID; - static constexpr uid_t kAidInet = 3003; - Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) { LOGD("Context::PreloadedDex::PreloadedDex: fd=%d, size=%zu", fd, size); auto *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); @@ -68,46 +48,10 @@ namespace lspd { if (*this) munmap(addr_, size_); } - void Context::LoadDex(JNIEnv *env, int fd, size_t size) { - LOGD("Context::LoadDex: %d", fd); - // map fd to memory. fd should be created with ASharedMemory_create. - auto dex = PreloadedDex(fd, size); // for RAII... - - auto classloader = JNI_FindClass(env, "java/lang/ClassLoader"); - auto getsyscl_mid = JNI_GetStaticMethodID( - env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); - auto sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid); - if (!sys_classloader) [[unlikely]] { - LOGE("getSystemClassLoader failed!!!"); - return; - } - auto in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader"); - auto initMid = JNI_GetMethodID(env, in_memory_classloader, "", - "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); - auto byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer"); - auto dex_buffer = env->NewDirectByteBuffer(dex.data(), dex.size()); - if (auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid, - dex_buffer, sys_classloader)) { - inject_class_loader_ = JNI_NewGlobalRef(env, my_cl); - } else { - LOGE("InMemoryDexClassLoader creation failed!!!"); - return; - } - - env->DeleteLocalRef(dex_buffer); - } - - void Context::Init() { - } - - void Context::Init(JNIEnv *env, const lsplant::InitInfo& initInfo) { + void Context::InitHooks(JNIEnv *env, const lsplant::InitInfo& initInfo) { if (!lsplant::Init(env, initInfo)) { return; } - if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), - kEntryClassName)) { - entry_class_ = JNI_NewGlobalRef(env, entry_class); - } RegisterResourcesHook(env); RegisterHookBridge(env); @@ -138,160 +82,4 @@ namespace lspd { LOGE("Class %s not found", class_name.data()); return {env, nullptr}; } - - template - void - Context::FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, - Args &&... args) const { - if (!entry_class_) [[unlikely]] { - LOGE("cannot call method %s, entry class is null", method_name.data()); - return; - } - jmethodID mid = JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig); - if (mid) [[likely]] { - JNI_CallStaticVoidMethod(env, entry_class_, mid, std::forward(args)...); - } else { - LOGE("method %s id is null", method_name.data()); - } - } - - void - Context::OnNativeForkSystemServerPre(JNIEnv *env) { - Service::instance()->InitService(env); - skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); - if (skip_) [[unlikely]] { - LOGW("skip system server due to symbol cache"); - } - setAllowUnload(skip_); - } - - void - Context::OnNativeForkSystemServerPost(JNIEnv *env) { - if (!skip_) { - auto *instance = Service::instance(); - auto system_server_binder = instance->RequestSystemServerBinder(env); - if (!system_server_binder) { - LOGF("Failed to get system server binder, system server initialization failed. "); - return; - } - - auto application_binder = instance->RequestApplicationBinderFromSystemServer(env, system_server_binder); - - // Call application_binder directly if application binder is available, - // or we proxy the request from system server binder - auto [dex_fd, size]= instance->RequestLSPDex(env, application_binder ? application_binder : system_server_binder); - LoadDex(env, dex_fd, size); - close(dex_fd); - instance->HookBridge(*this, env); - - if (application_binder) { - lsplant::InitInfo initInfo{ - .inline_hooker = [](auto t, auto r) { - void* bk = nullptr; - return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr; - }, - .inline_unhooker = [](auto t) { - return UnhookFunction(t) == RT_SUCCESS ; - }, - .art_symbol_resolver = [](auto symbol) { - return GetArt()->getSymbAddress(symbol); - }, - }; - InstallInlineHooks(initInfo); - Init(env, initInfo); - FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", application_binder); - GetArt(true); - } else { - LOGI("skipped system server"); - GetArt(true); - } - } - } - - void Context::OnNativeForkAndSpecializePre(JNIEnv *env, - jint uid, - jintArray &gids, - jstring nice_name, - jboolean is_child_zygote, - jstring app_data_dir) { - if (uid == kAidInjected) { - int array_size = gids ? env->GetArrayLength(gids) : 0; - auto region = std::make_unique(array_size + 1); - auto *new_gids = env->NewIntArray(array_size + 1); - if (gids) env->GetIntArrayRegion(gids, 0, array_size, region.get()); - region.get()[array_size] = kAidInet; - env->SetIntArrayRegion(new_gids, 0, array_size + 1, region.get()); - if (gids) env->SetIntArrayRegion(gids, 0, 1, region.get() + array_size); - gids = new_gids; - } - Service::instance()->InitService(env); - const auto app_id = uid % PER_USER_RANGE; - JUTFString process_name(env, nice_name); - skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); - if (!skip_ && !app_data_dir) { - LOGD("skip injecting into %s because it has no data dir", process_name.get()); - skip_ = true; - } - if (!skip_ && is_child_zygote) { - skip_ = true; - LOGD("skip injecting into %s because it's a child zygote", process_name.get()); - } - - if (!skip_ && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || - (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && - app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || - app_id == SHARED_RELRO_UID)) { - skip_ = true; - LOGI("skip injecting into %s because it's isolated", process_name.get()); - } - setAllowUnload(skip_); - } - - void - Context::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, - jstring app_data_dir) { - const JUTFString process_name(env, nice_name); - auto *instance = Service::instance(); - auto binder = skip_ ? ScopedLocalRef{env, nullptr} - : instance->RequestBinder(env, nice_name); - if (binder) { - lsplant::InitInfo initInfo{ - .inline_hooker = [](auto t, auto r) { - void* bk = nullptr; - return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr; - }, - .inline_unhooker = [](auto t) { - return UnhookFunction(t) == RT_SUCCESS; - }, - .art_symbol_resolver = [](auto symbol){ - return GetArt()->getSymbAddress(symbol); - }, - }; - InstallInlineHooks(initInfo); - auto [dex_fd, size] = instance->RequestLSPDex(env, binder); - LoadDex(env, dex_fd, size); - close(dex_fd); - Init(env, initInfo); - LOGD("Done prepare"); - FindAndCall(env, "forkAndSpecializePost", - "(Ljava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", - app_data_dir, nice_name, - binder); - LOGD("injected xposed into %s", process_name.get()); - setAllowUnload(false); - GetArt(true); - } else { - auto context = Context::ReleaseInstance(); - auto service = Service::ReleaseInstance(); - GetArt(true); - LOGD("skipped %s", process_name.get()); - setAllowUnload(true); - } - } - - void Context::setAllowUnload(bool unload) { - if (allowUnload) { - *allowUnload = unload ? 1 : 0; - } - } } // namespace lspd diff --git a/core/src/main/jni/template/config.cpp b/core/src/main/jni/template/config.cpp index d2fcef6d..3f6e9568 100644 --- a/core/src/main/jni/template/config.cpp +++ b/core/src/main/jni/template/config.cpp @@ -2,7 +2,5 @@ namespace lspd { const int versionCode = ${VERSION_CODE}; -const int apiVersion = ${API_VERSION}; const char* const versionName = "${VERSION_NAME}"; -const char* const moduleName = "${MODULE_NAME}"; } diff --git a/magisk-loader/.gitignore b/magisk-loader/.gitignore new file mode 100644 index 00000000..b5e4b9f3 --- /dev/null +++ b/magisk-loader/.gitignore @@ -0,0 +1,3 @@ +/build +/release +/.cxx diff --git a/magisk-loader/build.gradle.kts b/magisk-loader/build.gradle.kts new file mode 100644 index 00000000..7ec0fa55 --- /dev/null +++ b/magisk-loader/build.gradle.kts @@ -0,0 +1,321 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2021 - 2022 LSPosed Contributors + */ + +import org.apache.commons.codec.binary.Hex +import org.apache.tools.ant.filters.FixCrLfFilter +import org.apache.tools.ant.filters.ReplaceTokens +import java.io.ByteArrayOutputStream +import java.security.MessageDigest +import java.util.* + +plugins { + id("com.android.application") +} + +val moduleName = "LSPosed" +val moduleBaseId = "lsposed" +val authors = "LSPosed Developers" + +val riruModuleId = "lsposed" +val moduleMinRiruApiVersion = 25 +val moduleMinRiruVersionName = "25.0.1" +val moduleMaxRiruApiVersion = 25 + +val injectedPackageName: String by rootProject.extra +val injectedPackageUid: Int by rootProject.extra + +val defaultManagerPackageName: String by rootProject.extra +val apiCode: Int by rootProject.extra +val verCode: Int by rootProject.extra +val verName: String by rootProject.extra + +android { + flavorDimensions += "api" + + buildFeatures { + prefab = true + } + + defaultConfig { + applicationId = "org.lsposed.lspd" + multiDexEnabled = false + + buildConfigField("int", "API_CODE", "$apiCode") + buildConfigField( + "String", + "DEFAULT_MANAGER_PACKAGE_NAME", + """"$defaultManagerPackageName"""" + ) + buildConfigField("String", "MANAGER_INJECTED_PKG_NAME", """"$injectedPackageName"""") + buildConfigField("int", "MANAGER_INJECTED_UID", """$injectedPackageUid""") + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles("proguard-rules.pro") + } + } + + externalNativeBuild { + cmake { + path("src/main/jni/CMakeLists.txt") + } + } + + productFlavors { + all { + externalNativeBuild { + cmake { + arguments += "-DMODULE_NAME=${name.toLowerCase()}_$moduleBaseId" + arguments += "-DAPI=${name.toLowerCase()}" + } + } + buildConfigField("String", "API", """"$name"""") + } + + create("Riru") { + dimension = "api" + externalNativeBuild { + cmake { + arguments += "-DAPI_VERSION=$moduleMaxRiruApiVersion" + } + } + } + + create("Zygisk") { + dimension = "api" + externalNativeBuild { + cmake { + arguments += "-DAPI_VERSION=1" + } + } + } + } +} + +dependencies { + compileOnly("androidx.annotation:annotation:1.3.0") + compileOnly(projects.hiddenapi.stubs) + implementation(projects.core) + implementation(projects.hiddenapi.bridge) + implementation(projects.services.managerService) + implementation(projects.services.daemonService) +} + +val zipAll = task("zipAll") { + +} + +val apkDir: String + get() = if (rootProject.extra.properties["android.injected.invoked.from.ide"] == "true") "intermediates" else "outputs" + +fun afterEval() = android.applicationVariants.forEach { variant -> + val variantCapped = variant.name.capitalize(Locale.ROOT) + val variantLowered = variant.name.toLowerCase(Locale.ROOT) + val buildTypeCapped = variant.buildType.name.capitalize(Locale.ROOT) + val buildTypeLowered = variant.buildType.name.toLowerCase(Locale.ROOT) + val flavorCapped = variant.flavorName!!.capitalize(Locale.ROOT) + val flavorLowered = variant.flavorName!!.toLowerCase(Locale.ROOT) + + val magiskDir = "$buildDir/magisk/$variantLowered" + + val moduleId = "${flavorLowered}_$moduleBaseId" + val zipFileName = "$moduleName-v$verName-$verCode-${flavorLowered}-$buildTypeLowered.zip" + + val prepareMagiskFilesTask = task("prepareMagiskFiles$variantCapped") { + dependsOn( + "assemble$variantCapped", + ":app:package$buildTypeCapped", + ":daemon:package$buildTypeCapped" + ) + into(magiskDir) + from("${rootProject.projectDir}/README.md") + from("$projectDir/magisk_module") { + exclude("riru.sh", "module.prop", "customize.sh", "sepolicy.rule", "post-fs-data.sh") + } + from("$projectDir/magisk_module") { + include("module.prop") + expand( + "moduleId" to moduleId, + "versionName" to "v$verName", + "versionCode" to verCode, + "authorList" to authors, + "updateJson" to "https://lsposed.github.io/LSPosed/release/${flavorLowered}.json", + "requirement" to when (flavorLowered) { + "riru" -> "Requires Riru $moduleMinRiruVersionName or above installed" + "zygisk" -> "Requires Magisk 24.0+ and Zygisk enabled" + else -> "No further requirements" + }, + "api" to flavorCapped + ) + filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) + } + from("$projectDir/magisk_module") { + include("customize.sh", "post-fs-data.sh") + val tokens = mapOf("FLAVOR" to flavorLowered) + filter("tokens" to tokens) + filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) + } + if (flavorLowered == "riru") { + from("${projectDir}/magisk_module") { + include("riru.sh", "sepolicy.rule") + val tokens = mapOf( + "RIRU_MODULE_LIB_NAME" to "lspd", + "RIRU_MODULE_API_VERSION" to moduleMaxRiruApiVersion.toString(), + "RIRU_MODULE_MIN_API_VERSION" to moduleMinRiruApiVersion.toString(), + "RIRU_MODULE_MIN_RIRU_VERSION_NAME" to moduleMinRiruVersionName, + "RIRU_MODULE_DEBUG" to if (buildTypeLowered == "debug") "true" else "false", + ) + filter("tokens" to tokens) + filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) + } + } + from(project(":app").tasks.getByName("package$buildTypeCapped").outputs) { + include("*.apk") + rename(".*\\.apk", "manager.apk") + } + from(project(":daemon").tasks.getByName("package$buildTypeCapped").outputs) { + include("*.apk") + rename(".*\\.apk", "daemon.apk") + } + into("lib") { + from("${buildDir}/intermediates/cmake/$variantCapped/obj") { + include("**/liblspd.so") + } + from("${project(":daemon").buildDir}/intermediates/cmake/$buildTypeLowered/obj") { + include("**/libdaemon.so") + } + } + val dexOutPath = if (buildTypeLowered == "release") + "$buildDir/intermediates/dex/$variantCapped/minify${variantCapped}WithR8" else + "$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped" + into("framework") { + from(dexOutPath) + rename("classes.dex", "lspd.dex") + } + doLast { + fileTree(magiskDir).visit { + if (isDirectory) return@visit + val md = MessageDigest.getInstance("SHA-256") + file.forEachBlock(4096) { bytes, size -> + md.update(bytes, 0, size) + } + file(file.path + ".sha256").writeText(Hex.encodeHexString(md.digest())) + } + } + } + + val zipTask = task("zip${variantCapped}") { + dependsOn(prepareMagiskFilesTask) + archiveFileName.set(zipFileName) + destinationDirectory.set(file("$projectDir/release")) + from(magiskDir) + } + + zipAll.dependsOn(zipTask) + + val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath + val pushTask = task("push${variantCapped}") { + dependsOn(zipTask) + workingDir("${projectDir}/release") + commandLine(adb, "push", zipFileName, "/data/local/tmp/") + } + val flashTask = task("flash${variantCapped}") { + dependsOn(pushTask) + commandLine( + adb, "shell", "su", "-c", + "magisk --install-module /data/local/tmp/${zipFileName}" + ) + } + task("flashAndReboot${variantCapped}") { + dependsOn(flashTask) + commandLine(adb, "shell", "reboot") + } +} + +afterEvaluate { + afterEval() +} + +val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath +val killLspd = task("killLspd") { + commandLine(adb, "shell", "su", "-c", "killall", "lspd") + isIgnoreExitValue = true +} +val pushDaemon = task("pushDaemon") { + dependsOn(":daemon:assembleDebug") + workingDir("${project(":daemon").buildDir}/$apkDir/apk/debug") + commandLine(adb, "push", "daemon-debug.apk", "/data/local/tmp/daemon.apk") +} +val pushDaemonNative = task("pushDaemonNative") { + dependsOn(":daemon:assembleDebug") + doFirst { + val abi: String = ByteArrayOutputStream().use { outputStream -> + exec { + commandLine(adb, "shell", "getprop", "ro.product.cpu.abi") + standardOutput = outputStream + } + outputStream.toString().trim() + } + workingDir("${project(":daemon").buildDir}/intermediates/ndkBuild/debug/obj/local/$abi") + } + commandLine(adb, "push", "libdaemon.so", "/data/local/tmp/libdaemon.so") +} +val reRunDaemon = task("reRunDaemon") { + dependsOn(pushDaemon, pushDaemonNative, killLspd) + // tricky to pass a minus number to avoid the injection warning + commandLine( + adb, + "shell", + "ASH_STANDALONE=1", + "su", + "-pc", + "/data/adb/magisk/busybox sh /data/adb/modules/*_lsposed/service.sh --system-server-max-retry=-1&" + ) + isIgnoreExitValue = true +} +val tmpApk = "/data/local/tmp/lsp.apk" +val pushApk = task("pushApk") { + dependsOn(":app:assembleDebug") + workingDir("${project(":app").buildDir}/$apkDir/apk/debug") + commandLine(adb, "push", "app-debug.apk", tmpApk) +} +val openApp = task("openApp") { + commandLine( + adb, + "shell", + "am", + "start", + "-a", + "android.intent.action.MAIN", + "-c", + "org.lsposed.manager.LAUNCH_MANAGER", + "com.android.shell/.BugreportWarningActivity" + ) +} +task("reRunApp") { + dependsOn(pushApk) + commandLine(adb, "shell", "su", "-c", "mv -f $tmpApk /data/adb/lspd/manager.apk") + isIgnoreExitValue = true + finalizedBy(reRunDaemon) +} + +evaluationDependsOn(":app") +evaluationDependsOn(":daemon") diff --git a/core/magisk_module/META-INF/com/google/android/update-binary b/magisk-loader/magisk_module/META-INF/com/google/android/update-binary similarity index 100% rename from core/magisk_module/META-INF/com/google/android/update-binary rename to magisk-loader/magisk_module/META-INF/com/google/android/update-binary diff --git a/core/magisk_module/META-INF/com/google/android/updater-script b/magisk-loader/magisk_module/META-INF/com/google/android/updater-script similarity index 100% rename from core/magisk_module/META-INF/com/google/android/updater-script rename to magisk-loader/magisk_module/META-INF/com/google/android/updater-script diff --git a/core/magisk_module/customize.sh b/magisk-loader/magisk_module/customize.sh similarity index 100% rename from core/magisk_module/customize.sh rename to magisk-loader/magisk_module/customize.sh diff --git a/core/magisk_module/daemon b/magisk-loader/magisk_module/daemon similarity index 100% rename from core/magisk_module/daemon rename to magisk-loader/magisk_module/daemon diff --git a/core/magisk_module/module.prop b/magisk-loader/magisk_module/module.prop similarity index 100% rename from core/magisk_module/module.prop rename to magisk-loader/magisk_module/module.prop diff --git a/core/magisk_module/post-fs-data.sh b/magisk-loader/magisk_module/post-fs-data.sh similarity index 100% rename from core/magisk_module/post-fs-data.sh rename to magisk-loader/magisk_module/post-fs-data.sh diff --git a/core/magisk_module/riru.sh b/magisk-loader/magisk_module/riru.sh similarity index 100% rename from core/magisk_module/riru.sh rename to magisk-loader/magisk_module/riru.sh diff --git a/core/magisk_module/sepolicy.rule b/magisk-loader/magisk_module/sepolicy.rule similarity index 100% rename from core/magisk_module/sepolicy.rule rename to magisk-loader/magisk_module/sepolicy.rule diff --git a/core/magisk_module/service.sh b/magisk-loader/magisk_module/service.sh similarity index 100% rename from core/magisk_module/service.sh rename to magisk-loader/magisk_module/service.sh diff --git a/core/magisk_module/system.prop b/magisk-loader/magisk_module/system.prop similarity index 100% rename from core/magisk_module/system.prop rename to magisk-loader/magisk_module/system.prop diff --git a/core/magisk_module/uninstall.sh b/magisk-loader/magisk_module/uninstall.sh similarity index 100% rename from core/magisk_module/uninstall.sh rename to magisk-loader/magisk_module/uninstall.sh diff --git a/core/magisk_module/util_functions.sh b/magisk-loader/magisk_module/util_functions.sh similarity index 100% rename from core/magisk_module/util_functions.sh rename to magisk-loader/magisk_module/util_functions.sh diff --git a/core/magisk_module/verify.sh b/magisk-loader/magisk_module/verify.sh similarity index 100% rename from core/magisk_module/verify.sh rename to magisk-loader/magisk_module/verify.sh diff --git a/magisk-loader/proguard-rules.pro b/magisk-loader/proguard-rules.pro new file mode 100644 index 00000000..bb5a17a5 --- /dev/null +++ b/magisk-loader/proguard-rules.pro @@ -0,0 +1,17 @@ +-keepclasseswithmembers class org.lsposed.lspd.core.Main { + public static void forkCommon(boolean, java.lang.String, android.os.IBinder); +} +-keepclasseswithmembers,includedescriptorclasses class * { + native ; +} +-keepclasseswithmembers class org.lsposed.lspd.service.BridgeService { + public static boolean *(android.os.IBinder, int, long, long, int); +} + +-assumenosideeffects class android.util.Log { + public static *** v(...); + public static *** d(...); +} +-repackageclasses +-allowaccessmodification +-dontwarn org.slf4j.impl.StaticLoggerBinder diff --git a/core/src/main/AndroidManifest.xml b/magisk-loader/src/main/AndroidManifest.xml similarity index 89% rename from core/src/main/AndroidManifest.xml rename to magisk-loader/src/main/AndroidManifest.xml index 1f659bee..c04c0dc9 100644 --- a/core/src/main/AndroidManifest.xml +++ b/magisk-loader/src/main/AndroidManifest.xml @@ -14,8 +14,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with LSPosed. If not, see . ~ - ~ Copyright (C) 2020 EdXposed Contributors - ~ Copyright (C) 2021 LSPosed Contributors + ~ Copyright (C) 2022 LSPosed Contributors --> diff --git a/magisk-loader/src/main/java/org/lsposed/lspd/core/Main.java b/magisk-loader/src/main/java/org/lsposed/lspd/core/Main.java new file mode 100644 index 00000000..1ba77634 --- /dev/null +++ b/magisk-loader/src/main/java/org/lsposed/lspd/core/Main.java @@ -0,0 +1,41 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2022 LSPosed Contributors + */ + +package org.lsposed.lspd.core; + +import android.os.IBinder; + +import org.lsposed.lspd.BuildConfig; +import org.lsposed.lspd.config.LSPApplicationServiceClient; +import org.lsposed.lspd.util.ParasiticManagerHooker; +import org.lsposed.lspd.util.Utils; + +public class Main { + + public static void forkCommon(boolean isSystem, String niceName, IBinder binder) { + LSPApplicationServiceClient.Init(binder, niceName); + Startup.initXposed(isSystem); + if ((niceName.equals(BuildConfig.MANAGER_INJECTED_PKG_NAME) || niceName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME)) + && ParasiticManagerHooker.start()) { + Utils.logI("Loaded manager, skipping next steps"); + return; + } + Startup.bootstrapXposed(niceName); + } +} diff --git a/core/src/main/java/org/lsposed/lspd/service/ActivityController.java b/magisk-loader/src/main/java/org/lsposed/lspd/service/ActivityController.java similarity index 100% rename from core/src/main/java/org/lsposed/lspd/service/ActivityController.java rename to magisk-loader/src/main/java/org/lsposed/lspd/service/ActivityController.java diff --git a/core/src/main/java/org/lsposed/lspd/service/BridgeService.java b/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java similarity index 99% rename from core/src/main/java/org/lsposed/lspd/service/BridgeService.java rename to magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java index 10ccbb40..71fcd4e1 100644 --- a/core/src/main/java/org/lsposed/lspd/service/BridgeService.java +++ b/magisk-loader/src/main/java/org/lsposed/lspd/service/BridgeService.java @@ -28,7 +28,6 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; -import android.os.Process; import android.os.RemoteException; import android.util.Log; diff --git a/core/src/main/java/org/lsposed/lspd/service/ParcelUtils.java b/magisk-loader/src/main/java/org/lsposed/lspd/service/ParcelUtils.java similarity index 100% rename from core/src/main/java/org/lsposed/lspd/service/ParcelUtils.java rename to magisk-loader/src/main/java/org/lsposed/lspd/service/ParcelUtils.java diff --git a/core/src/main/java/org/lsposed/lspd/util/ParasiticManagerHooker.java b/magisk-loader/src/main/java/org/lsposed/lspd/util/ParasiticManagerHooker.java similarity index 100% rename from core/src/main/java/org/lsposed/lspd/util/ParasiticManagerHooker.java rename to magisk-loader/src/main/java/org/lsposed/lspd/util/ParasiticManagerHooker.java diff --git a/magisk-loader/src/main/jni/CMakeLists.txt b/magisk-loader/src/main/jni/CMakeLists.txt new file mode 100644 index 00000000..4dfc3f69 --- /dev/null +++ b/magisk-loader/src/main/jni/CMakeLists.txt @@ -0,0 +1,32 @@ +project(lspd) +cmake_minimum_required(VERSION 3.4.1) + +add_subdirectory(${CORE_ROOT} core) + +configure_file(template/loader.cpp src/loader.cpp) + +aux_source_directory(src SRC_LIST) +if (${API} STREQUAL "riru") + set(SRC_LIST ${SRC_LIST} api/riru_main.cpp) +elseif (${API} STREQUAL "zygisk") + set(SRC_LIST ${SRC_LIST} api/zygisk_main.cpp) +endif() + +add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${CMAKE_CURRENT_BINARY_DIR}/src/loader.cpp) + +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PRIVATE src) + +target_link_libraries(${PROJECT_NAME} core log) + +if (DEFINED DEBUG_SYMBOLS_PATH) + set(DEBUG_SYMBOLS_PATH ${DEBUG_SYMBOLS_PATH}/${API}) + message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI} + COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $ + ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + COMMAND ${CMAKE_STRIP} --strip-all $ + COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug + $) +endif() diff --git a/core/src/main/jni/api/riru.h b/magisk-loader/src/main/jni/api/riru.h similarity index 100% rename from core/src/main/jni/api/riru.h rename to magisk-loader/src/main/jni/api/riru.h diff --git a/core/src/main/jni/api/riru_main.cpp b/magisk-loader/src/main/jni/api/riru_main.cpp similarity index 86% rename from core/src/main/jni/api/riru_main.cpp rename to magisk-loader/src/main/jni/api/riru_main.cpp index 22c36708..80bb0b17 100644 --- a/core/src/main/jni/api/riru_main.cpp +++ b/magisk-loader/src/main/jni/api/riru_main.cpp @@ -23,8 +23,8 @@ #include #include #include "logging.h" -#include "config.h" -#include "context.h" +#include "loader.h" +#include "magisk_loader.h" #include "symbol_cache.h" #define RIRU_MODULE @@ -36,13 +36,12 @@ namespace lspd { std::string magiskPath; jstring nice_name = nullptr; - jstring app_data_dir = nullptr; void onModuleLoaded() { LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode); InitSymbolCache(nullptr); - Context::GetInstance()->Init(); + MagiskLoader::Init(); } void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *, @@ -57,28 +56,27 @@ namespace lspd { jboolean *, jboolean *) { nice_name = *_nice_name; - app_data_dir = *_app_data_dir; - Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids, + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids, nice_name, *start_child_zygote, - app_data_dir); + *_app_data_dir); } void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) { if (res == 0) - Context::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name, app_data_dir); + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name); } void nativeForkSystemServerPre(JNIEnv *env, jclass, uid_t *, gid_t *, jintArray *, jint *, jobjectArray *, jlong *, jlong *) { - Context::GetInstance()->OnNativeForkSystemServerPre(env); + MagiskLoader::GetInstance()->OnNativeForkSystemServerPre(env); } void nativeForkSystemServerPost(JNIEnv *env, jclass, jint res) { if (res == 0) - Context::GetInstance()->OnNativeForkSystemServerPost(env); + MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env); } /* method added in Android Q */ @@ -93,15 +91,14 @@ namespace lspd { jboolean *, jboolean *) { nice_name = *_nice_name; - app_data_dir = *_app_data_dir; - Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids, + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids, nice_name, *start_child_zygote, - app_data_dir); + *_app_data_dir); } void specializeAppProcessPost(JNIEnv *env, jclass) { - Context::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name, app_data_dir); + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name); } } diff --git a/core/src/main/jni/api/zygisk.h b/magisk-loader/src/main/jni/api/zygisk.h similarity index 100% rename from core/src/main/jni/api/zygisk.h rename to magisk-loader/src/main/jni/api/zygisk.h diff --git a/core/src/main/jni/api/zygisk_main.cpp b/magisk-loader/src/main/jni/api/zygisk_main.cpp similarity index 96% rename from core/src/main/jni/api/zygisk_main.cpp rename to magisk-loader/src/main/jni/api/zygisk_main.cpp index 3914d2ce..05b3537e 100644 --- a/core/src/main/jni/api/zygisk_main.cpp +++ b/magisk-loader/src/main/jni/api/zygisk_main.cpp @@ -24,8 +24,8 @@ #include "zygisk.h" #include "logging.h" -#include "context.h" -#include "config.h" +#include "loader.h" +#include "magisk_loader.h" #include "symbol_cache.h" namespace lspd { @@ -281,7 +281,7 @@ namespace lspd { void onLoad(zygisk::Api *api, JNIEnv *env) override { env_ = env; api_ = api; - Context::GetInstance()->Init(); + MagiskLoader::Init(); auto companion = api->connectCompanion(); if (companion == -1) { @@ -308,19 +308,18 @@ namespace lspd { } void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - Context::GetInstance()->OnNativeForkAndSpecializePre( + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre( env_, args->uid, args->gids, args->nice_name, args->is_child_zygote ? *args->is_child_zygote : false, args->app_data_dir); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - Context::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name, - args->app_data_dir); + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name); if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); } void preServerSpecialize([[maybe_unused]] zygisk::ServerSpecializeArgs *args) override { - Context::GetInstance()->OnNativeForkSystemServerPre(env_); + MagiskLoader::GetInstance()->OnNativeForkSystemServerPre(env_); } void postServerSpecialize([[maybe_unused]] const zygisk::ServerSpecializeArgs *args) override { @@ -333,7 +332,7 @@ namespace lspd { env_->DeleteLocalRef(name); env_->DeleteLocalRef(process); } - Context::GetInstance()->OnNativeForkSystemServerPost(env_); + MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env_); if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); } }; diff --git a/magisk-loader/src/main/jni/include/loader.h b/magisk-loader/src/main/jni/include/loader.h new file mode 100644 index 00000000..90838ee3 --- /dev/null +++ b/magisk-loader/src/main/jni/include/loader.h @@ -0,0 +1,34 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2022 LSPosed Contributors + */ + +// +// Created by Nullptr on 2022/3/16. +// + +#pragma once + +#include "config.h" + +namespace lspd { + inline static constexpr auto kEntryClassName = "org.lsposed.lspd.core.Main"_tstr; + inline static constexpr auto kBridgeServiceClassName = "org.lsposed.lspd.service.BridgeService"_tstr; + + extern const int apiVersion; + extern const char* const moduleName; +} diff --git a/magisk-loader/src/main/jni/src/magisk_loader.cpp b/magisk-loader/src/main/jni/src/magisk_loader.cpp new file mode 100644 index 00000000..8801a32f --- /dev/null +++ b/magisk-loader/src/main/jni/src/magisk_loader.cpp @@ -0,0 +1,224 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2020 EdXposed Contributors + * Copyright (C) 2021 - 2022 LSPosed Contributors + */ + +#include +#include +#include + +#include "elf_util.h" +#include "loader.h" +#include "magisk_loader.h" +#include "native_hook.h" +#include "native_util.h" +#include "service.h" +#include "symbol_cache.h" +#include "utils/jni_helper.hpp" + +using namespace lsplant; + +static_assert(FS_IOC_SETFLAGS == LP_SELECT(0x40046602, 0x40086602)); + +namespace lspd { + extern int *allowUnload; + + constexpr int FIRST_ISOLATED_UID = 99000; + constexpr int LAST_ISOLATED_UID = 99999; + constexpr int FIRST_APP_ZYGOTE_ISOLATED_UID = 90000; + constexpr int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; + constexpr int SHARED_RELRO_UID = 1037; + constexpr int PER_USER_RANGE = 100000; + + static constexpr uid_t kAidInjected = INJECTED_AID; + static constexpr uid_t kAidInet = 3003; + + void MagiskLoader::LoadDex(JNIEnv *env, PreloadedDex &&dex) { + auto classloader = JNI_FindClass(env, "java/lang/ClassLoader"); + auto getsyscl_mid = JNI_GetStaticMethodID( + env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); + auto sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid); + if (!sys_classloader) [[unlikely]] { + LOGE("getSystemClassLoader failed!!!"); + return; + } + auto in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader"); + auto initMid = JNI_GetMethodID(env, in_memory_classloader, "", + "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"); + auto byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer"); + auto dex_buffer = env->NewDirectByteBuffer(dex.data(), dex.size()); + if (auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid, + dex_buffer, sys_classloader)) { + inject_class_loader_ = JNI_NewGlobalRef(env, my_cl); + } else { + LOGE("InMemoryDexClassLoader creation failed!!!"); + return; + } + + env->DeleteLocalRef(dex_buffer); + } + + void MagiskLoader::SetupEntryClass(JNIEnv *env) { + if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), + kEntryClassName)) { + entry_class_ = JNI_NewGlobalRef(env, entry_class); + } + } + + void + MagiskLoader::OnNativeForkSystemServerPre(JNIEnv *env) { + Service::instance()->InitService(env); + skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); + if (skip_) [[unlikely]] { + LOGW("skip system server due to symbol cache"); + } + setAllowUnload(skip_); + } + + void + MagiskLoader::OnNativeForkSystemServerPost(JNIEnv *env) { + if (!skip_) { + auto *instance = Service::instance(); + auto system_server_binder = instance->RequestSystemServerBinder(env); + if (!system_server_binder) { + LOGF("Failed to get system server binder, system server initialization failed. "); + return; + } + + auto application_binder = instance->RequestApplicationBinderFromSystemServer(env, system_server_binder); + + // Call application_binder directly if application binder is available, + // or we proxy the request from system server binder + auto [dex_fd, size]= instance->RequestLSPDex(env, application_binder ? application_binder : system_server_binder); + LoadDex(env, PreloadedDex(dex_fd, size)); + close(dex_fd); + instance->HookBridge(*this, env); + + if (application_binder) { + lsplant::InitInfo initInfo{ + .inline_hooker = [](auto t, auto r) { + void* bk = nullptr; + return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr; + }, + .inline_unhooker = [](auto t) { + return UnhookFunction(t) == RT_SUCCESS ; + }, + .art_symbol_resolver = [](auto symbol) { + return GetArt()->getSymbAddress(symbol); + }, + }; + InstallInlineHooks(initInfo); + InitHooks(env, initInfo); + SetupEntryClass(env); + FindAndCall(env, "forkCommon", + "(ZLjava/lang/String;Landroid/os/IBinder;)V", + JNI_TRUE, JNI_NewStringUTF(env, "android"), application_binder); + GetArt(true); + } else { + LOGI("skipped system server"); + GetArt(true); + } + } + } + + void MagiskLoader::OnNativeForkAndSpecializePre(JNIEnv *env, + jint uid, + jintArray &gids, + jstring nice_name, + jboolean is_child_zygote, + jstring app_data_dir) { + if (uid == kAidInjected) { + int array_size = gids ? env->GetArrayLength(gids) : 0; + auto region = std::make_unique(array_size + 1); + auto *new_gids = env->NewIntArray(array_size + 1); + if (gids) env->GetIntArrayRegion(gids, 0, array_size, region.get()); + region.get()[array_size] = kAidInet; + env->SetIntArrayRegion(new_gids, 0, array_size + 1, region.get()); + if (gids) env->SetIntArrayRegion(gids, 0, 1, region.get() + array_size); + gids = new_gids; + } + Service::instance()->InitService(env); + const auto app_id = uid % PER_USER_RANGE; + JUTFString process_name(env, nice_name); + skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); + if (!skip_ && !app_data_dir) { + LOGD("skip injecting into %s because it has no data dir", process_name.get()); + skip_ = true; + } + if (!skip_ && is_child_zygote) { + skip_ = true; + LOGD("skip injecting into %s because it's a child zygote", process_name.get()); + } + + if (!skip_ && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) || + (app_id >= FIRST_APP_ZYGOTE_ISOLATED_UID && + app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) || + app_id == SHARED_RELRO_UID)) { + skip_ = true; + LOGI("skip injecting into %s because it's isolated", process_name.get()); + } + setAllowUnload(skip_); + } + + void + MagiskLoader::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name) { + const JUTFString process_name(env, nice_name); + auto *instance = Service::instance(); + auto binder = skip_ ? ScopedLocalRef{env, nullptr} + : instance->RequestBinder(env, nice_name); + if (binder) { + lsplant::InitInfo initInfo{ + .inline_hooker = [](auto t, auto r) { + void* bk = nullptr; + return HookFunction(t, r, &bk) == RS_SUCCESS ? bk : nullptr; + }, + .inline_unhooker = [](auto t) { + return UnhookFunction(t) == RT_SUCCESS; + }, + .art_symbol_resolver = [](auto symbol){ + return GetArt()->getSymbAddress(symbol); + }, + }; + InstallInlineHooks(initInfo); + auto [dex_fd, size] = instance->RequestLSPDex(env, binder); + LoadDex(env, PreloadedDex(dex_fd, size)); + close(dex_fd); + InitHooks(env, initInfo); + SetupEntryClass(env); + LOGD("Done prepare"); + FindAndCall(env, "forkCommon", + "(ZLjava/lang/String;Landroid/os/IBinder;)V", + JNI_FALSE, nice_name, binder); + LOGD("injected xposed into %s", process_name.get()); + setAllowUnload(false); + GetArt(true); + } else { + auto context = Context::ReleaseInstance(); + auto service = Service::ReleaseInstance(); + GetArt(true); + LOGD("skipped %s", process_name.get()); + setAllowUnload(true); + } + } + + void MagiskLoader::setAllowUnload(bool unload) { + if (allowUnload) { + *allowUnload = unload ? 1 : 0; + } + } +} // namespace lspd diff --git a/magisk-loader/src/main/jni/src/magisk_loader.h b/magisk-loader/src/main/jni/src/magisk_loader.h new file mode 100644 index 00000000..f418ebcf --- /dev/null +++ b/magisk-loader/src/main/jni/src/magisk_loader.h @@ -0,0 +1,58 @@ +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2022 LSPosed Contributors + */ + +// +// Created by Nullptr on 2022/3/16. +// + +#pragma once + +#include "context.h" + +namespace lspd { + class MagiskLoader : public Context { + public: + inline static void Init() { + instance_ = std::make_unique(); + } + + inline static MagiskLoader *GetInstance() { + return static_cast(instance_.get()); + } + + void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring nice_name, + jboolean is_child_zygote, jstring app_data_dir); + + void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name); + + void OnNativeForkSystemServerPost(JNIEnv *env); + + void OnNativeForkSystemServerPre(JNIEnv *env); + + protected: + void LoadDex(JNIEnv *env, PreloadedDex &&dex) override; + + void SetupEntryClass(JNIEnv *env) override; + + private: + bool skip_ = false; + + static void setAllowUnload(bool unload); + }; +} // namespace lspd diff --git a/core/src/main/jni/src/service.cpp b/magisk-loader/src/main/jni/src/service.cpp similarity index 99% rename from core/src/main/jni/src/service.cpp rename to magisk-loader/src/main/jni/src/service.cpp index 6e0c2ca8..d828b2b8 100644 --- a/core/src/main/jni/src/service.cpp +++ b/magisk-loader/src/main/jni/src/service.cpp @@ -23,7 +23,7 @@ #include #include -#include "config.h" +#include "loader.h" #include "service.h" #include "context.h" #include "utils/jni_helper.hpp" diff --git a/core/src/main/jni/src/service.h b/magisk-loader/src/main/jni/src/service.h similarity index 100% rename from core/src/main/jni/src/service.h rename to magisk-loader/src/main/jni/src/service.h diff --git a/magisk-loader/src/main/jni/template/loader.cpp b/magisk-loader/src/main/jni/template/loader.cpp new file mode 100644 index 00000000..f4394c43 --- /dev/null +++ b/magisk-loader/src/main/jni/template/loader.cpp @@ -0,0 +1,6 @@ +#include "loader.h" + +namespace lspd { +const int apiVersion = ${API_VERSION}; +const char* const moduleName = "${MODULE_NAME}"; +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5711dd1f..ad8ab6d4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,6 +31,7 @@ include( ":daemon", ":hiddenapi:stubs", ":hiddenapi:bridge", + ":magisk-loader", ":services:manager-service", ":services:daemon-service", ":services:xposed-service:interface",