Separate core into bridge and loader (#1766)
This commit is contained in:
parent
6b368b041b
commit
e0de4ca6d7
|
|
@ -55,7 +55,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.ccache
|
~/.ccache
|
||||||
core/build/.lto-cache
|
magisk-loader/build/.lto-cache
|
||||||
daemon/build/.lto-cache
|
daemon/build/.lto-cache
|
||||||
key: native-cache-${{ github.sha }}
|
key: native-cache-${{ github.sha }}
|
||||||
restore-keys: native-cache-
|
restore-keys: native-cache-
|
||||||
|
|
@ -83,14 +83,14 @@ jobs:
|
||||||
if: success()
|
if: success()
|
||||||
id: prepareArtifact
|
id: prepareArtifact
|
||||||
run: |
|
run: |
|
||||||
riruReleaseName=`ls core/release/LSPosed-v*-riru-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "::set-output name=riruReleaseName::$riruReleaseName"
|
riruReleaseName=`ls magisk-loader/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"
|
riruDebugName=`ls magisk-loader/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"
|
zygiskReleaseName=`ls magisk-loader/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"
|
zygiskDebugName=`ls magisk-loader/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 magisk-loader/release/LSPosed-v*-riru-release.zip -d LSPosed-riru-release
|
||||||
unzip core/release/LSPosed-v*-riru-debug.zip -d LSPosed-riru-debug
|
unzip magisk-loader/release/LSPosed-v*-riru-debug.zip -d LSPosed-riru-debug
|
||||||
unzip core/release/LSPosed-v*-zygisk-release.zip -d LSPosed-zygisk-release
|
unzip magisk-loader/release/LSPosed-v*-zygisk-release.zip -d LSPosed-zygisk-release
|
||||||
unzip core/release/LSPosed-v*-zygisk-debug.zip -d LSPosed-zygisk-debug
|
unzip magisk-loader/release/LSPosed-v*-zygisk-debug.zip -d LSPosed-zygisk-debug
|
||||||
- name: Upload riru release
|
- name: Upload riru release
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
|
|
@ -116,14 +116,14 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: mappings
|
name: mappings
|
||||||
path: |
|
path: |
|
||||||
core/build/outputs/mapping
|
magisk-loader/build/outputs/mapping
|
||||||
app/build/outputs/mapping
|
app/build/outputs/mapping
|
||||||
- name: Upload symbols
|
- name: Upload symbols
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: symbols
|
name: symbols
|
||||||
path: |
|
path: |
|
||||||
core/build/symbols
|
magisk-loader/build/symbols
|
||||||
daemon/build/symbols
|
daemon/build/symbols
|
||||||
- name: Post to channel
|
- name: Post to channel
|
||||||
if: ${{ github.event_name != 'pull_request' && success() && github.ref == 'refs/heads/master' }}
|
if: ${{ github.event_name != 'pull_request' && success() && github.ref == 'refs/heads/master' }}
|
||||||
|
|
@ -134,7 +134,7 @@ jobs:
|
||||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||||
run: |
|
run: |
|
||||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||||
OUTPUT="core/release/"
|
OUTPUT="magisk-loader/release/"
|
||||||
export riruRelease=$(find $OUTPUT -name "LSPosed-v*-riru-release.zip")
|
export riruRelease=$(find $OUTPUT -name "LSPosed-v*-riru-release.zip")
|
||||||
export riruDebug=$(find $OUTPUT -name "LSPosed-v*-riru-debug.zip")
|
export riruDebug=$(find $OUTPUT -name "LSPosed-v*-riru-debug.zip")
|
||||||
export zygiskRelease=$(find $OUTPUT -name "LSPosed-v*-zygisk-release.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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* 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.dsl.ApplicationExtension
|
||||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.BaseExtension
|
||||||
|
|
@ -77,6 +78,7 @@ fun Project.configureBaseExtension() {
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "external")}"
|
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")
|
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
|
||||||
val flags = arrayOf(
|
val flags = arrayOf(
|
||||||
"-Wall",
|
"-Wall",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,2 @@
|
||||||
/build
|
/build
|
||||||
/release
|
|
||||||
/src/main/cpp/api/config.cpp
|
|
||||||
/.cxx
|
/.cxx
|
||||||
|
|
|
||||||
|
|
@ -14,56 +14,26 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* 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
|
val apiCode: Int by rootProject.extra
|
||||||
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 {
|
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 {
|
android {
|
||||||
flavorDimensions += "api"
|
namespace = "org.lsposed.lspd.core"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
prefab = true
|
androidResources = false
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "org.lsposed.lspd"
|
consumerProguardFiles("proguard-rules.pro")
|
||||||
multiDexEnabled = false
|
|
||||||
|
|
||||||
buildConfigField("int", "API_CODE", "$apiCode")
|
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 {
|
buildTypes {
|
||||||
|
|
@ -72,253 +42,14 @@ android {
|
||||||
proguardFiles("proguard-rules.pro")
|
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 {
|
dependencies {
|
||||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||||
implementation("de.upb.cs.swt:axml:2.1.2")
|
implementation("de.upb.cs.swt:axml:2.1.2")
|
||||||
compileOnly("androidx.annotation:annotation:1.3.0")
|
compileOnly("androidx.annotation:annotation:1.3.0")
|
||||||
compileOnly(projects.hiddenapi.stubs)
|
compileOnly(projects.hiddenapi.stubs)
|
||||||
implementation(projects.hiddenapi.bridge)
|
implementation(projects.hiddenapi.bridge)
|
||||||
implementation(projects.services.managerService)
|
|
||||||
implementation(projects.services.daemonService)
|
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 de.robv.android.xposed.** {*;}
|
||||||
-keep class android.** { *; }
|
-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 * {
|
-keepclasseswithmembers,includedescriptorclasses class * {
|
||||||
native <methods>;
|
native <methods>;
|
||||||
}
|
}
|
||||||
-keepclasseswithmembers class org.lsposed.lspd.service.BridgeService {
|
|
||||||
public static boolean *(android.os.IBinder, int, long, long, int);
|
|
||||||
}
|
|
||||||
|
|
||||||
-assumenosideeffects class android.util.Log {
|
-assumenosideeffects class android.util.Log {
|
||||||
public static *** v(...);
|
public static *** v(...);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
* Copyright (C) 2020 EdXposed Contributors
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
package de.robv.android.xposed;
|
||||||
|
|
@ -31,7 +31,7 @@ import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.util.XmlUtils;
|
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.lsposed.lspd.util.MetaDataReader;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
* Copyright (C) 2020 EdXposed Contributors
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.robv.android.xposed;
|
package de.robv.android.xposed;
|
||||||
|
|
@ -27,7 +27,7 @@ import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.util.Log;
|
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.HookBridge;
|
||||||
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
import org.lsposed.lspd.nativebridge.ResourcesHook;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
* Copyright (C) 2020 EdXposed Contributors
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.lsposed.lspd.core;
|
package org.lsposed.lspd.core;
|
||||||
|
|
@ -24,30 +24,23 @@ import android.app.ActivityThread;
|
||||||
import android.app.LoadedApk;
|
import android.app.LoadedApk;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.CompatibilityInfo;
|
import android.content.res.CompatibilityInfo;
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import com.android.internal.os.ZygoteInit;
|
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.deopt.PrebuiltMethodsDeopter;
|
||||||
import org.lsposed.lspd.hooker.CrashDumpHooker;
|
import org.lsposed.lspd.hooker.CrashDumpHooker;
|
||||||
import org.lsposed.lspd.hooker.HandleBindAppHooker;
|
import org.lsposed.lspd.hooker.HandleBindAppHooker;
|
||||||
import org.lsposed.lspd.hooker.LoadedApkCstrHooker;
|
|
||||||
import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;
|
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 org.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import de.robv.android.xposed.XposedHelpers;
|
import de.robv.android.xposed.XposedHelpers;
|
||||||
import de.robv.android.xposed.XposedInit;
|
import de.robv.android.xposed.XposedInit;
|
||||||
|
|
||||||
public class Main {
|
public class Startup {
|
||||||
public static void startBootstrapHook(boolean isSystem, String appDataDir) {
|
private static void startBootstrapHook(boolean isSystem) {
|
||||||
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
Utils.logD("startBootstrapHook starts: isSystem = " + isSystem);
|
||||||
XposedHelpers.findAndHookMethod(Thread.class, "dispatchUncaughtException",
|
XposedHelpers.findAndHookMethod(Thread.class, "dispatchUncaughtException",
|
||||||
Throwable.class, new CrashDumpHooker());
|
Throwable.class, new CrashDumpHooker());
|
||||||
|
|
@ -58,45 +51,28 @@ public class Main {
|
||||||
XposedHelpers.findAndHookMethod(ActivityThread.class,
|
XposedHelpers.findAndHookMethod(ActivityThread.class,
|
||||||
"handleBindApplication",
|
"handleBindApplication",
|
||||||
"android.app.ActivityThread$AppBindData",
|
"android.app.ActivityThread$AppBindData",
|
||||||
new HandleBindAppHooker(appDataDir));
|
new HandleBindAppHooker());
|
||||||
XposedHelpers.findAndHookConstructor(LoadedApk.class,
|
XposedHelpers.findAndHookConstructor(LoadedApk.class,
|
||||||
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
|
ActivityThread.class, ApplicationInfo.class, CompatibilityInfo.class,
|
||||||
ClassLoader.class, boolean.class, boolean.class, boolean.class,
|
ClassLoader.class, boolean.class, boolean.class, boolean.class,
|
||||||
new LoadedApkCstrHooker());
|
new LoadedApkCstrHooker());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void installBootstrapHooks(boolean isSystem, String appDataDir) {
|
public static void bootstrapXposed(String niceName) {
|
||||||
// Initialize the Xposed framework
|
// Initialize the Xposed framework
|
||||||
try {
|
try {
|
||||||
startBootstrapHook(isSystem, appDataDir);
|
startBootstrapHook(XposedInit.startsSystemServer);
|
||||||
|
Utils.logI("Loading modules for " + niceName + "/" + Process.myUid());
|
||||||
|
XposedInit.loadModules();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Utils.logE("error during Xposed initialization", 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
|
// init logger
|
||||||
XposedBridge.initXResources();
|
XposedBridge.initXResources();
|
||||||
XposedInit.startsSystemServer = isSystem;
|
XposedInit.startsSystemServer = isSystem;
|
||||||
PrebuiltMethodsDeopter.deoptBootMethods(); // do it once for secondary zygote
|
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/>.
|
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
* Copyright (C) 2020 EdXposed Contributors
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.lsposed.lspd.hooker;
|
package org.lsposed.lspd.hooker;
|
||||||
|
|
@ -35,11 +35,6 @@ import de.robv.android.xposed.XposedInit;
|
||||||
|
|
||||||
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
// normal process initialization (for new Activity, Service, BroadcastReceiver etc.)
|
||||||
public class HandleBindAppHooker extends XC_MethodHook {
|
public class HandleBindAppHooker extends XC_MethodHook {
|
||||||
String appDataDir;
|
|
||||||
|
|
||||||
public HandleBindAppHooker(String appDataDir) {
|
|
||||||
this.appDataDir = appDataDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void beforeHookedMethod(MethodHookParam param) {
|
protected void beforeHookedMethod(MethodHookParam param) {
|
||||||
|
|
@ -54,8 +49,7 @@ public class HandleBindAppHooker extends XC_MethodHook {
|
||||||
|
|
||||||
// Note: packageName="android" -> system_server process, ams pms etc;
|
// Note: packageName="android" -> system_server process, ams pms etc;
|
||||||
// packageName="system" -> android pkg, system dialogues.
|
// packageName="system" -> android pkg, system dialogues.
|
||||||
Utils.logD("processName=" + appProcessName +
|
Utils.logD("processName=" + appProcessName + ", packageName=" + reportedPackageName);
|
||||||
", packageName=" + reportedPackageName + ", appDataDir=" + appDataDir);
|
|
||||||
|
|
||||||
CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(bindData, "compatInfo");
|
CompatibilityInfo compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(bindData, "compatInfo");
|
||||||
if (appInfo.sourceDir == null) {
|
if (appInfo.sourceDir == null) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
project(lspd)
|
project(core)
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
|
|
||||||
add_subdirectory(${EXTERNAL_ROOT} external)
|
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 SRC_LIST)
|
||||||
aux_source_directory(src/jni 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} PUBLIC include)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} dobby dex_builder_static log lsplant_static)
|
target_link_libraries(${PROJECT_NAME} PUBLIC dobby lsplant_static log)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE dex_builder_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()
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2020 EdXposed Contributors
|
* Copyright (C) 2020 EdXposed Contributors
|
||||||
* Copyright (C) 2021 LSPosed Contributors
|
* Copyright (C) 2021 - 2022 LSPosed Contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -59,9 +59,6 @@ namespace lspd {
|
||||||
# define LP_SELECT(lp32, lp64) lp32
|
# define LP_SELECT(lp32, lp64) lp32
|
||||||
#endif
|
#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 kLibArtName = "libart.so"_tstr;
|
||||||
inline static constexpr auto kLibFwName = "libandroidfw.so"_tstr;
|
inline static constexpr auto kLibFwName = "libandroidfw.so"_tstr;
|
||||||
|
|
||||||
|
|
@ -70,7 +67,5 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern const int versionCode;
|
extern const int versionCode;
|
||||||
extern const int apiVersion;
|
|
||||||
extern const char* const versionName;
|
extern const char* const versionName;
|
||||||
extern const char* const moduleName;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,22 +49,12 @@ namespace lspd {
|
||||||
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
||||||
};
|
};
|
||||||
|
|
||||||
void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring nice_name,
|
virtual ~Context() = default;
|
||||||
jboolean is_child_zygote, jstring app_data_dir);
|
|
||||||
|
|
||||||
void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_data_dir);
|
protected:
|
||||||
|
inline static std::unique_ptr<Context> instance_;
|
||||||
void OnNativeForkSystemServerPost(JNIEnv *env);
|
|
||||||
|
|
||||||
void OnNativeForkSystemServerPre(JNIEnv *env);
|
|
||||||
|
|
||||||
void Init();
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline static std::unique_ptr<Context> instance_ = std::make_unique<Context>();
|
|
||||||
jobject inject_class_loader_ = nullptr;
|
jobject inject_class_loader_ = nullptr;
|
||||||
jclass entry_class_ = nullptr;
|
jclass entry_class_ = nullptr;
|
||||||
bool skip_ = false;
|
|
||||||
|
|
||||||
struct PreloadedDex {
|
struct PreloadedDex {
|
||||||
|
|
||||||
|
|
@ -90,7 +80,7 @@ namespace lspd {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use with caution!
|
// 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_; }
|
operator bool() const { return addr_ && size_; }
|
||||||
|
|
||||||
|
|
@ -107,19 +97,31 @@ namespace lspd {
|
||||||
|
|
||||||
Context() {}
|
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,
|
static lsplant::ScopedLocalRef<jclass> FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||||
std::string_view class_name);
|
std::string_view class_name);
|
||||||
|
|
||||||
static void setAllowUnload(bool unload);
|
|
||||||
|
|
||||||
template<typename ...Args>
|
template<typename ...Args>
|
||||||
void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig,
|
inline void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig,
|
||||||
Args &&... args) const;
|
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>();
|
friend std::unique_ptr<Context> std::make_unique<Context>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -19,39 +19,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "utils/jni_helper.hpp"
|
|
||||||
#include "jni/resources_hook.h"
|
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
#include "elf_util.h"
|
#include "native_util.h"
|
||||||
#include "jni/hook_bridge.h"
|
#include "jni/hook_bridge.h"
|
||||||
#include "jni/native_api.h"
|
#include "jni/native_api.h"
|
||||||
#include "service.h"
|
#include "jni/resources_hook.h"
|
||||||
#include <sys/mman.h>
|
|
||||||
#include "symbol_cache.h"
|
#include "symbol_cache.h"
|
||||||
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <native_util.h>
|
|
||||||
|
|
||||||
using namespace lsplant;
|
using namespace lsplant;
|
||||||
|
|
||||||
static_assert(FS_IOC_SETFLAGS == LP_SELECT(0x40046602, 0x40086602));
|
|
||||||
|
|
||||||
namespace lspd {
|
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) {
|
Context::PreloadedDex::PreloadedDex(int fd, std::size_t size) {
|
||||||
LOGD("Context::PreloadedDex::PreloadedDex: fd=%d, size=%zu", fd, size);
|
LOGD("Context::PreloadedDex::PreloadedDex: fd=%d, size=%zu", fd, size);
|
||||||
auto *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
auto *addr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
|
@ -68,46 +48,10 @@ namespace lspd {
|
||||||
if (*this) munmap(addr_, size_);
|
if (*this) munmap(addr_, size_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::LoadDex(JNIEnv *env, int fd, size_t size) {
|
void Context::InitHooks(JNIEnv *env, const lsplant::InitInfo& initInfo) {
|
||||||
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) {
|
|
||||||
if (!lsplant::Init(env, initInfo)) {
|
if (!lsplant::Init(env, initInfo)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(),
|
|
||||||
kEntryClassName)) {
|
|
||||||
entry_class_ = JNI_NewGlobalRef(env, entry_class);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegisterResourcesHook(env);
|
RegisterResourcesHook(env);
|
||||||
RegisterHookBridge(env);
|
RegisterHookBridge(env);
|
||||||
|
|
@ -138,160 +82,4 @@ namespace lspd {
|
||||||
LOGE("Class %s not found", class_name.data());
|
LOGE("Class %s not found", class_name.data());
|
||||||
return {env, nullptr};
|
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
|
} // namespace lspd
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,5 @@
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
const int versionCode = ${VERSION_CODE};
|
const int versionCode = ${VERSION_CODE};
|
||||||
const int apiVersion = ${API_VERSION};
|
|
||||||
const char* const versionName = "${VERSION_NAME}";
|
const char* const versionName = "${VERSION_NAME}";
|
||||||
const char* const moduleName = "${MODULE_NAME}";
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
@ -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
|
~ You should have received a copy of the GNU General Public License
|
||||||
~ along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
~ along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||||
~
|
~
|
||||||
~ Copyright (C) 2020 EdXposed Contributors
|
~ Copyright (C) 2022 LSPosed Contributors
|
||||||
~ Copyright (C) 2021 LSPosed Contributors
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<manifest package="org.lsposed.lspd" />
|
<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.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Process;
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
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 <cstdlib>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "config.h"
|
#include "loader.h"
|
||||||
#include "context.h"
|
#include "magisk_loader.h"
|
||||||
#include "symbol_cache.h"
|
#include "symbol_cache.h"
|
||||||
|
|
||||||
#define RIRU_MODULE
|
#define RIRU_MODULE
|
||||||
|
|
@ -36,13 +36,12 @@ namespace lspd {
|
||||||
std::string magiskPath;
|
std::string magiskPath;
|
||||||
|
|
||||||
jstring nice_name = nullptr;
|
jstring nice_name = nullptr;
|
||||||
jstring app_data_dir = nullptr;
|
|
||||||
|
|
||||||
void onModuleLoaded() {
|
void onModuleLoaded() {
|
||||||
LOGI("onModuleLoaded: welcome to LSPosed!");
|
LOGI("onModuleLoaded: welcome to LSPosed!");
|
||||||
LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode);
|
LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode);
|
||||||
InitSymbolCache(nullptr);
|
InitSymbolCache(nullptr);
|
||||||
Context::GetInstance()->Init();
|
MagiskLoader::Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *,
|
void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *,
|
||||||
|
|
@ -57,28 +56,27 @@ namespace lspd {
|
||||||
jboolean *,
|
jboolean *,
|
||||||
jboolean *) {
|
jboolean *) {
|
||||||
nice_name = *_nice_name;
|
nice_name = *_nice_name;
|
||||||
app_data_dir = *_app_data_dir;
|
MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids,
|
||||||
Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids,
|
|
||||||
nice_name,
|
nice_name,
|
||||||
*start_child_zygote,
|
*start_child_zygote,
|
||||||
app_data_dir);
|
*_app_data_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) {
|
void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) {
|
||||||
if (res == 0)
|
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 *,
|
void nativeForkSystemServerPre(JNIEnv *env, jclass, uid_t *, gid_t *,
|
||||||
jintArray *, jint *,
|
jintArray *, jint *,
|
||||||
jobjectArray *, jlong *,
|
jobjectArray *, jlong *,
|
||||||
jlong *) {
|
jlong *) {
|
||||||
Context::GetInstance()->OnNativeForkSystemServerPre(env);
|
MagiskLoader::GetInstance()->OnNativeForkSystemServerPre(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nativeForkSystemServerPost(JNIEnv *env, jclass, jint res) {
|
void nativeForkSystemServerPost(JNIEnv *env, jclass, jint res) {
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
Context::GetInstance()->OnNativeForkSystemServerPost(env);
|
MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* method added in Android Q */
|
/* method added in Android Q */
|
||||||
|
|
@ -93,15 +91,14 @@ namespace lspd {
|
||||||
jboolean *,
|
jboolean *,
|
||||||
jboolean *) {
|
jboolean *) {
|
||||||
nice_name = *_nice_name;
|
nice_name = *_nice_name;
|
||||||
app_data_dir = *_app_data_dir;
|
MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids,
|
||||||
Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid, *gids,
|
|
||||||
nice_name,
|
nice_name,
|
||||||
*start_child_zygote,
|
*start_child_zygote,
|
||||||
app_data_dir);
|
*_app_data_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void specializeAppProcessPost(JNIEnv *env, jclass) {
|
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 "zygisk.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "context.h"
|
#include "loader.h"
|
||||||
#include "config.h"
|
#include "magisk_loader.h"
|
||||||
#include "symbol_cache.h"
|
#include "symbol_cache.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
@ -281,7 +281,7 @@ namespace lspd {
|
||||||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||||||
env_ = env;
|
env_ = env;
|
||||||
api_ = api;
|
api_ = api;
|
||||||
Context::GetInstance()->Init();
|
MagiskLoader::Init();
|
||||||
|
|
||||||
auto companion = api->connectCompanion();
|
auto companion = api->connectCompanion();
|
||||||
if (companion == -1) {
|
if (companion == -1) {
|
||||||
|
|
@ -308,19 +308,18 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
|
||||||
Context::GetInstance()->OnNativeForkAndSpecializePre(
|
MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre(
|
||||||
env_, args->uid, args->gids, args->nice_name,
|
env_, args->uid, args->gids, args->nice_name,
|
||||||
args->is_child_zygote ? *args->is_child_zygote : false, args->app_data_dir);
|
args->is_child_zygote ? *args->is_child_zygote : false, args->app_data_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
||||||
Context::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name,
|
MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name);
|
||||||
args->app_data_dir);
|
|
||||||
if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void preServerSpecialize([[maybe_unused]] zygisk::ServerSpecializeArgs *args) override {
|
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 {
|
void postServerSpecialize([[maybe_unused]] const zygisk::ServerSpecializeArgs *args) override {
|
||||||
|
|
@ -333,7 +332,7 @@ namespace lspd {
|
||||||
env_->DeleteLocalRef(name);
|
env_->DeleteLocalRef(name);
|
||||||
env_->DeleteLocalRef(process);
|
env_->DeleteLocalRef(process);
|
||||||
}
|
}
|
||||||
Context::GetInstance()->OnNativeForkSystemServerPost(env_);
|
MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env_);
|
||||||
if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
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 <dobby.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "config.h"
|
#include "loader.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "utils/jni_helper.hpp"
|
#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",
|
":daemon",
|
||||||
":hiddenapi:stubs",
|
":hiddenapi:stubs",
|
||||||
":hiddenapi:bridge",
|
":hiddenapi:bridge",
|
||||||
|
":magisk-loader",
|
||||||
":services:manager-service",
|
":services:manager-service",
|
||||||
":services:daemon-service",
|
":services:daemon-service",
|
||||||
":services:xposed-service:interface",
|
":services:xposed-service:interface",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue