0
0
mirror of https://github.com/LSPosed/LSPosed.git synced 2025-02-19 11:49:16 +00:00

Separate core into bridge and loader ()

This commit is contained in:
Nullptr
2022-03-17 19:12:01 +08:00
committed by GitHub
parent 6b368b041b
commit e0de4ca6d7
51 changed files with 829 additions and 637 deletions

@ -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

@ -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")

@ -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

@ -0,0 +1,3 @@
/build
/release
/.cxx

@ -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

@ -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" />

@ -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;

@ -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);
}
};

@ -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;
}

@ -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

@ -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"

@ -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",