From 7a7c0c56e3d905825213fc0c1be9c43795ee434a Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 17 Oct 2021 03:44:26 +0800 Subject: [PATCH] [core] Support zygisk (#1228) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 南宫雪珊 Co-authored-by: tehcneko <88844448+tehcneko@users.noreply.github.com> --- .github/workflows/core.yml | 45 ++- .../org/lsposed/manager/ConfigManager.java | 10 + .../lsposed/manager/adapters/AppHelper.java | 8 + .../manager/adapters/ScopeAdapter.java | 33 +- app/src/main/res/menu/menu_app_list.xml | 6 + app/src/main/res/values/strings.xml | 3 + core/.gitignore | 2 +- core/build.gradle.kts | 160 ++++++---- core/magisk_module/customize.sh | 117 +++++--- core/magisk_module/module.prop | 4 +- core/magisk_module/sepolicy.rule | 3 + core/src/main/cpp/main/Android.mk | 21 +- .../main/{src/main.cpp => api/riru_main.cpp} | 70 ++--- core/src/main/cpp/main/api/zygisk_main.cpp | 242 +++++++++++++++ core/src/main/cpp/main/include/config.h | 2 + core/src/main/cpp/main/src/context.cpp | 80 +++-- core/src/main/cpp/main/src/context.h | 59 +++- core/src/main/cpp/main/src/jni/zygisk.h | 282 ++++++++++++++++++ core/src/main/cpp/main/template/config.cpp | 6 - .../lspd/service/ConfigFileManager.java | 1 + .../lsposed/lspd/service/ConfigManager.java | 23 ++ .../lspd/service/LSPManagerService.java | 5 + .../org/lsposed/lspd/ILSPManagerService.aidl | 2 + 23 files changed, 978 insertions(+), 206 deletions(-) rename core/src/main/cpp/main/{src/main.cpp => api/riru_main.cpp} (71%) create mode 100644 core/src/main/cpp/main/api/zygisk_main.cpp create mode 100644 core/src/main/cpp/main/src/jni/zygisk.h delete mode 100644 core/src/main/cpp/main/template/config.cpp diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml index 62367c67..66487b68 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -71,27 +71,40 @@ jobs: echo 'org.gradle.vfs.watch=true' >> gradle.properties echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties echo 'android.native.buildOutput=verbose' >> gradle.properties - ./gradlew zipRelease zipDebug + ./gradlew zipAll ccache -s - name: Prepare artifact if: success() id: prepareArtifact run: | - releaseName=`ls core/release/LSPosed-v*-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=releaseName::$releaseName" - debugName=`ls core/release/LSPosed-v*-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=debugName::$debugName" - unzip core/release/LSPosed-v*-release.zip -d LSPosed-release - unzip core/release/LSPosed-v*-debug.zip -d LSPosed-debug - - name: Upload release + riruReleaseName=`ls core/release/LSPosed-riru-v*-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruReleaseName::$riruReleaseName" + riruDebugName=`ls core/release/LSPosed-riru-v*-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruDebugName::$riruDebugName" + zygiskReleaseName=`ls core/release/LSPosed-zygisk-v*-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskReleaseName::$zygiskReleaseName" + zygiskDebugName=`ls core/release/LSPosed-zygisk-v*-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=zygiskDebugName::$zygiskDebugName" + unzip core/release/LSPosed-riru-v*-release.zip -d LSPosed-riru-release + unzip core/release/LSPosed-riru-v*-debug.zip -d LSPosed-riru-debug + unzip core/release/LSPosed-zygisk-v*-release.zip -d LSPosed-zygisk-release + unzip core/release/LSPosed-zygisk-v*-debug.zip -d LSPosed-zygisk-debug + - name: Upload riru release uses: actions/upload-artifact@v2 with: - name: ${{ steps.prepareArtifact.outputs.releaseName }} - path: './LSPosed-release/*' - - name: Upload debug -# if: ${{ github.event_name == 'pull_request' && success() }} + name: ${{ steps.prepareArtifact.outputs.riruReleaseName }} + path: './LSPosed-riru-release/*' + - name: Upload riru debug uses: actions/upload-artifact@v2 with: - name: ${{ steps.prepareArtifact.outputs.debugName }} - path: './LSPosed-debug/*' + name: ${{ steps.prepareArtifact.outputs.riruDebugName }} + path: './LSPosed-riru-debug/*' + - name: Upload zygisk release + uses: actions/upload-artifact@v2 + with: + name: ${{ steps.prepareArtifact.outputs.zygiskReleaseName }} + path: './LSPosed-zygisk-release/*' + - name: Upload zygisk debug + uses: actions/upload-artifact@v2 + with: + name: ${{ steps.prepareArtifact.outputs.zygiskDebugName }} + path: './LSPosed-zygisk-debug/*' - name: Upload mappings uses: actions/upload-artifact@v2 with: @@ -108,7 +121,9 @@ jobs: COMMIT_URL: ${{ github.event.head_commit.url }} run: | OUTPUT="core/release/" - export release=$(find $OUTPUT -name "LSPosed-v*-release.zip") - export debug=$(find $OUTPUT -name "LSPosed-v*-debug.zip") + export riruRelease=$(find $OUTPUT -name "LSPosed-riru-v*-release.zip") + export riruDebug=$(find $OUTPUT -name "LSPosed-riru-v*-debug.zip") + export zygiskRelease=$(find $OUTPUT -name "LSPosed-zygisk-v*-release.zip") + export zygiskDebug=$(find $OUTPUT -name "LSPosed-zygisk-v*-debug.zip") ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'` - curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${CHANNEL_ID}&media=%5B%7B%22type%22:%22document%22,%20%22media%22:%22attach://release%22%7D,%7B%22type%22:%22document%22,%20%22media%22:%22attach://debug%22,%22caption%22:${ESCAPED}%7D%5D" -F release="@$release" -F debug="@$debug" + curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${CHANNEL_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskDebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruDebug%22%2C%22caption%22:${ESCAPED}%7D%5D" -F riruRelease="@$riruRelease" -F riruDebug="@$riruDebug" diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index 8822e092..82d5b565 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -342,6 +342,16 @@ public class ConfigManager { } } + public static List getDenyListPackages() { + List list = new ArrayList<>(); + try { + list.addAll(LSPManagerServiceHolder.getService().getDenyListPackages()); + } catch (RemoteException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + } + return list; + } + public static void flashZip(String zipPath, ParcelFileDescriptor outputStream) { try { LSPManagerServiceHolder.getService().flashZip(zipPath, outputStream); diff --git a/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java b/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java index da423c9c..b39e5959 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java +++ b/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java @@ -39,6 +39,7 @@ public class AppHelper { public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS"; public static final int FLAG_SHOW_FOR_ALL_USERS = 0x0400; + private static List denyList; private static List appList; public static Intent getSettingsIntent(String packageName, int userId) { @@ -137,4 +138,11 @@ public class AppHelper { } return appList; } + + public static List getDenyList(boolean force) { + if (denyList == null || force) { + denyList = ConfigManager.getDenyListPackages(); + } + return denyList; + } } diff --git a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java index e512f475..ac956ef6 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java @@ -105,6 +105,7 @@ public class ScopeAdapter extends RecyclerView.Adapter private final HashSet checkedList = new HashSet<>(); private final ConcurrentLinkedQueue searchList = new ConcurrentLinkedQueue<>(); private final List showList = new ArrayList<>(); + private final List denyList = new ArrayList<>(); private final SwitchBar.OnCheckedChangeListener switchBarOnCheckedChangeListener = new SwitchBar.OnCheckedChangeListener() { @Override @@ -161,6 +162,11 @@ public class ScopeAdapter extends RecyclerView.Adapter if (checkedList.contains(app)) { return false; } + if (preferences.getBoolean("filter_denylist", false)) { + if (denyList.contains(info.packageName)) { + return true; + } + } if (preferences.getBoolean("filter_modules", true)) { if (info.applicationInfo.metaData != null && info.applicationInfo.metaData.containsKey("xposedminversion")) { return true; @@ -248,6 +254,9 @@ public class ScopeAdapter extends RecyclerView.Adapter } else if (itemId == R.id.item_filter_modules) { item.setChecked(!item.isChecked()); preferences.edit().putBoolean("filter_modules", item.isChecked()).apply(); + } else if (itemId == R.id.item_filter_denylist) { + item.setChecked(!item.isChecked()); + preferences.edit().putBoolean("filter_denylist", item.isChecked()).apply(); } else if (itemId == R.id.menu_launch) { Intent launchIntent = AppHelper.getSettingsIntent(module.packageName, module.userId); if (launchIntent != null) { @@ -320,6 +329,7 @@ public class ScopeAdapter extends RecyclerView.Adapter menu.findItem(R.id.item_filter_system).setChecked(preferences.getBoolean("filter_system_apps", true)); menu.findItem(R.id.item_filter_games).setChecked(preferences.getBoolean("filter_games", true)); menu.findItem(R.id.item_filter_modules).setChecked(preferences.getBoolean("filter_modules", true)); + menu.findItem(R.id.item_filter_denylist).setChecked(preferences.getBoolean("filter_denylist", false)); switch (preferences.getInt("list_sort", 0)) { case 7: menu.findItem(R.id.item_sort_by_update_time).setChecked(true); @@ -354,8 +364,9 @@ public class ScopeAdapter extends RecyclerView.Adapter @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - holder.root.setAlpha(enabled ? 1.0f : .5f); AppInfo appInfo = showList.get(position); + boolean deny = denyList.contains(appInfo.packageName); + holder.root.setAlpha(!deny && enabled ? 1.0f : .5f); boolean android = appInfo.packageName.equals("android"); CharSequence appName; int userId = appInfo.applicationInfo.uid / 100000; @@ -403,6 +414,21 @@ public class ScopeAdapter extends RecyclerView.Adapter } else { holder.hint.setVisibility(View.GONE); } + if (deny) { + if (sb.length() != 0) sb.append("\n"); + String denylist = activity.getString(R.string.deny_list_info); + sb.append(denylist); + final ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(ResourceUtils.resolveColor(activity.getTheme(), rikka.material.R.attr.colorWarning)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + final TypefaceSpan typefaceSpan = new TypefaceSpan(Typeface.create("sans-serif-medium", Typeface.NORMAL)); + sb.setSpan(typefaceSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } else { + final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD); + sb.setSpan(styleSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } + sb.setSpan(foregroundColorSpan, sb.length() - denylist.length(), sb.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } + holder.hint.setText(sb); holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { activity.getMenuInflater().inflate(R.menu.menu_app_item, menu); @@ -470,6 +496,8 @@ public class ScopeAdapter extends RecyclerView.Adapter List appList = AppHelper.getAppList(force); checkedList.clear(); recommendedList.clear(); + denyList.clear(); + denyList.addAll(AppHelper.getDenyList(force)); var tmpList = new ArrayList(); checkedList.addAll(ConfigManager.getModuleScope(module.packageName)); HashSet installedList = new HashSet<>(); @@ -548,6 +576,9 @@ public class ScopeAdapter extends RecyclerView.Adapter Snackbar.make(fragment.binding.snackbar, R.string.reboot_required, Snackbar.LENGTH_SHORT) .setAction(R.string.reboot, v -> ConfigManager.reboot(false)) .show(); + } else if (denyList.contains(appInfo.packageName)) { + Snackbar.make(fragment.binding.snackbar, activity.getString(R.string.deny_list, appInfo.label), Snackbar.LENGTH_SHORT) + .show(); } } diff --git a/app/src/main/res/menu/menu_app_list.xml b/app/src/main/res/menu/menu_app_list.xml index a670523d..1555c0a4 100644 --- a/app/src/main/res/menu/menu_app_list.xml +++ b/app/src/main/res/menu/menu_app_list.xml @@ -58,6 +58,12 @@ android:checkable="true" android:checked="true" android:title="@string/menu_show_system_apps" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f92d863..4bacde3c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -124,6 +124,7 @@ You did not select any app. Continue? Games Modules + Denylist Failed save scope list %1$s\nVersion %2$s Recommended @@ -142,6 +143,8 @@ Reboot is required for apply changes Reboot Hide + %s is on denylist. It may not take effect. + On denylist View in other app diff --git a/core/.gitignore b/core/.gitignore index 12da621a..00ac22a3 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -1,3 +1,3 @@ /build /release -/src/main/cpp/main/src/config.cpp +/src/main/cpp/main/api/config.cpp diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 2a2b3e2d..a980a2e5 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -36,7 +36,7 @@ plugins { } val moduleName = "LSPosed" -val moduleId = "riru_lsposed" +val moduleBaseId = "lsposed" val authors = "LSPosed Developers" val riruModuleId = "lsposed" @@ -62,30 +62,13 @@ val androidCompileNdkVersion: String by rootProject.extra val androidSourceCompatibility: JavaVersion by rootProject.extra val androidTargetCompatibility: JavaVersion by rootProject.extra -dependencies { - implementation("dev.rikka.ndk:riru:26.0.0") - implementation("dev.rikka.ndk.thirdparty:cxx:1.1.0") - implementation("io.github.vvb2060.ndk:dobby:1.2") - implementation("com.android.tools.build:apksig:$agpVersion") - implementation("org.apache.commons:commons-lang3:3.12.0") - implementation("de.upb.cs.swt:axml:2.1.1") - compileOnly("androidx.annotation:annotation:1.2.0") - compileOnly(project(":hiddenapi-stubs")) - implementation(project(":hiddenapi-bridge")) - implementation(project(":manager-service")) - debugImplementation(files(File(project.buildDir, "tmp/debugR.jar")) { - builtBy("generateAppDebugRFile") - }) - releaseImplementation(files(File(project.buildDir, "tmp/releaseR.jar")) { - builtBy("generateAppReleaseRFile") - }) -} - android { compileSdk = androidCompileSdkVersion ndkVersion = androidCompileNdkVersion buildToolsVersion = androidBuildToolsVersion + flavorDimensions += "api" + buildFeatures { prefab = true } @@ -100,9 +83,9 @@ android { externalNativeBuild { ndkBuild { - arguments += "RIRU_MODULE_API_VERSION=$moduleMaxRiruApiVersion" - arguments += "MODULE_NAME=$riruModuleId" arguments += "INJECTED_AID=$injectedPackageUid" + arguments += "VERSION_CODE=$verCode" + arguments += "VERSION_NAME=$verName" arguments += "-j${Runtime.getRuntime().availableProcessors()}" } } @@ -139,6 +122,59 @@ android { sourceCompatibility(androidSourceCompatibility) } + productFlavors { + all { + externalNativeBuild { + ndkBuild { + arguments += "MODULE_NAME=${name.toLowerCase()}_$moduleBaseId" + arguments += "API=${name.toLowerCase()}" + } + } + } + + create("Riru") { + dimension = "api" + externalNativeBuild { + ndkBuild { + arguments += "API_VERSION=$moduleMaxRiruApiVersion" + } + } + } + + create("Zygisk") { + dimension = "api" + externalNativeBuild { + ndkBuild { + arguments += "API_VERSION=1" + } + } + } + } + +} + + +dependencies { + // keep this dep since it affects ccache + implementation("dev.rikka.ndk:riru:26.0.0") + implementation("dev.rikka.ndk.thirdparty:cxx:1.1.0") + implementation("io.github.vvb2060.ndk:dobby:1.2") + implementation("com.android.tools.build:apksig:$agpVersion") + implementation("org.apache.commons:commons-lang3:3.12.0") + implementation("de.upb.cs.swt:axml:2.1.1") + compileOnly("androidx.annotation:annotation:1.2.0") + compileOnly(project(":hiddenapi-stubs")) + implementation(project(":hiddenapi-bridge")) + implementation(project(":manager-service")) + android.applicationVariants.all { + "${name}Implementation"(files(File(project.buildDir, "tmp/${name}R.jar")) { + builtBy("generateApp${name}RFile") + }) + } +} + +val zipAll = task("zipAll", Task::class) { + } androidComponents.onVariants { v -> @@ -147,19 +183,23 @@ androidComponents.onVariants { v -> else (v as AnalyticsEnabledApplicationVariant).delegate as ApplicationVariantImpl val variantCapped = variant.name.capitalize() val variantLowered = variant.name.toLowerCase() - val zipFileName = "$moduleName-v$verName-$verCode-$variantLowered.zip" + val buildTypeCapped = variant.buildType!!.capitalize() + val buildTypeLowered = variant.buildType!!.toLowerCase() + val flavorCapped = variant.flavorName!!.capitalize() + val flavorLowered = variant.flavorName!!.toLowerCase() + val magiskDir = "$buildDir/magisk/$variantLowered" task("generateApp${variantCapped}RFile", Jar::class) { - dependsOn(":app:process${variantCapped}Resources") + dependsOn(":app:process${buildTypeCapped}Resources") doLast { val rFile = JarFile( File( project(":app").buildDir, - "intermediates/compile_and_runtime_not_namespaced_r_class_jar/${variantLowered}/R.jar" + "intermediates/compile_and_runtime_not_namespaced_r_class_jar/${buildTypeLowered}/R.jar" ) ) - ZipOutputStream(FileOutputStream(File(project.buildDir, "tmp/${variantLowered}R.jar"))).use { + ZipOutputStream(FileOutputStream(File(project.buildDir, "tmp/${variantCapped}R.jar"))).use { for (entry in rFile.entries()) { if (entry.name.startsWith("org/lsposed/manager")) { it.putNextEntry(entry) @@ -176,10 +216,10 @@ androidComponents.onVariants { v -> val outSrcDir = file("$buildDir/generated/source/signInfo/${variantLowered}") val outSrc = file("$outSrcDir/org/lsposed/lspd/util/SignInfo.java") val signInfoTask = tasks.register("generate${variantCapped}SignInfo") { - dependsOn(":app:validateSigning${variantCapped}") + dependsOn(":app:validateSigning${buildTypeCapped}") outputs.file(outSrc) doLast { - val sign = app.buildTypes.named(variantLowered).get().signingConfig + val sign = app.buildTypes.named(buildTypeLowered).get().signingConfig outSrc.parentFile.mkdirs() val certificateInfo = KeystoreHelper.getCertificateInfo( sign?.storeType, @@ -202,13 +242,16 @@ androidComponents.onVariants { v -> variant.variantData.registerJavaGeneratingTask(signInfoTask, arrayListOf(outSrcDir)) } + val moduleId = "${flavorLowered}_$moduleBaseId" + val zipFileName = "$moduleName-${flavorLowered}-v$verName-$verCode-$buildTypeLowered.zip" + val prepareMagiskFilesTask = task("prepareMagiskFiles$variantCapped", Sync::class) { dependsOn("assemble$variantCapped") - dependsOn(":app:assemble$variantCapped") + dependsOn(":app:assemble$buildTypeCapped") into(magiskDir) from("${rootProject.projectDir}/README.md") from("$projectDir/magisk_module") { - exclude("riru.sh", "module.prop") + exclude("riru.sh", "module.prop", "customize.sh") } from("$projectDir/magisk_module") { include("module.prop") @@ -217,32 +260,45 @@ androidComponents.onVariants { v -> "versionName" to "v$verName", "versionCode" to verCode, "authorList" to authors, - "minRiruVersionName" to moduleMinRiruVersionName + "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("riru.sh") - 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 (variantLowered == "debug") "true" else "false", - ) + from("$projectDir/magisk_module") { + include("customize.sh") + val tokens = mapOf("FLAVOR" to flavorLowered) filter("tokens" to tokens) filter("eol" to FixCrLfFilter.CrLf.newInstance("lf")) } - from("${project(":app").buildDir}/outputs/apk/${variantLowered}") { + if (flavorLowered == "riru") { + from("${projectDir}/magisk_module") { + include("riru.sh") + 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").buildDir}/outputs/apk/${buildTypeLowered}") { include("*.apk") rename(".*\\.apk", "manager.apk") } into("lib") { - from("${buildDir}/intermediates/stripped_native_libs/$variantLowered/out/lib") + from("${buildDir}/intermediates/stripped_native_libs/$variantCapped/out/lib") } - val dexOutPath = if (variantLowered == "release") - "$buildDir/intermediates/dex/$variantLowered/minify${variantCapped}WithR8" else - "$buildDir/intermediates/dex/$variantLowered/mergeDex$variantCapped" + 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") @@ -266,6 +322,8 @@ androidComponents.onVariants { v -> from(magiskDir) } + zipAll.dependsOn(zipTask) + val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath val pushTask = task("push${variantCapped}", Exec::class) { dependsOn(zipTask) @@ -313,7 +371,7 @@ val reRunLspd = task("reRunLspd", Exec::class) { dependsOn(pushLspd) dependsOn(pushLspdNative) dependsOn(killLspd) - commandLine(adb, "shell", "su", "-c", "sh /data/adb/modules/riru_lsposed/service.sh&") + commandLine(adb, "shell", "su", "-c", "sh /data/adb/modules/*_lsposed/service.sh&") isIgnoreExitValue = true } val tmpApk = "/data/local/tmp/lsp.apk" @@ -335,13 +393,3 @@ task("reRunApp", Exec::class) { isIgnoreExitValue = true finalizedBy(reRunLspd) } - -val generateVersion = task("generateVersion", Copy::class) { - inputs.property("VERSION_CODE", verCode) - inputs.property("VERSION_NAME", verName) - from("${projectDir}/src/main/cpp/main/template") - include("config.cpp") - expand("VERSION_CODE" to verCode, "VERSION_NAME" to verName) - into("${projectDir}/src/main/cpp/main/src") -} -tasks.getByName("preBuild").dependsOn(generateVersion) diff --git a/core/magisk_module/customize.sh b/core/magisk_module/customize.sh index 316d0408..daa40c56 100644 --- a/core/magisk_module/customize.sh +++ b/core/magisk_module/customize.sh @@ -21,6 +21,20 @@ # shellcheck disable=SC2034 SKIPUNZIP=1 +FLAVOR=@FLAVOR@ + +enforce_install_from_magisk_app() { + if $BOOTMODE; then + ui_print "- Installing from Magisk app" + else + ui_print "*********************************************************" + ui_print "! Install from recovery is NOT supported" + ui_print "! Some recovery has broken implementations, install with such recovery will finally cause Riru or Riru modules not working" + ui_print "! Please install from Magisk app" + abort "*********************************************************" + fi +} + VERSION=$(grep_prop version "${TMPDIR}/module.prop") ui_print "- LSPosed version ${VERSION}" @@ -44,12 +58,14 @@ check_android_version check_magisk_version check_incompatible_module -# Extract riru.sh -extract "$ZIPFILE" 'riru.sh' "$TMPDIR" -. "$TMPDIR/riru.sh" +if [ "$FLAVOR" == "riru" ]; then + # Extract riru.sh + extract "$ZIPFILE" 'riru.sh' "$TMPDIR" + . "$TMPDIR/riru.sh" + # Functions from riru.sh + check_riru_version +fi -# Functions from riru.sh -check_riru_version enforce_install_from_magisk_app # Check architecture @@ -73,48 +89,63 @@ extract "$ZIPFILE" 'lspd' "$MODPATH" rm -f /data/adb/lspd/manager.apk extract "$ZIPFILE" 'manager.apk' '/data/adb/lspd' -mkdir "$MODPATH/riru" -mkdir "$MODPATH/riru/lib" -mkdir "$MODPATH/riru/lib64" - -if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then - ui_print "- Extracting arm libraries" - extract "$ZIPFILE" "lib/armeabi-v7a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib" true - - if [ "$IS64BIT" = true ]; then - ui_print "- Extracting arm64 libraries" - extract "$ZIPFILE" "lib/arm64-v8a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true - extract "$ZIPFILE" 'lib/arm64-v8a/libdaemon.so' "$MODPATH" true - else - extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true - fi +ui_print "- Extracting daemon libraries" +if [ "$ARCH" = "arm" ] ; then + extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true +elif [ "$ARCH" = "arm64" ]; then + extract "$ZIPFILE" 'lib/arm64-v8a/libdaemon.so' "$MODPATH" true +elif [ "$ARCH" = "x86" ]; then + extract "$ZIPFILE" 'lib/x86/libdaemon.so' "$MODPATH" true +elif [ "$ARCH" = "x64" ]; then + extract "$ZIPFILE" 'lib/x86_64/libdaemon.so' "$MODPATH" true fi +if [ "$FLAVOR" == "zygisk" ]; then + mkdir -p "$MODPATH/zygisk" + extract "$ZIPFILE" "lib/armeabi-v7a/liblspd.so" "$MODPATH/zygisk" true + mv "$MODPATH/zygisk/liblspd.so" "$MODPATH/zygisk/armeabi-v7a.so" + extract "$ZIPFILE" "lib/arm64-v8a/liblspd.so" "$MODPATH/zygisk" true + mv "$MODPATH/zygisk/liblspd.so" "$MODPATH/zygisk/arm64-v8a.so" + extract "$ZIPFILE" "lib/x86_64/liblspd.so" "$MODPATH/zygisk" true + mv "$MODPATH/zygisk/liblspd.so" "$MODPATH/zygisk/x86_64.so" + extract "$ZIPFILE" "lib/x86/liblspd.so" "$MODPATH/zygisk" true + mv "$MODPATH/zygisk/liblspd.so" "$MODPATH/zygisk/x86.so" +elif [ "$FLAVOR" == "riru" ]; then + mkdir "$MODPATH/riru" + mkdir "$MODPATH/riru/lib" + mkdir "$MODPATH/riru/lib64" + if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then + ui_print "- Extracting arm libraries" + extract "$ZIPFILE" "lib/armeabi-v7a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib" true -if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then - ui_print "- Extracting x86 libraries" - extract "$ZIPFILE" "lib/x86/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib" true - - if [ "$IS64BIT" = true ]; then - ui_print "- Extracting x64 libraries" - extract "$ZIPFILE" "lib/x86_64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true - extract "$ZIPFILE" 'lib/x86_64/libdaemon.so' "$MODPATH" true - else - extract "$ZIPFILE" 'lib/x86/libdaemon.so' "$MODPATH" true + if [ "$IS64BIT" = true ]; then + ui_print "- Extracting arm64 libraries" + extract "$ZIPFILE" "lib/arm64-v8a/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true + fi fi -fi -if [ "$RIRU_MODULE_DEBUG" = true ]; then - mv "$MODPATH/riru" "$MODPATH/system" - mv "$MODPATH/system/lib/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib/libriru_$RIRU_MODULE_LIB_NAME.so" - mv "$MODPATH/system/lib64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib64/libriru_$RIRU_MODULE_LIB_NAME.so" - mv "$MODPATH/framework" "$MODPATH/system/framework" - if [ "$RIRU_API" -ge 26 ]; then - mkdir -p "$MODPATH/riru/lib" - mkdir -p "$MODPATH/riru/lib64" - touch "$MODPATH/riru/lib/libriru_$RIRU_MODULE_LIB_NAME" - touch "$MODPATH/riru/lib64/libriru_$RIRU_MODULE_LIB_NAME" - else - mkdir -p "/data/adb/riru/modules/$RIRU_MODULE_LIB_NAME" + if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then + ui_print "- Extracting x86 libraries" + extract "$ZIPFILE" "lib/x86/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib" true + + if [ "$IS64BIT" = true ]; then + ui_print "- Extracting x64 libraries" + extract "$ZIPFILE" "lib/x86_64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/riru/lib64" true + fi + fi + + if [ "$RIRU_MODULE_DEBUG" = true ]; then + mv "$MODPATH/riru" "$MODPATH/system" + mv "$MODPATH/system/lib/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib/libriru_$RIRU_MODULE_LIB_NAME.so" + mv "$MODPATH/system/lib64/lib$RIRU_MODULE_LIB_NAME.so" "$MODPATH/system/lib64/libriru_$RIRU_MODULE_LIB_NAME.so" + mv "$MODPATH/framework" "$MODPATH/system/framework" + if [ "$RIRU_API" -ge 26 ]; then + mkdir -p "$MODPATH/riru/lib" + mkdir -p "$MODPATH/riru/lib64" + touch "$MODPATH/riru/lib/libriru_$RIRU_MODULE_LIB_NAME" + touch "$MODPATH/riru/lib64/libriru_$RIRU_MODULE_LIB_NAME" + else + mkdir -p "/data/adb/riru/modules/$RIRU_MODULE_LIB_NAME" + fi fi fi diff --git a/core/magisk_module/module.prop b/core/magisk_module/module.prop index edfd47ae..cb86e63a 100644 --- a/core/magisk_module/module.prop +++ b/core/magisk_module/module.prop @@ -1,6 +1,6 @@ id=${moduleId} -name=Riru - LSPosed +name=${api} - LSPosed version=${versionName} (${versionCode}) versionCode=${versionCode} author=${authorList} -description=Another enhanced implementation of Xposed Framework. Supports Android 8.1 ~ 12. Requires Riru ${minRiruVersionName} or above installed. +description=Another enhanced implementation of Xposed Framework. Supports Android 8.1 ~ 12. ${requirement}. diff --git a/core/magisk_module/sepolicy.rule b/core/magisk_module/sepolicy.rule index c7efd94a..f61a0bdf 100644 --- a/core/magisk_module/sepolicy.rule +++ b/core/magisk_module/sepolicy.rule @@ -1 +1,4 @@ allow system_server system_server process execmem + +# Used to load dex and should be removed after companion is ready +allow zygote adb_data_file dir search diff --git a/core/src/main/cpp/main/Android.mk b/core/src/main/cpp/main/Android.mk index 131244e3..0fa91beb 100644 --- a/core/src/main/cpp/main/Android.mk +++ b/core/src/main/cpp/main/Android.mk @@ -7,10 +7,23 @@ include $(CLEAR_VARS) LOCAL_MODULE := lspd LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/src)) -LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) -LOCAL_STATIC_LIBRARIES := cxx yahfa riru dobby dex_builder -LOCAL_CFLAGS := -DRIRU_MODULE -DRIRU_MODULE_API_VERSION=${RIRU_MODULE_API_VERSION} -LOCAL_CFLAGS += -DMODULE_NAME=${MODULE_NAME} +LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) api/config.cpp +LOCAL_STATIC_LIBRARIES := cxx riru yahfa dobby dex_builder +ifeq ($(API), riru) +LOCAL_SRC_FILES += api/riru_main.cpp +else ifeq ($(API), zygisk) +LOCAL_SRC_FILES += api/zygisk_main.cpp +endif LOCAL_CFLAGS += -DINJECTED_AID=${INJECTED_AID} LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY) + +$(LOCAL_PATH)/api/config.cpp : FORCE + $(file > $@,#include "config.h") + $(file >> $@,namespace lspd {) + $(file >> $@,const int versionCode = ${VERSION_CODE};) + $(file >> $@,const int apiVersion = ${API_VERSION};) + $(file >> $@,const char* const versionName = "${VERSION_NAME}";) + $(file >> $@,const char* const moduleName = "${MODULE_NAME}";) + $(file >> $@,}) +FORCE: ; diff --git a/core/src/main/cpp/main/src/main.cpp b/core/src/main/cpp/main/api/riru_main.cpp similarity index 71% rename from core/src/main/cpp/main/src/main.cpp rename to core/src/main/cpp/main/api/riru_main.cpp index 5358798b..bc95c735 100644 --- a/core/src/main/cpp/main/src/main.cpp +++ b/core/src/main/cpp/main/api/riru_main.cpp @@ -25,59 +25,51 @@ #include "logging.h" #include "config.h" #include "context.h" + +#define RIRU_MODULE #include -#include "symbol_cache.h" namespace lspd { int *allowUnload = nullptr; - - static constexpr uid_t kAidInjected = INJECTED_AID; - static constexpr uid_t kAidInet = 3003; - namespace { 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); + Context::GetInstance()->Init(); if constexpr (isDebug) { Context::GetInstance()->PreLoadDex("/system/" + kDexPath); } else { Context::GetInstance()->PreLoadDex(magiskPath + '/' + kDexPath); } - InitSymbolCache(); } void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *, jintArray *gids, jint *, jobjectArray *, jint *, - jstring *, jstring *nice_name, + jstring *, jstring *_nice_name, jintArray *, jintArray *, jboolean *start_child_zygote, jstring *, - jstring *app_data_dir, jboolean *, + jstring *_app_data_dir, jboolean *, jobjectArray *, jobjectArray *, jboolean *, jboolean *) { - 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; - } - Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, - *nice_name, + nice_name = *_nice_name; + app_data_dir = *_app_data_dir; + Context::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); + Context::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name, app_data_dir); } void nativeForkSystemServerPre(JNIEnv *env, jclass, uid_t *, gid_t *, @@ -88,43 +80,36 @@ namespace lspd { } void nativeForkSystemServerPost(JNIEnv *env, jclass, jint res) { - Context::GetInstance()->OnNativeForkSystemServerPost(env, res); + if (res == 0) + Context::GetInstance()->OnNativeForkSystemServerPost(env); } /* method added in Android Q */ void specializeAppProcessPre(JNIEnv *env, jclass, jint *_uid, jint *, jintArray *gids, jint *, jobjectArray *, jint *, - jstring *, jstring *nice_name, + jstring *, jstring *_nice_name, jboolean *start_child_zygote, jstring *, - jstring *app_data_dir, jboolean *, + jstring *_app_data_dir, jboolean *, jobjectArray *, jobjectArray *, jboolean *, jboolean *) { - 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; - } - Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, - *nice_name, + nice_name = *_nice_name; + app_data_dir = *_app_data_dir; + Context::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); + Context::GetInstance()->OnNativeForkAndSpecializePost(env, nice_name, app_data_dir); } } RiruVersionedModuleInfo module{ - .moduleApiVersion = RIRU_MODULE_API_VERSION, + .moduleApiVersion = apiVersion, .moduleInfo = RiruModuleInfo{ .supportHide = !isDebug, .version = versionCode, @@ -140,14 +125,11 @@ namespace lspd { }; } -#define quote(s) #s -#define str(s) quote(s) - RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) { LOGD("using riru %d", riru->riruApiVersion); LOGD("module path: %s", riru->magiskModulePath); lspd::magiskPath = riru->magiskModulePath; - if (!lspd::isDebug && lspd::magiskPath.find(str(MODULE_NAME)) == std::string::npos) { + if (!lspd::isDebug && lspd::magiskPath.find(lspd::moduleName) == std::string::npos) { LOGE("who am i"); return nullptr; } diff --git a/core/src/main/cpp/main/api/zygisk_main.cpp b/core/src/main/cpp/main/api/zygisk_main.cpp new file mode 100644 index 00000000..d1c69d5b --- /dev/null +++ b/core/src/main/cpp/main/api/zygisk_main.cpp @@ -0,0 +1,242 @@ +/* + * 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 LSPosed Contributors + */ + +#include +#include + +#include "jni/zygisk.h" +#include "logging.h" +#include "context.h" +#include "config.h" + +namespace lspd { + int *allowUnload = nullptr; + + namespace { + ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags) { + int sent = sendmsg(sockfd, msg, flags); + if (sent < 0) { + PLOGE("sendmsg"); + } + return sent; + } + + ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) { + int rec = recvmsg(sockfd, msg, flags); + if (rec < 0) { + PLOGE("recvmsg"); + } + return rec; + } + + // Read exact same size as count + ssize_t xxread(int fd, void *buf, size_t count) { + size_t read_sz = 0; + ssize_t ret; + do { + ret = read(fd, (std::byte *) buf + read_sz, count - read_sz); + if (ret < 0) { + if (errno == EINTR) + continue; + PLOGE("read"); + return ret; + } + read_sz += ret; + } while (read_sz != count && ret != 0); + if (read_sz != count) { + PLOGE("read (%zu != %zu)", count, read_sz); + } + return read_sz; + } + + // Write exact same size as count + ssize_t xwrite(int fd, const void *buf, size_t count) { + size_t write_sz = 0; + ssize_t ret; + do { + ret = write(fd, (std::byte *) buf + write_sz, count - write_sz); + if (ret < 0) { + if (errno == EINTR) + continue; + PLOGE("write"); + return ret; + } + write_sz += ret; + } while (write_sz != count && ret != 0); + if (write_sz != count) { + PLOGE("write (%zu != %zu)", count, write_sz); + } + return write_sz; + } + + int send_fds(int sockfd, void *cmsgbuf, size_t bufsz, const int *fds, int cnt) { + iovec iov = { + .iov_base = &cnt, + .iov_len = sizeof(cnt), + }; + msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + if (cnt) { + msg.msg_control = cmsgbuf; + msg.msg_controllen = bufsz; + cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * cnt); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * cnt); + } + + return xsendmsg(sockfd, &msg, 0); + } + + int send_fd(int sockfd, int fd) { + if (fd < 0) { + return send_fds(sockfd, nullptr, 0, nullptr, 0); + } + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + return send_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), &fd, 1); + } + + void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) { + iovec iov = { + .iov_base = &cnt, + .iov_len = sizeof(cnt), + }; + msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = bufsz + }; + + xrecvmsg(sockfd, &msg, MSG_WAITALL); + cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + if (msg.msg_controllen != bufsz || + cmsg == nullptr || + cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + return nullptr; + } + + return CMSG_DATA(cmsg); + } + + int recv_fd(int sockfd) { + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + + void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1); + if (data == nullptr) + return -1; + + int result; + memcpy(&result, data, sizeof(int)); + return result; + } + + int read_int(int fd) { + int val; + if (xxread(fd, &val, sizeof(val)) != sizeof(val)) + return -1; + return val; + } + + void write_int(int fd, int val) { + if (fd < 0) return; + xwrite(fd, &val, sizeof(val)); + } + } + + class ZygiskModule : public zygisk::ModuleBase { + JNIEnv *env_; + + void onLoad(zygisk::Api *api, JNIEnv *env) override { + env_ = env; + Context::GetInstance()->Init(); + + // === workaround without companion === + using namespace std::string_literals; + Context::GetInstance()->PreLoadDex("/data/adb/modules/"s + moduleName + "/framework/lspd.dex"); + // === end workaround === + + auto companion = api->connectCompanion(); + if (companion == -1) { + LOGE("Failed to connect to companion"); + return; + } + + if (int fd; read_int(companion) == 0 && (fd = recv_fd(companion)) != -1) { + Context::GetInstance()->PreLoadDex( + "/proc/self/fd/" + std::to_string(fd)); + close(fd); + } else { + LOGE("Failed to read dex fd"); + } + close(companion); + } + + void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { + Context::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); + } + + void preServerSpecialize(zygisk::ServerSpecializeArgs *args) override { + Context::GetInstance()->OnNativeForkSystemServerPre(env_); + } + + void postServerSpecialize(const zygisk::ServerSpecializeArgs *args) override { + Context::GetInstance()->OnNativeForkSystemServerPost(env_); + } + }; + +#define quote(s) #s + + bool InitCompanion() { + LOGD("onModuleLoaded: welcome to LSPosed!"); + LOGD("onModuleLoaded: version v%s (%d)", versionName, versionCode); + return true; + } + + void CompanionEntry(int client) { + using namespace std::string_literals; + static bool inited = InitCompanion(); + static std::string path = "/data/adb/modules/"s + lspd::moduleName + "/" + kDexPath; + static int fd = open(path.data(), O_RDONLY | O_CLOEXEC); + if (inited && fd > 0) { + write_int(client, 0); + send_fd(client, fd); + } else write_int(client, -1); + close(client); + } +} //namespace lspd + +REGISTER_ZYGISK_MODULE(lspd::ZygiskModule); + +REGISTER_ZYGISK_COMPANION(lspd::CompanionEntry); diff --git a/core/src/main/cpp/main/include/config.h b/core/src/main/cpp/main/include/config.h index f2f29fd4..e5baf050 100644 --- a/core/src/main/cpp/main/include/config.h +++ b/core/src/main/cpp/main/include/config.h @@ -71,5 +71,7 @@ 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/cpp/main/src/context.cpp b/core/src/main/cpp/main/src/context.cpp index 58d32d1f..417906fd 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/cpp/main/src/context.cpp @@ -41,6 +41,9 @@ namespace lspd { 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 Context::CallOnPostFixupStaticTrampolines(void *class_ptr) { if (!class_ptr || !class_linker_class_ || !post_fixup_static_mid_) [[unlikely]] { return; @@ -55,24 +58,33 @@ namespace lspd { } } - void Context::PreLoadDex(std::string_view dex_path) { - if (!dex.empty()) [[unlikely]] return; + Context::PreloadedDex::PreloadedDex(FILE *f) { + fseek(f, 0, SEEK_END); + auto size = ftell(f); + rewind(f); + if (auto addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fileno(f), 0); addr) { + addr_ = addr; + size_ = size; + } else { + LOGE("Read dex failed"); + } + } - FILE *f = fopen(dex_path.data(), "rb"); - if (!f) { + Context::PreloadedDex::~PreloadedDex() { + if (*this) munmap(addr_, size_); + } + + void Context::PreLoadDex(std::string_view dex_path) { + if (dex_) [[unlikely]] return; + + std::unique_ptr f{fopen(dex_path.data(), "rb"), &fclose}; + + if (!f || !(dex_ = PreloadedDex(f.get()))) { LOGE("Fail to open dex from %s", dex_path.data()); return; } - fseek(f, 0, SEEK_END); - dex.resize(ftell(f)); - rewind(f); - if (dex.size() != fread(dex.data(), sizeof(decltype(dex)::value_type), dex.size(), f)) { - LOGE("Read dex failed"); - dex.resize(0); - } - fclose(f); - LOGI("Loaded %s with size %zu", dex_path.data(), dex.size()); + LOGI("Loaded %s with size %zu", dex_path.data(), dex_.size()); } void Context::LoadDex(JNIEnv *env) { @@ -88,6 +100,7 @@ namespace lspd { 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 = std::move(dex_); auto dex_buffer = env->NewDirectByteBuffer(dex.data(), dex.size()); if (auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid, dex_buffer, sys_classloader)) { @@ -102,6 +115,10 @@ namespace lspd { env->GetJavaVM(&vm_); } + void Context::Init() { + InitSymbolCache(); + } + void Context::Init(JNIEnv *env) { if (auto class_linker_class = FindClassFromCurrentLoader(env, kClassLinkerClassName)) { class_linker_class_ = JNI_NewGlobalRef(env, class_linker_class); @@ -110,7 +127,8 @@ namespace lspd { "onPostFixupStaticTrampolines", "(Ljava/lang/Class;)V"); - if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)) { + if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), + kEntryClassName)) { entry_class_ = JNI_NewGlobalRef(env, entry_class); } @@ -122,13 +140,16 @@ namespace lspd { } ScopedLocalRef - Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) { + Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, + std::string_view class_name) { if (class_loader == nullptr) return {env, nullptr}; - static auto clz = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/DexClassLoader")); + static auto clz = JNI_NewGlobalRef(env, + JNI_FindClass(env, "dalvik/system/DexClassLoader")); static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if (!mid) { - mid = JNI_GetMethodID(env, clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + mid = JNI_GetMethodID(env, clz, "findClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); } if (mid) [[likely]] { auto target = JNI_CallObjectMethod(env, class_loader, mid, @@ -167,8 +188,7 @@ namespace lspd { } void - Context::OnNativeForkSystemServerPost(JNIEnv *env, jint res) { - if (res != 0) return; + Context::OnNativeForkSystemServerPost(JNIEnv *env) { if (!skip_) { LoadDex(env); Service::instance()->HookBridge(*this, env); @@ -184,13 +204,22 @@ namespace lspd { 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; - app_data_dir_ = app_data_dir; - nice_name_ = nice_name; JUTFString process_name(env, nice_name); skip_ = !sym_initialized; if (!skip_ && !app_data_dir) { @@ -213,10 +242,11 @@ namespace lspd { } void - Context::OnNativeForkAndSpecializePost(JNIEnv *env) { - const JUTFString process_name(env, nice_name_); + Context::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, + jstring app_data_dir) { + const JUTFString process_name(env, nice_name); auto binder = skip_ ? ScopedLocalRef{env, nullptr} - : Service::instance()->RequestBinder(env, nice_name_); + : Service::instance()->RequestBinder(env, nice_name); if (binder) { InstallInlineHooks(); LoadDex(env); @@ -224,7 +254,7 @@ namespace lspd { LOGD("Done prepare"); FindAndCall(env, "forkAndSpecializePost", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", - app_data_dir_, nice_name_, + app_data_dir, nice_name, binder); LOGD("injected xposed into %s", process_name.get()); setAllowUnload(false); diff --git a/core/src/main/cpp/main/src/context.h b/core/src/main/cpp/main/src/context.h index d1de8f65..d6c0ff55 100644 --- a/core/src/main/cpp/main/src/context.h +++ b/core/src/main/cpp/main/src/context.h @@ -45,31 +45,70 @@ namespace lspd { void CallOnPostFixupStaticTrampolines(void *class_ptr); - inline ScopedLocalRef FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const { + inline ScopedLocalRef + FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const { return FindClassFromLoader(env, GetCurrentClassLoader(), className); }; - void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jstring nice_name, jboolean is_child_zygote, jstring app_data_dir); + void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring nice_name, + jboolean is_child_zygote, jstring app_data_dir); - void OnNativeForkAndSpecializePost(JNIEnv *env); + void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_data_dir); - void OnNativeForkSystemServerPost(JNIEnv *env, jint res); + void OnNativeForkSystemServerPost(JNIEnv *env); void OnNativeForkSystemServerPre(JNIEnv *env); void PreLoadDex(std::string_view dex_paths); + void Init(); + private: inline static std::unique_ptr instance_ = std::make_unique(); jobject inject_class_loader_ = nullptr; jclass entry_class_ = nullptr; - jstring app_data_dir_ = nullptr; - jstring nice_name_ = nullptr; JavaVM *vm_ = nullptr; jclass class_linker_class_ = nullptr; jmethodID post_fixup_static_mid_ = nullptr; bool skip_ = false; - std::vector dex{}; + + struct PreloadedDex { + + PreloadedDex() : addr_(nullptr), size_(0) {} + + PreloadedDex(const PreloadedDex &) = delete; + + PreloadedDex &operator=(const PreloadedDex &) = delete; + + PreloadedDex &operator=(PreloadedDex &&other) { + addr_ = other.addr_; + size_ = other.size_; + other.addr_ = nullptr; + other.size_ = 0; + return *this; + } + + PreloadedDex(PreloadedDex &&other) : addr_(other.addr_), size_(other.size_) { + other.addr_ = nullptr; + other.size_ = 0; + }; + + operator bool() const { return addr_ && size_; } + + auto size() const { return size_; } + + auto data() const { return addr_; } + + PreloadedDex(FILE *f); + + ~PreloadedDex(); + + private: + void *addr_; + std::size_t size_; + }; + + PreloadedDex dex_{}; Context() {} @@ -78,11 +117,13 @@ namespace lspd { void Init(JNIEnv *env); static ScopedLocalRef FindClassFromLoader(JNIEnv *env, jobject class_loader, - std::string_view class_name); + std::string_view class_name); + static void setAllowUnload(bool unload); template - void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, Args&&... args) const; + void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, + Args &&... args) const; friend std::unique_ptr std::make_unique(); }; diff --git a/core/src/main/cpp/main/src/jni/zygisk.h b/core/src/main/cpp/main/src/jni/zygisk.h new file mode 100644 index 00000000..c0cfe1a8 --- /dev/null +++ b/core/src/main/cpp/main/src/jni/zygisk.h @@ -0,0 +1,282 @@ +// All content of this file is released to the public domain. + +// This file is the public API for Zygisk modules. +// DO NOT use this file for developing Zygisk modules as it might contain WIP changes. +// Always use the following header for development as those are finalized APIs: +// https://github.com/topjohnwu/zygisk-module-sample/blob/master/module/jni/zygisk.hpp + +#pragma once + +#include + +#define ZYGISK_API_VERSION 1 + +/* +Define a class and inherit zygisk::ModuleBase to implement the functionality of your module. +Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk. +Please note that modules will only be loaded after zygote has forked the child process. +THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM SERVER PROCESS, NOT THE ZYGOTE DAEMON! +Example code: +static jint (*orig_logger_entry_max)(JNIEnv *env); +static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); } +static void example_handler(int socket) { ... } +class ExampleModule : public zygisk::ModuleBase { +public: + void onLoad(zygisk::Api *api, JNIEnv *env) override { + this->api = api; + } + void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { + JNINativeMethod methods[] = { + { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max }, + }; + api->hookJniNativeMethods("android/util/Log", methods, 1); + *(void **) &orig_logger_entry_max = methods[0].fnPtr; + } +private: + zygisk::Api *api; +}; +REGISTER_ZYGISK_MODULE(ExampleModule) +REGISTER_ZYGISK_COMPANION(example_handler) +*/ + +namespace zygisk { + + struct Api; + struct AppSpecializeArgs; + struct ServerSpecializeArgs; + + class ModuleBase { + public: + + // This function is called when the module is loaded into the target process. + // A Zygisk API handle will be sent as an argument; call utility functions or interface + // with Zygisk through this handle. + virtual void onLoad(Api *api, JNIEnv *env) {} + + // This function is called before the app process is specialized. + // At this point, the process just got forked from zygote, but no app specific specialization + // is applied. This means that the process does not have any sandbox restrictions and + // still runs with the same privilege of zygote. + // + // All the arguments that will be sent and used for app specialization is passed as a single + // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app + // process will be specialized. + // + // If you need to run some operations as superuser, you can call Api::connectCompanion() to + // get a socket to do IPC calls with a root companion process. + // See Api::connectCompanion() for more info. + virtual void preAppSpecialize(AppSpecializeArgs *args) {} + + // This function is called after the app process is specialized. + // At this point, the process has all sandbox restrictions enabled for this application. + // This means that this function runs as the same privilege of the app's own code. + virtual void postAppSpecialize(const AppSpecializeArgs *args) {} + + // This function is called before the system server process is specialized. + // See preAppSpecialize(args) for more info. + virtual void preServerSpecialize(ServerSpecializeArgs *args) {} + + // This function is called after the app process is specialized. + // At this point, the process runs with the privilege of system_server. + virtual void postServerSpecialize(const ServerSpecializeArgs *args) {} + }; + + struct AppSpecializeArgs { + // Required arguments. These arguments are guaranteed to exist on all Android versions. + jint &uid; + jint &gid; + jintArray &gids; + jint &runtime_flags; + jint &mount_external; + jstring &se_info; + jstring &nice_name; + jstring &instruction_set; + jstring &app_data_dir; + + // Optional arguments. Please check whether the pointer is null before de-referencing + jboolean *const is_child_zygote; + jboolean *const is_top_app; + jobjectArray *const pkg_data_info_list; + jobjectArray *const whitelisted_data_info_list; + jboolean *const mount_data_dirs; + jboolean *const mount_storage_dirs; + + AppSpecializeArgs() = delete; + }; + + struct ServerSpecializeArgs { + jint &uid; + jint &gid; + jintArray &gids; + jint &runtime_flags; + jlong &permitted_capabilities; + jlong &effective_capabilities; + + ServerSpecializeArgs() = delete; + }; + + namespace internal { + struct api_table; + template void entry_impl(api_table *, JNIEnv *); + } + + struct Api { + + // Connect to a root companion process and get a Unix domain socket for IPC. + // + // This API only works in the pre[XXX]Specialize functions due to SELinux restrictions. + // + // The pre[XXX]Specialize functions run with the same privilege of zygote. + // If you would like to do some operations with superuser permissions, register a handler + // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func). + // Another good use case for a companion process is that if you want to share some resources + // across multiple processes, hold the resources in the companion process and pass it over. + // + // When this function is called, in the companion process, a socket pair will be created, + // your module's onCompanionRequest(int) callback will receive one socket, and the other + // socket will be returned. + // + // Returns a file descriptor to a socket that is connected to the socket passed to + // your module's onCompanionRequest(int). Returns -1 if the connection attempt failed. + int connectCompanion(); + + // Force Magisk's denylist unmount routines to run on this process. + // + // This API only works in preAppSpecialize. + // + // Processes added to Magisk's denylist will have all Magisk and its modules' files unmounted + // from its mount namespace. In addition, all Zygisk code will be unloaded from memory, which + // also implies that no Zygisk modules (including yours) are loaded. + // + // However, if for any reason your module still wants the unmount part of the denylist + // operation to be enabled EVEN IF THE PROCESS IS NOT ON THE DENYLIST, call this function. + // No code will be unloaded from memory (including your module) because there is no way to + // guarantee no crashes will occur. + // + // The unmounting does not happen immediately after the function is called. It is actually + // done during app process specialization. + void forceDenyListUnmount(); + + // Hook JNI native methods for a class + // + // Lookup all registered JNI native methods and replace it with your own functions. + // The original function pointer will be saved in each JNINativeMethod's fnPtr. + // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr + // will be set to nullptr. + void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods); + + // For ELFs loaded in memory matching `regex`, replace function `symbol` with `newFunc`. + // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`. + void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc); + + // For ELFs loaded in memory matching `regex`, exclude hooks registered for `symbol`. + // If `symbol` is nullptr, then all symbols will be excluded. + void pltHookExclude(const char *regex, const char *symbol); + + // Commit all the hooks that was previously registered. + // Returns false if an error occurred. + bool pltHookCommit(); + + private: + internal::api_table *impl; + template friend void internal::entry_impl(internal::api_table *, JNIEnv *); + }; + +// Register a class as a Zygisk module + +#define REGISTER_ZYGISK_MODULE(clazz) \ +void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \ + zygisk::internal::entry_impl(table, env); \ +} + +// Register a root companion request handler function for your module +// +// The function runs in a superuser daemon process and handles a root companion request from +// your module running in a target process. The function has to accept an integer value, +// which is a socket that is connected to the target process. +// See Api::connectCompanion() for more info. +// +// NOTE: the function can run concurrently on multiple threads. +// Be aware of race conditions if you have a globally shared resource. + +#define REGISTER_ZYGISK_COMPANION(func) \ +void zygisk_companion_entry(int client) { func(client); } + +/************************************************************************************ + * All the code after this point is internal code used to interface with Zygisk + * and guarantee ABI stability. You do not have to understand what it is doing. + ************************************************************************************/ + + namespace internal { + + struct module_abi { + long api_version; + ModuleBase *_this; + + void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *); + void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *); + void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *); + void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *); + + module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), _this(module) { + preAppSpecialize = [](auto self, auto args) { self->preAppSpecialize(args); }; + postAppSpecialize = [](auto self, auto args) { self->postAppSpecialize(args); }; + preServerSpecialize = [](auto self, auto args) { self->preServerSpecialize(args); }; + postServerSpecialize = [](auto self, auto args) { self->postServerSpecialize(args); }; + } + }; + + struct api_table { + // These first 2 entries are permanent, shall never change + void *_this; + bool (*registerModule)(api_table *, module_abi *); + + // Utility functions + void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int); + void (*pltHookRegister)(const char *, const char *, void *, void **); + void (*pltHookExclude)(const char *, const char *); + bool (*pltHookCommit)(); + + // Zygisk functions + int (*connectCompanion)(void * /* _this */); + void (*forceDenyListUnmount)(void * /* _this */); + }; + + template + void entry_impl(api_table *table, JNIEnv *env) { + ModuleBase *module = new T(); + if (!table->registerModule(table, new module_abi(module))) + return; + auto api = new Api(); + api->impl = table; + module->onLoad(api, env); + } + + } // namespace internal + + int Api::connectCompanion() { + return impl->connectCompanion(impl->_this); + } + void Api::forceDenyListUnmount() { + impl->forceDenyListUnmount(impl->_this); + } + void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) { + impl->hookJniNativeMethods(env, className, methods, numMethods); + } + void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) { + impl->pltHookRegister(regex, symbol, newFunc, oldFunc); + } + void Api::pltHookExclude(const char *regex, const char *symbol) { + impl->pltHookExclude(regex, symbol); + } + bool Api::pltHookCommit() { + return impl->pltHookCommit(); + } + +} // namespace zygisk + +[[gnu::visibility("default")]] [[gnu::used]] +extern "C" void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *); + +[[gnu::visibility("default")]] [[gnu::used]] +extern "C" void zygisk_companion_entry(int); diff --git a/core/src/main/cpp/main/template/config.cpp b/core/src/main/cpp/main/template/config.cpp deleted file mode 100644 index eddf0e58..00000000 --- a/core/src/main/cpp/main/template/config.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "config.h" - -namespace lspd { - const int versionCode = ${VERSION_CODE}; - const char* const versionName = "${VERSION_NAME}"; -} diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index 36781928..bc2f5593 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -50,6 +50,7 @@ public class ConfigFileManager { private static final Path lockPath = basePath.resolve("lock"); private static final Path configDirPath = basePath.resolve("config"); static final File dbPath = configDirPath.resolve("modules_config.db").toFile(); + static final File magiskDbPath = new File("/data/adb/magisk.db"); private static final Path logDirPath = basePath.resolve("log"); private static final Path oldLogDirPath = basePath.resolve("log.old"); private static final DateTimeFormatter formatter = diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java index 746b979f..654dafe5 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -459,6 +459,8 @@ public class ConfigManager { final var obsoleteModules = new HashSet(); final var moduleAvailability = new HashMap, Boolean>(); final var cachedProcessScope = new HashMap, List>(); + + final var denylist = new HashSet<>(getDenyListPackages()); while (cursor.moveToNext()) { Application app = new Application(); app.packageName = cursor.getString(appPkgNameIdx); @@ -488,6 +490,8 @@ public class ConfigManager { try { List processesScope = cachedProcessScope.computeIfAbsent(new Pair<>(app.packageName, app.userId), (k) -> { try { + if (denylist.contains(app.packageName)) + Log.w(TAG, app.packageName + " is on denylist. It may not take effect."); return getAssociatedProcesses(app); } catch (RemoteException e) { return Collections.emptyList(); @@ -920,4 +924,23 @@ public class ConfigManager { public boolean isSepolicyLoaded() { return sepolicyLoaded; } + + public static List getDenyListPackages() { + List result = new ArrayList<>(); + try { + final SQLiteDatabase magiskDb = + SQLiteDatabase.openDatabase(ConfigFileManager.magiskDbPath, new SQLiteDatabase.OpenParams.Builder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build()); + try (Cursor cursor = magiskDb.query(true, "denylist", new String[]{"package_name"}, null, null, null, null, null, null, null)) { + if (cursor == null) return result; + int packageNameIdx = cursor.getColumnIndex("package_name"); + while (cursor.moveToNext()) { + result.add(cursor.getString(packageNameIdx)); + } + return result; + } + } catch (Throwable e) { + Log.e(TAG, "get denylist", e); + } + return result; + } } diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 7bc6a054..89c01db5 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -719,6 +719,11 @@ public class LSPManagerService extends ILSPManagerService.Stub { setAddShortcut(true); } + @Override + public List getDenyListPackages() { + return ConfigManager.getDenyListPackages(); + } + @Override public void flashZip(String zipPath, ParcelFileDescriptor outputStream) { var processBuilder = new ProcessBuilder("magisk", "--install-module", zipPath); diff --git a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl index 9a036bdc..e2898537 100644 --- a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl +++ b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl @@ -75,4 +75,6 @@ interface ILSPManagerService { oneway void flashZip(String zipPath, in ParcelFileDescriptor outputStream) = 39; boolean performDexOptMode(String packageName) = 40; + + List getDenyListPackages() = 41; }