You've already forked ZygiskNext
mirror of
https://github.com/Dr-TSNG/ZygiskNext.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33c4ea3c62 | ||
|
|
3eee57eb8f | ||
|
|
250b4b2f8c | ||
|
|
3772e23473 | ||
|
|
8c5acf1ebe | ||
|
|
9d0858be7c | ||
|
|
b7bed4ad35 | ||
|
|
80b19c4412 | ||
|
|
a6f455218f | ||
|
|
87cf885070 | ||
|
|
b775d28c23 | ||
|
|
bf72296d33 |
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Bug report/反馈 Bug
|
||||
description: Report errors or unexpected behavior./反馈错误或异常行为。
|
||||
labels: [bug]
|
||||
title: "[Bug] Short description/简单描述"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting issues of Zygisk on KernelSU!
|
||||
|
||||
To make it easier for us to help you please enter detailed information below.
|
||||
|
||||
感谢给 Zygisk on KernelSU 汇报问题!
|
||||
为了使我们更好地帮助你,请提供以下信息。
|
||||
为了防止重复汇报,标题请务必使用英文。
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce/复现步骤
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behaviour/预期行为
|
||||
placeholder: Tell us what should happen/正常情况下应该发生什么
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual behaviour/实际行为
|
||||
placeholder: Tell us what happens instead/实际上发生了什么
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: KernelSU Module List/KernelSU 模块列表
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Zygisk on KernelSU version/Zygisk on KernelSU 版本
|
||||
description: Don't use 'latest'. Specify actual version, otherwise your issue will be closed./不要填用“最新版”。给出具体版本号,不然 issue 会被关闭。
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Android version/Android 版本
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: KernelSU version/KernelSU 版本
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: latest
|
||||
attributes:
|
||||
label: Version requirement/版本要求
|
||||
options:
|
||||
- label: I am using latest debug CI version of Zygisk on KernelSU and enable verbose log/我正在使用最新 CI 调试版本 Zygisk on KernelSU 且启用详细日志
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs/日志
|
||||
description: For usage issues, please provide the log zip saved from KernelSU manager; for activation issues, please provide [bugreport](https://developer.android.com/studio/debug/bug-report). Without logs zip, the issue will be closed. /使用问题请提供从 KernelSU 管理器保存的日志压缩包;激活问题请提供 [bugreport](https://developer.android.google.cn/studio/debug/bug-report?hl=zh-cn) 日志。没有日志附件的问题会被关闭。
|
||||
placeholder: Upload logs zip by clicking the bar on the bottom. Upload logs to other websites or using external links is prohibited. /点击文本框底栏上传日志压缩包,禁止上传到其它网站或使用外链提供日志。
|
||||
validations:
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Feature request/新特性请求
|
||||
description: Suggest an idea./提出建议
|
||||
labels: [enhancement]
|
||||
title: "[Feature Request] Short description/简单描述"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem?/你的请求是否与某个问题相关?
|
||||
placeholder: A clear and concise description of what the problem is./请清晰准确表述该问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like/描述你想要的解决方案
|
||||
placeholder: A clear and concise description of what you want to happen./请清晰准确描述新特性的预期行为
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context/其他信息
|
||||
placeholder: Add any other context or screenshots about the feature request here./其他关于新特性的信息或者截图
|
||||
validations:
|
||||
required: false
|
||||
88
.github/workflows/ci.yml
vendored
Normal file
88
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
tags: [ v* ]
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
CCACHE_BASEDIR: "${{ github.workspace }}"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: "recursive"
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
- name: Setup rust-cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: zygiskd/src -> ../build/intermediates/rust
|
||||
cache-targets: false
|
||||
|
||||
- name: Setup Rust
|
||||
run: |
|
||||
rustup target add armv7-linux-androideabi
|
||||
rustup target add aarch64-linux-android
|
||||
rustup target add x86_64-linux-android
|
||||
rustup target add i686-linux-android
|
||||
|
||||
- name: Set up ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
max-size: 2G
|
||||
key: ${{ runner.os }}
|
||||
restore-keys: ${{ runner.os }}
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
echo 'org.gradle.parallel=true' >> gradle.properties
|
||||
echo 'org.gradle.vfs.watch=true' >> gradle.properties
|
||||
echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
|
||||
echo 'android.native.buildOutput=verbose' >> gradle.properties
|
||||
sed -i 's/org.gradle.unsafe.configuration-cache=true//g' gradle.properties
|
||||
./gradlew zipRelease
|
||||
./gradlew zipDebug
|
||||
|
||||
- name: Prepare artifact
|
||||
if: success()
|
||||
id: prepareArtifact
|
||||
run: |
|
||||
releaseName=`ls module/build/outputs/release/Zygisk-on-KernelSU-v*-release.zip | awk -F '(/|.zip)' '{print $5}'` && echo "releaseName=$releaseName" >> $GITHUB_OUTPUT
|
||||
debugName=`ls module/build/outputs/release/Zygisk-on-KernelSU-v*-debug.zip | awk -F '(/|.zip)' '{print $5}'` && echo "debugName=$debugName" >> $GITHUB_OUTPUT
|
||||
unzip module/build/outputs/release/Zygisk-on-KernelSU-v*-release.zip -d zksu-release
|
||||
unzip module/build/outputs/release/Zygisk-on-KernelSU-v*-debug.zip -d zksu-debug
|
||||
|
||||
- name: Upload release
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ steps.prepareArtifact.outputs.releaseName }}
|
||||
path: "./zksu-release/*"
|
||||
|
||||
- name: Upload debug
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ steps.prepareArtifact.outputs.debugName }}
|
||||
path: "./zksu-debug/*"
|
||||
23
.github/workflows/issue_moderator.yml
vendored
Normal file
23
.github/workflows/issue_moderator.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Issue moderator
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, reopened]
|
||||
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check issue
|
||||
uses: tachiyomiorg/issue-moderator-action@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
"type": "title",
|
||||
"regex": ".*(Short description|简单描述).*",
|
||||
"message": "You did not fill out the description in the title/你没有填写标题"
|
||||
}
|
||||
]
|
||||
auto-close-ignore-label: do-not-autoclose
|
||||
@@ -31,10 +31,10 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
|
||||
|
||||
val moduleId by extra("zygisksu")
|
||||
val moduleName by extra("Zygisk on KernelSU")
|
||||
val verName by extra("v4-0.6.2")
|
||||
val verName by extra("v4-0.6.5")
|
||||
val verCode by extra(gitCommitCount)
|
||||
val minKsuVersion by extra(10654)
|
||||
val minKsudVersion by extra(10670)
|
||||
val minKsuVersion by extra(10818)
|
||||
val minKsudVersion by extra(10818)
|
||||
val maxKsuVersion by extra(20000)
|
||||
val minMagiskVersion by extra(25208)
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonTransitiveRClass=true
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
import java.nio.file.Paths
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
}
|
||||
|
||||
fun Project.findInPath(executable: String, property: String): String? {
|
||||
val pathEnv = System.getenv("PATH")
|
||||
return pathEnv.split(File.pathSeparator).map { folder ->
|
||||
Paths.get("${folder}${File.separator}${executable}${if (OperatingSystem.current().isWindows) ".exe" else ""}")
|
||||
.toFile()
|
||||
}.firstOrNull { path ->
|
||||
path.exists()
|
||||
}?.absolutePath ?: properties.getOrDefault(property, null) as? String?
|
||||
}
|
||||
|
||||
val ccachePatch by lazy {
|
||||
project.findInPath("ccache", "ccache.path")?.also {
|
||||
println("loader: Use ccache: $it")
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
androidResources = false
|
||||
@@ -12,6 +31,16 @@ android {
|
||||
externalNativeBuild.ndkBuild {
|
||||
path("src/Android.mk")
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
ccachePatch?.let {
|
||||
arguments += "NDK_CCACHE=$it"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -8,19 +8,37 @@
|
||||
|
||||
namespace zygiskd {
|
||||
|
||||
bool sMagicRead = false;
|
||||
static std::string sSocketName;
|
||||
|
||||
void ReadMagic() {
|
||||
sMagicRead = true;
|
||||
char magic[PATH_MAX]{0};
|
||||
auto fp = fopen(kZygiskMagic, "r");
|
||||
if (fp == nullptr) {
|
||||
PLOGE("Open magic file");
|
||||
return;
|
||||
}
|
||||
fgets(magic, PATH_MAX, fp);
|
||||
fclose(fp);
|
||||
sSocketName.append(LP_SELECT("zygiskd32", "zygiskd64")).append(magic);
|
||||
LOGD("Socket name: %s", sSocketName.data());
|
||||
}
|
||||
|
||||
int Connect(uint8_t retry) {
|
||||
if (!sMagicRead) ReadMagic();
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
struct sockaddr_un addr{
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
strncpy(addr.sun_path + 1, kZygiskSocket.data(), kZygiskSocket.size());
|
||||
strcpy(addr.sun_path + 1, sSocketName.data());
|
||||
socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1;
|
||||
|
||||
while (retry--) {
|
||||
int r = connect(fd, reinterpret_cast<struct sockaddr*>(&addr), socklen);
|
||||
if (r == 0) return fd;
|
||||
LOGW("retrying to connect to zygiskd, sleep 1s");
|
||||
LOGW("Retrying to connect to zygiskd, sleep 1s");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
|
||||
1
loader/src/external/Android.mk
vendored
1
loader/src/external/Android.mk
vendored
@@ -16,5 +16,6 @@ include $(BUILD_STATIC_LIBRARY)
|
||||
# Header only library
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= libphmap
|
||||
LOCAL_CFLAGS := -Wno-unused-value
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/parallel-hashmap
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
2
loader/src/external/lsplt
vendored
2
loader/src/external/lsplt
vendored
Submodule loader/src/external/lsplt updated: b254b5b9a5...5d2b820cf9
2
loader/src/external/parallel-hashmap
vendored
2
loader/src/external/parallel-hashmap
vendored
Submodule loader/src/external/parallel-hashmap updated: 87ece91c6e...55725dbe70
@@ -26,14 +26,40 @@
|
||||
#define ZYGISK_API_VERSION 4
|
||||
|
||||
/*
|
||||
|
||||
***************
|
||||
* Introduction
|
||||
***************
|
||||
|
||||
On Android, all app processes are forked from a special daemon called "Zygote".
|
||||
For each new app process, zygote will fork a new process and perform "specialization".
|
||||
This specialization operation enforces the Android security sandbox on the newly forked
|
||||
process to make sure that 3rd party application code is only loaded after it is being
|
||||
restricted within a sandbox.
|
||||
|
||||
On Android, there is also this special process called "system_server". This single
|
||||
process hosts a significant portion of system services, which controls how the
|
||||
Android operating system and apps interact with each other.
|
||||
|
||||
The Zygisk framework provides a way to allow developers to build modules and run custom
|
||||
code before and after system_server and any app processes' specialization.
|
||||
This enable developers to inject code and alter the behavior of system_server and app processes.
|
||||
|
||||
Please note that modules will only be loaded after zygote has forked the child process.
|
||||
THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON!
|
||||
|
||||
*********************
|
||||
* Development Guide
|
||||
*********************
|
||||
|
||||
Define a class and inherit zygisk::ModuleBase to implement the functionality of your module.
|
||||
Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk.
|
||||
Please note that modules will only be loaded after zygote has forked the child process.
|
||||
THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM SERVER PROCESS, NOT THE ZYGOTE DAEMON!
|
||||
|
||||
Example code:
|
||||
|
||||
static jint (*orig_logger_entry_max)(JNIEnv *env);
|
||||
static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
|
||||
static void example_handler(int socket) { ... }
|
||||
|
||||
class ExampleModule : public zygisk::ModuleBase {
|
||||
public:
|
||||
void onLoad(zygisk::Api *api, JNIEnv *env) override {
|
||||
@@ -51,8 +77,26 @@ private:
|
||||
zygisk::Api *api;
|
||||
JNIEnv *env;
|
||||
};
|
||||
|
||||
REGISTER_ZYGISK_MODULE(ExampleModule)
|
||||
|
||||
-----------------------------------------------------------------------------------------
|
||||
|
||||
Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize,
|
||||
or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class
|
||||
never runs in a true superuser environment.
|
||||
|
||||
If your module require access to superuser permissions, you can create and register
|
||||
a root companion handler function. This function runs in a separate root companion
|
||||
daemon process, and an Unix domain socket is provided to allow you to perform IPC between
|
||||
your target process and the root companion process.
|
||||
|
||||
Example code:
|
||||
|
||||
static void example_handler(int socket) { ... }
|
||||
|
||||
REGISTER_ZYGISK_COMPANION(example_handler)
|
||||
|
||||
*/
|
||||
|
||||
namespace zygisk {
|
||||
@@ -84,7 +128,7 @@ namespace zygisk {
|
||||
|
||||
// This method is called after the app process is specialized.
|
||||
// At this point, the process has all sandbox restrictions enabled for this application.
|
||||
// This means that this method runs as the same privilege of the app's own code.
|
||||
// This means that this method runs with the same privilege of the app's own code.
|
||||
virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
|
||||
|
||||
// This method is called before the system server process is specialized.
|
||||
@@ -219,7 +263,16 @@ namespace zygisk {
|
||||
// will be set to nullptr.
|
||||
void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
|
||||
|
||||
// For ELFs loaded in memory matching `inode`, replace function `symbol` with `newFunc`.
|
||||
// Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory.
|
||||
//
|
||||
// Parsing /proc/[PID]/maps will give you the memory map of a process. As an example:
|
||||
//
|
||||
// <address> <perms> <offset> <dev> <inode> <pathname>
|
||||
// 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64
|
||||
// (More details: https://man7.org/linux/man-pages/man5/proc.5.html)
|
||||
//
|
||||
// The `dev` and `inode` pair uniquely identifies a file being mapped into memory.
|
||||
// For matching ELFs loaded in memory, replace function `symbol` with `newFunc`.
|
||||
// If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
|
||||
void pltHookRegister(dev_t dev, ino_t inode, const char *symbol, void *newFunc, void **oldFunc);
|
||||
|
||||
@@ -243,11 +296,11 @@ void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
|
||||
//
|
||||
// The function runs in a superuser daemon process and handles a root companion request from
|
||||
// your module running in a target process. The function has to accept an integer value,
|
||||
// which is a socket that is connected to the target process.
|
||||
// which is a Unix domain socket that is connected to the target process.
|
||||
// See Api::connectCompanion() for more info.
|
||||
//
|
||||
// NOTE: the function can run concurrently on multiple threads.
|
||||
// Be aware of race conditions if you have a globally shared resource.
|
||||
// Be aware of race conditions if you have globally shared resources.
|
||||
|
||||
#define REGISTER_ZYGISK_COMPANION(func) \
|
||||
void zygisk_companion_entry(int client) { func(client); }
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
#else
|
||||
# define LP_SELECT(lp32, lp64) lp32
|
||||
#endif
|
||||
constexpr std::string_view kZygiskSocket = LP_SELECT("zygiskd32", "zygiskd64") "socket_placeholder";
|
||||
|
||||
constexpr auto kZygiskMagic = "/system/zygisk_magic";
|
||||
|
||||
class UniqueFd {
|
||||
using Fd = int;
|
||||
|
||||
@@ -331,6 +331,7 @@ bool ZygiskModule::RegisterModuleImpl(ApiTable *api, long *module) {
|
||||
api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); };
|
||||
}
|
||||
if (api_version >= 4) {
|
||||
api->v4.pltHookCommit = lsplt::CommitHook;
|
||||
api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) {
|
||||
if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr)
|
||||
return;
|
||||
|
||||
@@ -10,12 +10,8 @@ using namespace std::string_view_literals;
|
||||
|
||||
namespace {
|
||||
constexpr auto MODULE_DIR = "/data/adb/modules";
|
||||
|
||||
struct overlay_backup {
|
||||
std::string target;
|
||||
std::string vfs_option;
|
||||
std::string fs_option;
|
||||
};
|
||||
constexpr auto KSU_OVERLAY_SOURCE = "KSU";
|
||||
const std::vector<std::string> KSU_PARTITIONS{"/system", "/vendor", "/product", "/system_ext", "/odm", "/oem"};
|
||||
|
||||
void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1) {
|
||||
@@ -26,16 +22,9 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
#define PARSE_OPT(name, flag) \
|
||||
if (opt == (name)) { \
|
||||
flags |= (flag); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
void revert_unmount_ksu() {
|
||||
std::string ksu_loop;
|
||||
std::vector<std::string> targets;
|
||||
std::list<overlay_backup> backups;
|
||||
|
||||
// Unmount ksu module dir last
|
||||
targets.emplace_back(MODULE_DIR);
|
||||
@@ -50,17 +39,10 @@ void revert_unmount_ksu() {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
// Unmount ksu overlays
|
||||
if (info.type == "overlay") {
|
||||
if (str_contains(info.fs_option, MODULE_DIR)) {
|
||||
targets.emplace_back(info.target);
|
||||
} else {
|
||||
auto backup = overlay_backup{
|
||||
.target = info.target,
|
||||
.vfs_option = info.vfs_option,
|
||||
.fs_option = info.fs_option,
|
||||
};
|
||||
backups.emplace_back(backup);
|
||||
}
|
||||
if (info.type == "overlay"
|
||||
&& info.source == KSU_OVERLAY_SOURCE
|
||||
&& std::find(KSU_PARTITIONS.begin(), KSU_PARTITIONS.end(), info.target) != KSU_PARTITIONS.end()) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
@@ -74,34 +56,6 @@ void revert_unmount_ksu() {
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
|
||||
// Affirm unmounted system overlays
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.type == "overlay") {
|
||||
backups.remove_if([&](overlay_backup& mnt) {
|
||||
return mnt.target == info.target && mnt.fs_option == info.fs_option;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Restore system overlays
|
||||
for (auto& mnt: backups) {
|
||||
auto opts = split_str(mnt.vfs_option, ",");
|
||||
opts.splice(opts.end(), split_str(mnt.fs_option, ","));
|
||||
unsigned long flags = 0;
|
||||
opts.remove_if([&](auto& opt) {
|
||||
PARSE_OPT(MNTOPT_RO, MS_RDONLY)
|
||||
PARSE_OPT(MNTOPT_NOSUID, MS_NOSUID)
|
||||
PARSE_OPT("relatime", MS_RELATIME)
|
||||
return false;
|
||||
});
|
||||
auto mnt_data = join_str(opts, ",");
|
||||
if (mount("overlay", mnt.target.data(), "overlay", flags, mnt_data.data()) != -1) {
|
||||
LOGD("Remounted (%s)", mnt.target.data());
|
||||
} else {
|
||||
PLOGE("Remount (%s, %s)", mnt.target.data(), mnt.fs_option.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_magisk() {
|
||||
|
||||
@@ -82,10 +82,6 @@ extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
||||
|
||||
if [ "$KSU" ]; then
|
||||
ui_print "- Checking SELinux patches"
|
||||
if [ "$(getprop ro.product.first_api_level)" -lt 31 ]; then
|
||||
echo "allow zygote appdomain_tmpfs file *" >> "$TMPDIR/sepolicy.rule"
|
||||
echo "allow zygote appdomain_tmpfs dir *" >> "$TMPDIR/sepolicy.rule"
|
||||
fi
|
||||
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Unable to apply SELinux patches!"
|
||||
@@ -146,20 +142,9 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $DEBUG = false ]; then
|
||||
ui_print "- Hex patching"
|
||||
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
||||
if [ "$HAS32BIT" = true ]; then
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygisk_injector.so"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygisk_loader.so"
|
||||
fi
|
||||
if [ "$HAS64BIT" = true ]; then
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygisk_injector.so"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygisk_loader.so"
|
||||
fi
|
||||
fi
|
||||
ui_print "- Generating magic"
|
||||
MAGIC=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
||||
echo -n "$MAGIC" > "$MODPATH/system/zygisk_magic"
|
||||
|
||||
ui_print "- Setting permissions"
|
||||
chmod 0744 "$MODPATH/daemon.sh"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
allow * tmpfs * *
|
||||
allow zygote appdomain_tmpfs dir *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
|
||||
type magisk_file file_type
|
||||
typeattribute magisk_file mlstrustedobject
|
||||
@@ -14,3 +16,4 @@ allow zygote adb_data_file dir search
|
||||
allow zygote mnt_vendor_file dir search
|
||||
allow zygote system_file dir mounton
|
||||
allow zygote labeledfs filesystem mount
|
||||
allow zygote fs_type filesystem unmount
|
||||
|
||||
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("com.android.library") version "7.4.1"
|
||||
id("com.android.application") version "7.4.1"
|
||||
id("com.android.library") version "7.4.2"
|
||||
id("com.android.application") version "7.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,12 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
|
||||
pub const PROP_CTL_RESTART: &str = "ctl.restart";
|
||||
pub const ZYGISK_LOADER: &str = "libzygisk_loader.so";
|
||||
|
||||
pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder";
|
||||
pub const ZYGISK_MAGIC: &str = "/system/zygisk_magic";
|
||||
|
||||
pub const PATH_MODULES_DIR: &str = "..";
|
||||
pub const PATH_MODULE_PROP: &str = "module.prop";
|
||||
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
||||
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
||||
pub const PATH_TMP_DIR: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER);
|
||||
pub const PATH_TMP_PROP: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER, "/module.prop");
|
||||
|
||||
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
|
||||
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
|
||||
|
||||
19
zygiskd/src/magic.rs
Normal file
19
zygiskd/src/magic.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use std::fs;
|
||||
use anyhow::Result;
|
||||
use crate::constants;
|
||||
use crate::utils::LateInit;
|
||||
|
||||
pub static MAGIC: LateInit<String> = LateInit::new();
|
||||
pub static PATH_TMP_DIR: LateInit<String> = LateInit::new();
|
||||
pub static PATH_TMP_PROP: LateInit<String> = LateInit::new();
|
||||
|
||||
pub fn setup() -> Result<()> {
|
||||
let name = fs::read_to_string(constants::ZYGISK_MAGIC)?;
|
||||
let path_tmp_dir = format!("/dev/{}", name);
|
||||
let path_tmp_prop = format!("{}/module.prop", path_tmp_dir);
|
||||
|
||||
MAGIC.init(name);
|
||||
PATH_TMP_DIR.init(path_tmp_dir);
|
||||
PATH_TMP_PROP.init(path_tmp_prop);
|
||||
Ok(())
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
mod companion;
|
||||
mod constants;
|
||||
mod dl;
|
||||
mod magic;
|
||||
mod root_impl;
|
||||
mod utils;
|
||||
mod watchdog;
|
||||
@@ -39,6 +40,7 @@ fn init_android_logger(tag: &str) {
|
||||
|
||||
fn start() -> Result<()> {
|
||||
root_impl::setup();
|
||||
magic::setup()?;
|
||||
let cli = Args::parse();
|
||||
match cli.command {
|
||||
Commands::Watchdog => watchdog::entry()?,
|
||||
|
||||
@@ -5,7 +5,6 @@ const KERNEL_SU_OPTION: i32 = 0xdeadbeefu32 as i32;
|
||||
|
||||
const CMD_GET_VERSION: usize = 2;
|
||||
const CMD_GET_ALLOW_LIST: usize = 5;
|
||||
const CMD_GET_DENY_LIST: usize = 6;
|
||||
|
||||
pub enum Version {
|
||||
Supported,
|
||||
@@ -24,6 +23,7 @@ pub fn get_kernel_su() -> Option<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
let mut size = 1024u32;
|
||||
let mut uids = vec![0; size as usize];
|
||||
@@ -32,10 +32,7 @@ pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
uids.contains(&uid)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||
let mut size = 1024u32;
|
||||
let mut uids = vec![0; size as usize];
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_DENY_LIST, uids.as_mut_ptr(), &mut size as *mut u32) };
|
||||
uids.resize(size as usize, 0);
|
||||
uids.contains(&uid)
|
||||
false
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub fn get_magisk() -> Option<Version> {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
let output: Option<String> = Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
@@ -40,6 +41,7 @@ pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||
// TODO: uid_on_denylist
|
||||
return false;
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::ffi::c_char;
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::os::unix::net::UnixListener;
|
||||
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
|
||||
use once_cell::sync::OnceCell;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
@@ -18,6 +19,27 @@ macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp32 };
|
||||
}
|
||||
|
||||
pub struct LateInit<T> {
|
||||
cell: OnceCell<T>,
|
||||
}
|
||||
|
||||
impl<T> LateInit<T> {
|
||||
pub const fn new() -> Self {
|
||||
LateInit { cell: OnceCell::new() }
|
||||
}
|
||||
|
||||
pub fn init(&self, value: T) {
|
||||
assert!(self.cell.set(value).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for LateInit<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
self.cell.get().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_string() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{constants, root_impl, utils};
|
||||
use crate::{constants, magic, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use nix::unistd::{getgid, getuid, Pid};
|
||||
use std::process::{Child, Command};
|
||||
@@ -12,10 +12,10 @@ use binder::IBinder;
|
||||
use nix::errno::Errno;
|
||||
use nix::libc;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use once_cell::sync::OnceCell;
|
||||
use crate::utils::LateInit;
|
||||
|
||||
static LOCK: OnceCell<UnixListener> = OnceCell::new();
|
||||
static PROP_SECTIONS: OnceCell<[String; 2]> = OnceCell::new();
|
||||
static LOCK: LateInit<UnixListener> = LateInit::new();
|
||||
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
||||
|
||||
pub fn entry() -> Result<()> {
|
||||
log::info!("Start zygisksu watchdog");
|
||||
@@ -55,9 +55,9 @@ fn check_permission() -> Result<()> {
|
||||
|
||||
fn ensure_single_instance() -> Result<()> {
|
||||
log::info!("Ensure single instance");
|
||||
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
|
||||
let name = format!("zygiskwd{}", magic::MAGIC.as_str());
|
||||
match utils::abstract_namespace_socket(&name) {
|
||||
Ok(socket) => { let _ = LOCK.set(socket); }
|
||||
Ok(socket) => LOCK.init(socket),
|
||||
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
|
||||
}
|
||||
Ok(())
|
||||
@@ -91,15 +91,15 @@ fn mount_prop() -> Result<()> {
|
||||
sections[section].push('\n');
|
||||
}
|
||||
}
|
||||
let _ = PROP_SECTIONS.set(sections);
|
||||
PROP_SECTIONS.init(sections);
|
||||
|
||||
fs::create_dir(constants::PATH_TMP_DIR)?;
|
||||
fs::File::create(constants::PATH_TMP_PROP)?;
|
||||
fs::create_dir(magic::PATH_TMP_DIR.as_str())?;
|
||||
fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
||||
|
||||
// FIXME: sys_mount cannot be compiled on 32 bit
|
||||
unsafe {
|
||||
let r = libc::mount(
|
||||
CString::new(constants::PATH_TMP_PROP)?.as_ptr(),
|
||||
CString::new(magic::PATH_TMP_PROP.as_str())?.as_ptr(),
|
||||
CString::new(module_prop)?.as_ptr(),
|
||||
std::ptr::null(),
|
||||
libc::MS_BIND,
|
||||
@@ -112,13 +112,12 @@ fn mount_prop() -> Result<()> {
|
||||
}
|
||||
|
||||
fn set_prop_hint(hint: &str) -> Result<()> {
|
||||
let mut file = fs::File::create(constants::PATH_TMP_PROP)?;
|
||||
let sections = PROP_SECTIONS.get().unwrap();
|
||||
file.write_all(sections[0].as_bytes())?;
|
||||
let mut file = fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
||||
file.write_all(PROP_SECTIONS[0].as_bytes())?;
|
||||
file.write_all(b"[")?;
|
||||
file.write_all(hint.as_bytes())?;
|
||||
file.write_all(b"] ")?;
|
||||
file.write_all(sections[1].as_bytes())?;
|
||||
file.write_all(PROP_SECTIONS[1].as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::constants::DaemonSocketAction;
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::{constants, lp_select, root_impl, utils};
|
||||
use crate::{constants, lp_select, magic, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use memfd::Memfd;
|
||||
use nix::{
|
||||
@@ -135,7 +135,7 @@ fn create_memfd(so_path: &PathBuf) -> Result<Memfd> {
|
||||
fn create_daemon_socket() -> Result<UnixListener> {
|
||||
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||
let prefix = lp_select!("zygiskd32", "zygiskd64");
|
||||
let name = String::from(prefix) + constants::SOCKET_PLACEHOLDER;
|
||||
let name = format!("{}{}", prefix, magic::MAGIC.as_str());
|
||||
let listener = utils::abstract_namespace_socket(&name)?;
|
||||
log::debug!("Daemon socket: {name}");
|
||||
Ok(listener)
|
||||
@@ -198,7 +198,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
match root_impl::get_impl() {
|
||||
root_impl::RootImpl::KernelSU => flags |= constants::PROCESS_ROOT_IS_KSU,
|
||||
root_impl::RootImpl::Magisk => flags |= constants::PROCESS_ROOT_IS_MAGISK,
|
||||
_ => ()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// TODO: PROCESS_IS_SYSUI?
|
||||
stream.write_u32(flags)?;
|
||||
|
||||
Reference in New Issue
Block a user