mirror of
https://github.com/LSPosed/LSPosed.git
synced 2025-02-19 11:49:16 +00:00
Separate core into bridge and loader (#1766)
This commit is contained in:
.github/workflows
build.gradle.ktscore
magisk-loader
.gitignorebuild.gradle.ktsproguard-rules.pro
settings.gradle.ktsmagisk_module
META-INF/com/google/android
customize.shdaemonmodule.proppost-fs-data.shriru.shsepolicy.ruleservice.shsystem.propuninstall.shutil_functions.shverify.shsrc/main
24
.github/workflows/core.yml
vendored
24
.github/workflows/core.yml
vendored
@ -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")
|
||||
|
@ -14,8 +14,9 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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",
|
||||
|
2
core/.gitignore
vendored
2
core/.gitignore
vendored
@ -1,4 +1,2 @@
|
||||
/build
|
||||
/release
|
||||
/src/main/cpp/api/config.cpp
|
||||
/.cxx
|
||||
|
@ -14,56 +14,26 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<Sync>("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<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
from("$projectDir/magisk_module") {
|
||||
include("customize.sh", "post-fs-data.sh")
|
||||
val tokens = mapOf("FLAVOR" to flavorLowered)
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
filter<FixCrLfFilter>("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<ReplaceTokens>("tokens" to tokens)
|
||||
filter<FixCrLfFilter>("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>("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<Exec>("push${variantCapped}") {
|
||||
dependsOn(zipTask)
|
||||
workingDir("${projectDir}/release")
|
||||
commandLine(adb, "push", zipFileName, "/data/local/tmp/")
|
||||
}
|
||||
val flashTask = task<Exec>("flash${variantCapped}") {
|
||||
dependsOn(pushTask)
|
||||
commandLine(
|
||||
adb, "shell", "su", "-c",
|
||||
"magisk --install-module /data/local/tmp/${zipFileName}"
|
||||
)
|
||||
}
|
||||
task<Exec>("flashAndReboot${variantCapped}") {
|
||||
dependsOn(flashTask)
|
||||
commandLine(adb, "shell", "reboot")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
afterEval()
|
||||
}
|
||||
|
||||
val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath
|
||||
val killLspd = task<Exec>("killLspd") {
|
||||
commandLine(adb, "shell", "su", "-c", "killall", "lspd")
|
||||
isIgnoreExitValue = true
|
||||
}
|
||||
val pushDaemon = task<Exec>("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<Exec>("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<Exec>("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<Exec>("pushApk") {
|
||||
dependsOn(":app:assembleDebug")
|
||||
workingDir("${project(":app").buildDir}/$apkDir/apk/debug")
|
||||
commandLine(adb, "push", "app-debug.apk", tmpApk)
|
||||
}
|
||||
val openApp = task<Exec>("openApp") {
|
||||
commandLine(
|
||||
adb,
|
||||
"shell",
|
||||
"am",
|
||||
"start",
|
||||
"-a",
|
||||
"android.intent.action.MAIN",
|
||||
"-c",
|
||||
"org.lsposed.manager.LAUNCH_MANAGER",
|
||||
"com.android.shell/.BugreportWarningActivity"
|
||||
)
|
||||
}
|
||||
task<Exec>("reRunApp") {
|
||||
dependsOn(pushApk)
|
||||
commandLine(adb, "shell", "su", "-c", "mv -f $tmpApk /data/adb/lspd/manager.apk")
|
||||
isIgnoreExitValue = true
|
||||
finalizedBy(reRunDaemon)
|
||||
}
|
||||
|
||||
evaluationDependsOn(":app")
|
||||
evaluationDependsOn(":daemon")
|
||||
|
7
core/proguard-rules.pro
vendored
7
core/proguard-rules.pro
vendored
@ -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 <methods>;
|
||||
}
|
||||
-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(...);
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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");
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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) {
|
||||
|
@ -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 $<TARGET_FILE:${PROJECT_NAME}>
|
||||
${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
|
||||
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>
|
||||
COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
|
||||
$<TARGET_FILE:${PROJECT_NAME}>)
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC dobby lsplant_static log)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dex_builder_static)
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
@ -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<Context> instance_ = std::make_unique<Context>();
|
||||
protected:
|
||||
inline static std::unique_ptr<Context> 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<jclass> FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||
std::string_view class_name);
|
||||
|
||||
static void setAllowUnload(bool unload);
|
||||
std::string_view class_name);
|
||||
|
||||
template<typename ...Args>
|
||||
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>(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<Context> std::make_unique<Context>();
|
||||
};
|
||||
|
@ -19,39 +19,19 @@
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#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 <sys/mman.h>
|
||||
#include "jni/resources_hook.h"
|
||||
#include "symbol_cache.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <fcntl.h>
|
||||
#include <native_util.h>
|
||||
|
||||
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, "<init>",
|
||||
"(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<typename ...Args>
|
||||
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>(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<void*>(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<jint[]>(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<jobject>{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<void*>(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
|
||||
|
@ -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}";
|
||||
}
|
||||
|
3
magisk-loader/.gitignore
vendored
Normal file
3
magisk-loader/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/build
|
||||
/release
|
||||
/.cxx
|
321
magisk-loader/build.gradle.kts
Normal file
321
magisk-loader/build.gradle.kts
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<Sync>("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<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
from("$projectDir/magisk_module") {
|
||||
include("customize.sh", "post-fs-data.sh")
|
||||
val tokens = mapOf("FLAVOR" to flavorLowered)
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
filter<FixCrLfFilter>("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<ReplaceTokens>("tokens" to tokens)
|
||||
filter<FixCrLfFilter>("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>("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<Exec>("push${variantCapped}") {
|
||||
dependsOn(zipTask)
|
||||
workingDir("${projectDir}/release")
|
||||
commandLine(adb, "push", zipFileName, "/data/local/tmp/")
|
||||
}
|
||||
val flashTask = task<Exec>("flash${variantCapped}") {
|
||||
dependsOn(pushTask)
|
||||
commandLine(
|
||||
adb, "shell", "su", "-c",
|
||||
"magisk --install-module /data/local/tmp/${zipFileName}"
|
||||
)
|
||||
}
|
||||
task<Exec>("flashAndReboot${variantCapped}") {
|
||||
dependsOn(flashTask)
|
||||
commandLine(adb, "shell", "reboot")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
afterEval()
|
||||
}
|
||||
|
||||
val adb: String = androidComponents.sdkComponents.adb.get().asFile.absolutePath
|
||||
val killLspd = task<Exec>("killLspd") {
|
||||
commandLine(adb, "shell", "su", "-c", "killall", "lspd")
|
||||
isIgnoreExitValue = true
|
||||
}
|
||||
val pushDaemon = task<Exec>("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<Exec>("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<Exec>("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<Exec>("pushApk") {
|
||||
dependsOn(":app:assembleDebug")
|
||||
workingDir("${project(":app").buildDir}/$apkDir/apk/debug")
|
||||
commandLine(adb, "push", "app-debug.apk", tmpApk)
|
||||
}
|
||||
val openApp = task<Exec>("openApp") {
|
||||
commandLine(
|
||||
adb,
|
||||
"shell",
|
||||
"am",
|
||||
"start",
|
||||
"-a",
|
||||
"android.intent.action.MAIN",
|
||||
"-c",
|
||||
"org.lsposed.manager.LAUNCH_MANAGER",
|
||||
"com.android.shell/.BugreportWarningActivity"
|
||||
)
|
||||
}
|
||||
task<Exec>("reRunApp") {
|
||||
dependsOn(pushApk)
|
||||
commandLine(adb, "shell", "su", "-c", "mv -f $tmpApk /data/adb/lspd/manager.apk")
|
||||
isIgnoreExitValue = true
|
||||
finalizedBy(reRunDaemon)
|
||||
}
|
||||
|
||||
evaluationDependsOn(":app")
|
||||
evaluationDependsOn(":daemon")
|
17
magisk-loader/proguard-rules.pro
vendored
Normal file
17
magisk-loader/proguard-rules.pro
vendored
Normal file
@ -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 <methods>;
|
||||
}
|
||||
-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
|
@ -14,8 +14,7 @@
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
~
|
||||
~ Copyright (C) 2020 EdXposed Contributors
|
||||
~ Copyright (C) 2021 LSPosed Contributors
|
||||
~ Copyright (C) 2022 LSPosed Contributors
|
||||
-->
|
||||
|
||||
<manifest package="org.lsposed.lspd" />
|
41
magisk-loader/src/main/java/org/lsposed/lspd/core/Main.java
Normal file
41
magisk-loader/src/main/java/org/lsposed/lspd/core/Main.java
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
32
magisk-loader/src/main/jni/CMakeLists.txt
Normal file
32
magisk-loader/src/main/jni/CMakeLists.txt
Normal file
@ -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 $<TARGET_FILE:${PROJECT_NAME}>
|
||||
${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
|
||||
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>
|
||||
COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
|
||||
$<TARGET_FILE:${PROJECT_NAME}>)
|
||||
endif()
|
@ -23,8 +23,8 @@
|
||||
#include <cstdlib>
|
||||
#include <array>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
34
magisk-loader/src/main/jni/include/loader.h
Normal file
34
magisk-loader/src/main/jni/include/loader.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
224
magisk-loader/src/main/jni/src/magisk_loader.cpp
Normal file
224
magisk-loader/src/main/jni/src/magisk_loader.cpp
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2020 EdXposed Contributors
|
||||
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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, "<init>",
|
||||
"(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<void*>(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<jint[]>(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<jobject>{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<void*>(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
|
58
magisk-loader/src/main/jni/src/magisk_loader.h
Normal file
58
magisk-loader/src/main/jni/src/magisk_loader.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<MagiskLoader>();
|
||||
}
|
||||
|
||||
inline static MagiskLoader *GetInstance() {
|
||||
return static_cast<MagiskLoader*>(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
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <dobby.h>
|
||||
#include <thread>
|
||||
#include "config.h"
|
||||
#include "loader.h"
|
||||
#include "service.h"
|
||||
#include "context.h"
|
||||
#include "utils/jni_helper.hpp"
|
6
magisk-loader/src/main/jni/template/loader.cpp
Normal file
6
magisk-loader/src/main/jni/template/loader.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "loader.h"
|
||||
|
||||
namespace lspd {
|
||||
const int apiVersion = ${API_VERSION};
|
||||
const char* const moduleName = "${MODULE_NAME}";
|
||||
}
|
@ -31,6 +31,7 @@ include(
|
||||
":daemon",
|
||||
":hiddenapi:stubs",
|
||||
":hiddenapi:bridge",
|
||||
":magisk-loader",
|
||||
":services:manager-service",
|
||||
":services:daemon-service",
|
||||
":services:xposed-service:interface",
|
||||
|
Reference in New Issue
Block a user