Skip to content

Commit bee5f2e

Browse files
authored
Refactor native modules and migrate to new Zygisk architecture (#550)
We replace the legacy magisk-loader and core JNI with a rewritten Zygisk module and a centralized native library. Major changes: - Zygisk Module: Replaced magisk-loader with a rewritten Zygisk implementation in Kotlin and C++. - Native Migration: Moved ELF parsing, symbol caching, and hooking logic from core/jni to a standalone native module. - Anti-detection: Removed enforced parcel descriptors to evade heap-based string detection. - Scripting: Simplified Magisk module shell scripts (customize.sh, service.sh) and removed redundant utility functions. - Dex-obfuscation: Refactored obfuscation management to improve code clarity. Note that `zygisk.json` and the `lspd` directory are kept to ensure a smooth update path and database continuity.
1 parent 6fc25c6 commit bee5f2e

121 files changed

Lines changed: 7060 additions & 6214 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/core.yml

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@ name: Core
22

33
on:
44
workflow_dispatch:
5-
inputs:
6-
post_telegram:
7-
description: 'Post to Telegram'
8-
required: true
9-
type: boolean
105
push:
116
branches: [ master ]
127
tags: [ v* ]
@@ -86,29 +81,29 @@ jobs:
8681
if: success()
8782
id: prepareArtifact
8883
run: |
89-
zygiskReleaseName=`ls magisk-loader/release/LSPosed-v*-zygisk-release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "zygiskReleaseName=$zygiskReleaseName" >> $GITHUB_OUTPUT
90-
zygiskDebugName=`ls magisk-loader/release/LSPosed-v*-zygisk-debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "zygiskDebugName=$zygiskDebugName" >> $GITHUB_OUTPUT
91-
unzip magisk-loader/release/LSPosed-v*-zygisk-release.zip -d LSPosed-zygisk-release
92-
unzip magisk-loader/release/LSPosed-v*-zygisk-debug.zip -d LSPosed-zygisk-debug
84+
zygiskReleaseName=`ls zygisk/release/Vector-v*-Release.zip | awk -F '(/|.zip)' '{print $3}'` && echo "zygiskReleaseName=$zygiskReleaseName" >> $GITHUB_OUTPUT
85+
zygiskDebugName=`ls zygisk/release/Vector-v*-Debug.zip | awk -F '(/|.zip)' '{print $3}'` && echo "zygiskDebugName=$zygiskDebugName" >> $GITHUB_OUTPUT
86+
unzip zygisk/release/Vector-v*-Release.zip -d Vector-Release
87+
unzip zygisk/release/Vector-v*-Debug.zip -d Vector-Debug
9388
9489
- name: Upload zygisk release
9590
uses: actions/upload-artifact@v6
9691
with:
9792
name: ${{ steps.prepareArtifact.outputs.zygiskReleaseName }}
98-
path: "./LSPosed-zygisk-release/*"
93+
path: "./Vector-Release/*"
9994

10095
- name: Upload zygisk debug
10196
uses: actions/upload-artifact@v6
10297
with:
10398
name: ${{ steps.prepareArtifact.outputs.zygiskDebugName }}
104-
path: "./LSPosed-zygisk-debug/*"
99+
path: "./Vector-Debug/*"
105100

106101
- name: Upload mappings
107102
uses: actions/upload-artifact@v6
108103
with:
109104
name: mappings
110105
path: |
111-
magisk-loader/build/outputs/mapping
106+
zygisk/build/outputs/mapping
112107
app/build/outputs/mapping
113108
114109
- name: Upload symbols
@@ -117,23 +112,3 @@ jobs:
117112
name: symbols
118113
path: build/symbols
119114

120-
- name: Post to channel
121-
if: ${{ success() && github.event_name != 'pull_request' && github.ref == 'refs/heads/master' && github.ref_type != 'tag' && inputs.post_telegram != 'false' }}
122-
env:
123-
CHANNEL_ID: ${{ secrets.CHANNEL_ID }}
124-
DISCUSSION_ID: ${{ secrets.DISCUSSION_ID }}
125-
TOPIC_ID: ${{ secrets.TOPIC_ID }}
126-
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
127-
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
128-
COMMIT_URL: ${{ github.event.head_commit.url }}
129-
run: |
130-
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
131-
OUTPUT="magisk-loader/release/"
132-
export riruRelease=$(find $OUTPUT -name "LSPosed-v*-riru-release.zip")
133-
export riruDebug=$(find $OUTPUT -name "LSPosed-v*-riru-debug.zip")
134-
export zygiskRelease=$(find $OUTPUT -name "LSPosed-v*-zygisk-release.zip")
135-
export zygiskDebug=$(find $OUTPUT -name "LSPosed-v*-zygisk-debug.zip")
136-
ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
137-
curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${CHANNEL_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruDebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskDebug%22%2C%22caption%22:${ESCAPED}%7D%5D" -F riruRelease="@$riruRelease" -F riruDebug="@$riruDebug" -F zygiskRelease="@$zygiskRelease" -F zygiskDebug="@$zygiskDebug"
138-
# curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${DISCUSSION_ID}&message_thread_id=${TOPIC_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FriruDebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FzygiskDebug%22%2C%22caption%22:${ESCAPED}%7D%5D" -F riruRelease="@$riruRelease" -F riruDebug="@$riruDebug" -F zygiskRelease="@$zygiskRelease" -F zygiskDebug="@$zygiskDebug"
139-
fi

build.gradle.kts

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
1-
/*
2-
* This file is part of LSPosed.
3-
*
4-
* LSPosed is free software: you can redistribute it and/or modify
5-
* it under the terms of the GNU General Public License as published by
6-
* the Free Software Foundation, either version 3 of the License, or
7-
* (at your option) any later version.
8-
*
9-
* LSPosed is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
* GNU General Public License for more details.
13-
*
14-
* You should have received a copy of the GNU General Public License
15-
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
16-
*
17-
* Copyright (C) 2021 - 2022 LSPosed Contributors
18-
*/
19-
201
import com.android.build.api.dsl.ApplicationDefaultConfig
212
import com.android.build.api.dsl.CommonExtension
223
import com.android.build.gradle.api.AndroidBasePlugin
234
import com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask
5+
import java.io.ByteArrayOutputStream
6+
import javax.inject.Inject
7+
import org.gradle.api.provider.ValueSource
8+
import org.gradle.api.provider.ValueSourceParameters
9+
import org.gradle.process.ExecOperations
2410

2511
plugins {
2612
alias(libs.plugins.lsplugin.cmaker)
@@ -32,43 +18,90 @@ plugins {
3218
alias(libs.plugins.ktfmt)
3319
}
3420

21+
/** A ValueSource that executes 'git rev-list --count' to get the total commit count. */
22+
abstract class GitCommitCountValueSource : ValueSource<String, ValueSourceParameters.None> {
23+
@get:Inject abstract val execOperations: ExecOperations
24+
25+
override fun obtain(): String {
26+
val output = ByteArrayOutputStream()
27+
val result =
28+
execOperations.exec {
29+
commandLine("git", "rev-list", "--count", "refs/remotes/origin/master")
30+
standardOutput = output
31+
isIgnoreExitValue = true
32+
}
33+
// Return the count if successful, otherwise a default of "1".
34+
return if (result.exitValue == 0 && output.toString().isNotBlank()) {
35+
output.toString().trim()
36+
} else {
37+
"1"
38+
}
39+
}
40+
}
41+
42+
/** A ValueSource that executes 'git tag' to get the latest version tag. */
43+
abstract class GitLatestTagValueSource : ValueSource<String, ValueSourceParameters.None> {
44+
@get:Inject abstract val execOperations: ExecOperations
45+
46+
override fun obtain(): String {
47+
val output = ByteArrayOutputStream()
48+
val result =
49+
execOperations.exec {
50+
commandLine("git", "tag", "--list", "--sort=-v:refname")
51+
standardOutput = output
52+
isIgnoreExitValue = true
53+
}
54+
// If successful, parse the first line. Provide a default if no tags are found.
55+
return if (result.exitValue == 0 && output.toString().isNotBlank()) {
56+
output.toString().lineSequence().first().removePrefix("v")
57+
} else {
58+
"1.0"
59+
}
60+
}
61+
}
62+
63+
// This defers the execution of the git commands and allows Gradle to cache the results.
64+
val versionCodeProvider by extra(providers.of(GitCommitCountValueSource::class.java) {})
65+
val versionNameProvider by extra(providers.of(GitLatestTagValueSource::class.java) {})
66+
67+
val repo = jgit.repo()
68+
val commitCount = (repo?.commitCount("refs/remotes/origin/master") ?: 1) + 4200
69+
val latestTag = repo?.latestTag?.removePrefix("v") ?: "1.0"
70+
71+
val injectedPackageName by extra("com.android.shell")
72+
val injectedPackageUid by extra(2000)
73+
74+
val defaultManagerPackageName by extra("org.lsposed.manager")
75+
val verCode by extra(commitCount)
76+
val verName by extra(latestTag)
77+
3578
cmaker {
3679
default {
3780
arguments.addAll(
81+
arrayOf("-DVECTOR_ROOT=${rootDir.absolutePath}", "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")
82+
)
83+
val flags =
3884
arrayOf(
39-
"-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "external")}",
40-
"-DCORE_ROOT=${File(rootDir.absolutePath, "core/src/main/jni")}",
41-
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
85+
"-DINJECTED_UID=$injectedPackageUid",
86+
"-DVERSION_CODE=${verCode}",
87+
"-DVERSION_NAME='\"${verName}\"'",
88+
"-Wno-gnu-string-literal-operator-template",
89+
"-Wno-c++2b-extensions",
4290
)
43-
)
44-
val flags = arrayOf(
45-
"-DINJECTED_AID=$injectedPackageUid",
46-
"-Wno-gnu-string-literal-operator-template",
47-
"-Wno-c++2b-extensions",
48-
)
4991
cFlags.addAll(flags)
5092
cppFlags.addAll(flags)
5193
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
5294
}
5395
buildTypes {
5496
if (it.name == "release") {
55-
arguments += "-DDEBUG_SYMBOLS_PATH=${
97+
arguments +=
98+
"-DDEBUG_SYMBOLS_PATH=${
5699
layout.buildDirectory.dir("symbols").get().asFile.absolutePath
57100
}"
58101
}
59102
}
60103
}
61104

62-
val repo = jgit.repo()
63-
val commitCount = (repo?.commitCount("refs/remotes/origin/master") ?: 1) + 4200
64-
val latestTag = repo?.latestTag?.removePrefix("v") ?: "1.0"
65-
66-
val injectedPackageName by extra("com.android.shell")
67-
val injectedPackageUid by extra(2000)
68-
69-
val defaultManagerPackageName by extra("org.lsposed.manager")
70-
val verCode by extra(commitCount)
71-
val verName by extra(latestTag)
72105
val androidTargetSdkVersion by extra(36)
73106
val androidMinSdkVersion by extra(27)
74107
val androidBuildToolsVersion by extra("36.0.0")
@@ -77,9 +110,7 @@ val androidCompileNdkVersion by extra("29.0.13113456")
77110
val androidSourceCompatibility by extra(JavaVersion.VERSION_21)
78111
val androidTargetCompatibility by extra(JavaVersion.VERSION_21)
79112

80-
tasks.register("Delete", Delete::class) {
81-
delete(rootProject.layout.buildDirectory)
82-
}
113+
tasks.register("Delete", Delete::class) { delete(rootProject.layout.buildDirectory) }
83114

84115
subprojects {
85116
plugins.withType(AndroidBasePlugin::class.java) {
@@ -127,6 +158,7 @@ tasks.register<KtfmtFormatTask>("format") {
127158
source = project.fileTree(rootDir)
128159
include("*.gradle.kts", "*/build.gradle.kts")
129160
dependsOn(":xposed:ktfmtFormat")
161+
dependsOn(":zygisk:ktfmtFormat")
130162
}
131163

132164
ktfmt { kotlinLangStyle() }

core/build.gradle.kts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,6 @@ android {
4848
}
4949
}
5050

51-
copy {
52-
from("src/main/jni/template/") {
53-
expand("VERSION_CODE" to "$verCode", "VERSION_NAME" to verName)
54-
}
55-
into("src/main/jni/src/")
56-
}
57-
5851
dependencies {
5952
api(projects.xposed)
6053
implementation(projects.external.apache)

core/src/main/java/android/content/res/XResources.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
package android.content.res;
2222

23-
import static org.lsposed.lspd.nativebridge.ResourcesHook.rewriteXmlReferencesNative;
23+
import static org.matrix.vector.nativebridge.ResourcesHook.rewriteXmlReferencesNative;
2424
import static de.robv.android.xposed.XposedHelpers.decrementMethodDepth;
2525
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
2626
import static de.robv.android.xposed.XposedHelpers.getBooleanField;

core/src/main/java/de/robv/android/xposed/XposedBridge.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727

2828
import org.lsposed.lspd.impl.LSPosedBridge;
2929
import org.lsposed.lspd.impl.LSPosedHookCallback;
30-
import org.lsposed.lspd.nativebridge.HookBridge;
31-
import org.lsposed.lspd.nativebridge.ResourcesHook;
30+
import org.matrix.vector.nativebridge.HookBridge;
31+
import org.matrix.vector.nativebridge.ResourcesHook;
3232

3333
import java.lang.reflect.AccessibleObject;
3434
import java.lang.reflect.Executable;

core/src/main/java/de/robv/android/xposed/XposedInit.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242

4343
import org.lsposed.lspd.impl.LSPosedContext;
4444
import org.lsposed.lspd.models.PreLoadedApk;
45-
import org.lsposed.lspd.nativebridge.NativeAPI;
46-
import org.lsposed.lspd.nativebridge.ResourcesHook;
45+
import org.matrix.vector.nativebridge.NativeAPI;
46+
import org.matrix.vector.nativebridge.ResourcesHook;
4747
import org.lsposed.lspd.util.LspModuleClassLoader;
4848
import org.lsposed.lspd.util.Utils.Log;
4949

core/src/main/java/org/lsposed/lspd/deopt/PrebuiltMethodsDeopter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_BOOT_IMAGE_MIUI_RES;
2525
import static org.lsposed.lspd.deopt.InlinedMethodCallers.KEY_SYSTEM_SERVER;
2626

27-
import org.lsposed.lspd.nativebridge.HookBridge;
27+
import org.matrix.vector.nativebridge.HookBridge;
2828
import org.lsposed.lspd.util.Hookers;
2929
import org.lsposed.lspd.util.Utils;
3030

core/src/main/java/org/lsposed/lspd/hooker/OpenDexFileHooker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import android.os.Build;
44

55
import org.lsposed.lspd.impl.LSPosedBridge;
6-
import org.lsposed.lspd.nativebridge.HookBridge;
6+
import org.matrix.vector.nativebridge.HookBridge;
77

88
import io.github.libxposed.api.XposedInterface;
99

core/src/main/java/org/lsposed/lspd/impl/LSPosedBridge.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import androidx.annotation.NonNull;
44

5-
import org.lsposed.lspd.nativebridge.HookBridge;
5+
import org.matrix.vector.nativebridge.HookBridge;
66
import org.lsposed.lspd.util.Utils.Log;
77

88
import java.lang.reflect.Executable;

core/src/main/java/org/lsposed/lspd/impl/LSPosedContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
import org.lsposed.lspd.core.BuildConfig;
1616
import org.lsposed.lspd.models.Module;
17-
import org.lsposed.lspd.nativebridge.HookBridge;
18-
import org.lsposed.lspd.nativebridge.NativeAPI;
17+
import org.matrix.vector.nativebridge.HookBridge;
18+
import org.matrix.vector.nativebridge.NativeAPI;
1919
import org.lsposed.lspd.service.ILSPInjectedModuleService;
2020
import org.lsposed.lspd.util.LspModuleClassLoader;
2121
import org.lsposed.lspd.util.Utils.Log;

0 commit comments

Comments
 (0)