You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abbca19c82 | ||
|
|
4587e39964 | ||
|
|
9df4fb64cd | ||
|
|
9f28e0a7ad | ||
|
|
8a80586fb8 | ||
|
|
218659dcbf | ||
|
|
8c0d5b5395 | ||
|
|
9051f59bf6 | ||
|
|
5f2dd50703 | ||
|
|
77cb323506 | ||
|
|
2b41a8336c | ||
|
|
e730ccd9b2 | ||
|
|
db47f03728 | ||
|
|
42503e7cfe | ||
|
|
14c920c553 | ||
|
|
ceaa2d431c | ||
|
|
ccb5764b72 | ||
|
|
e8958e94b6 | ||
|
|
0338cdb0ed | ||
|
|
99e653c576 | ||
|
|
7c27c32861 | ||
|
|
bea5ed47b8 | ||
|
|
954a712089 | ||
|
|
f6195ddb43 | ||
|
|
8b5e9db347 | ||
|
|
a04f636ac4 | ||
|
|
00f0a6e3fa | ||
|
|
f5bf82fa93 | ||
|
|
33c4ea3c62 | ||
|
|
3eee57eb8f | ||
|
|
250b4b2f8c | ||
|
|
3772e23473 | ||
|
|
8c5acf1ebe | ||
|
|
9d0858be7c | ||
|
|
b7bed4ad35 | ||
|
|
80b19c4412 | ||
|
|
a6f455218f | ||
|
|
87cf885070 | ||
|
|
b775d28c23 | ||
|
|
bf72296d33 | ||
|
|
9d648d9aa4 | ||
|
|
843086f6f3 | ||
|
|
49e3ac9d7a | ||
|
|
446ed92f26 | ||
|
|
2e9cbf79a7 | ||
|
|
cce8e6686f | ||
|
|
ff2658f2de | ||
|
|
f465cbf810 | ||
|
|
09b6673ab0 | ||
|
|
5f8eb4af09 | ||
|
|
8affc8f991 | ||
|
|
ec8475bca5 | ||
|
|
9ff1e27a7d | ||
|
|
6d9cc560cc | ||
|
|
f395cfb490 | ||
|
|
03575edd96 | ||
|
|
915749e59b | ||
|
|
d08b415577 | ||
|
|
f27aed5068 | ||
|
|
5365ab1f12 | ||
|
|
b99d042002 | ||
|
|
57d3d8a0ba | ||
|
|
e69aa5c527 | ||
|
|
9b5eb1bac7 |
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
73
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
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.
|
||||
Note: We will **NEVER** handle any issue related to root detection or writable system.
|
||||
|
||||
感谢给 Zygisk on KernelSU 汇报问题!
|
||||
为了使我们更好地帮助你,请提供以下信息。
|
||||
为了防止重复汇报,标题请务必使用英文。
|
||||
请注意:我们**不会**处理任何有关 root 检测和 system 分区可写相关的问题。
|
||||
- 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
|
||||
29
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
29
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Feature request/新特性请求
|
||||
description: Suggest an idea./提出建议
|
||||
labels: [enhancement]
|
||||
title: "[Feature Request] Short description/简单描述"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Note: We will **NEVER** handle any issue related to root detection or writable system.
|
||||
|
||||
请注意:我们**不会**处理任何有关 root 检测和 system 分区可写相关的问题。
|
||||
- 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
|
||||
87
.github/workflows/ci.yml
vendored
Normal file
87
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
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 override set nightly
|
||||
rustup target add aarch64-linux-android
|
||||
rustup target add x86_64-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
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -1,9 +1,6 @@
|
||||
[submodule "loader/src/external/liblsplt"]
|
||||
path = loader/src/external/liblsplt
|
||||
url = https://github.com/LSPosed/LSPlt
|
||||
[submodule "loader/src/external/lsplt"]
|
||||
path = loader/src/external/lsplt
|
||||
url = https://github.com/LSPosed/lsplt
|
||||
[submodule "loader/src/external/parallel-hashmap"]
|
||||
path = loader/src/external/parallel-hashmap
|
||||
url = https://github.com/greg7mdp/parallel-hashmap
|
||||
[submodule "zygiskd/src/external/binder_rs"]
|
||||
path = zygiskd/src/external/binder_rs
|
||||
url = https://github.com/Kernel-SU/binder_rs
|
||||
|
||||
31
README.md
31
README.md
@@ -2,25 +2,26 @@
|
||||
|
||||
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
|
||||
|
||||
Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD. (Coming soon)
|
||||
Also works as standalone loader for Magisk.
|
||||
|
||||
## Requirements
|
||||
|
||||
+ Minimal KernelSU version: 10575
|
||||
+ Minimal ksud version: 10616
|
||||
+ Full SELinux patch support (If non-gki kernel)
|
||||
### General
|
||||
|
||||
+ No multiple root implementation installed
|
||||
+ SELinux enforcing: We now rely on SELinux to prevent `vold` from aborting our fuse connection
|
||||
|
||||
### KernelSU
|
||||
|
||||
+ Minimal KernelSU version: 10940
|
||||
+ Minimal ksud version: 10942
|
||||
+ Kernel has full SELinux patch support
|
||||
|
||||
### Magisk
|
||||
|
||||
+ Minimal version: 26300
|
||||
+ Original Zygisk turned off
|
||||
|
||||
## Compatibility
|
||||
|
||||
Should work with everything except those rely on Magisk internal behaviors.
|
||||
|
||||
## Development road map
|
||||
|
||||
- [x] [Inject] Basic Zygisk loader
|
||||
- [x] [Inject] Stabilize injector
|
||||
- [x] [Inject] Unload
|
||||
- [x] [Daemon] Linker namespace
|
||||
- [x] [Daemon] Separate zygiskd process
|
||||
- [x] [Daemon] Handle 64 bit only devices
|
||||
- [x] [Daemon] Handle zygote death
|
||||
- [ ] [ Misc ] Support Magisk out of box
|
||||
|
||||
@@ -2,18 +2,7 @@ import com.android.build.gradle.LibraryExtension
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("com.android.application") apply false
|
||||
id("com.android.library") apply false
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r")
|
||||
classpath("org.mozilla.rust-android-gradle:plugin:0.9.3")
|
||||
}
|
||||
alias(libs.plugins.agp.lib) apply false
|
||||
}
|
||||
|
||||
fun String.execute(currentWorkingDir: File = file("./")): String {
|
||||
@@ -31,14 +20,19 @@ 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.4.0")
|
||||
val verName by extra("v4-0.8.0")
|
||||
val verCode by extra(gitCommitCount)
|
||||
val commitHash by extra(gitCommitHash)
|
||||
val minKsuVersion by extra(10940)
|
||||
val minKsudVersion by extra(10942)
|
||||
val maxKsuVersion by extra(20000)
|
||||
val minMagiskVersion by extra(26300)
|
||||
|
||||
val androidMinSdkVersion by extra(29)
|
||||
val androidTargetSdkVersion by extra(33)
|
||||
val androidCompileSdkVersion by extra(33)
|
||||
val androidBuildToolsVersion by extra("33.0.2")
|
||||
val androidCompileNdkVersion by extra("25.2.9519653")
|
||||
val androidTargetSdkVersion by extra(34)
|
||||
val androidCompileSdkVersion by extra(34)
|
||||
val androidBuildToolsVersion by extra("34.0.0")
|
||||
val androidCompileNdkVersion by extra("26.0.10792818")
|
||||
val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
|
||||
val androidTargetCompatibility by extra(JavaVersion.VERSION_11)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
9
gradle/libs.versions.toml
Normal file
9
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[versions]
|
||||
agp = "8.1.2"
|
||||
kotlin = "1.9.10"
|
||||
|
||||
[plugins]
|
||||
agp-lib = { id = "com.android.library", version.ref = "agp" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
lsplugin-jgit = { id = "org.lsposed.lsplugin.jgit", version = "1.1" }
|
||||
rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version = "0.9.3" }
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import java.nio.file.Paths
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
alias(libs.plugins.agp.lib)
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -12,6 +31,17 @@ android {
|
||||
externalNativeBuild.ndkBuild {
|
||||
path("src/Android.mk")
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
abiFilters("arm64-v8a", "x86_64")
|
||||
ccachePatch?.let {
|
||||
arguments += "NDK_CCACHE=$it"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -13,16 +13,7 @@ LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := zygiskloader
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/loader))
|
||||
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
|
||||
LOCAL_STATIC_LIBRARIES := cxx common
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := injector
|
||||
LOCAL_MODULE := zygisk
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/injector))
|
||||
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
|
||||
|
||||
@@ -14,13 +14,13 @@ namespace zygiskd {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
strncpy(addr.sun_path + 1, kZygiskSocket.data(), kZygiskSocket.size());
|
||||
socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1;
|
||||
strcpy(addr.sun_path, kCPSocketPath);
|
||||
socklen_t socklen = sizeof(addr);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -48,14 +48,15 @@ namespace zygiskd {
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::string ReadNativeBridge() {
|
||||
uint32_t GetProcessFlags(uid_t uid) {
|
||||
UniqueFd fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("ReadNativeBridge");
|
||||
return "";
|
||||
PLOGE("GetProcessFlags");
|
||||
return 0;
|
||||
}
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::ReadNativeBridge);
|
||||
return socket_utils::read_string(fd);
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
|
||||
socket_utils::write_u32(fd, uid);
|
||||
return socket_utils::read_u32(fd);
|
||||
}
|
||||
|
||||
std::vector<Module> ReadModules() {
|
||||
@@ -86,6 +87,7 @@ namespace zygiskd {
|
||||
if (socket_utils::read_u8(fd) == 1) {
|
||||
return fd;
|
||||
} else {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,17 @@ void* DlopenExt(const char* path, int flags) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
void* DlopenMem(int memfd, int flags) {
|
||||
char path[PATH_MAX];
|
||||
sprintf(path, "/proc/self/fd/%d", memfd);
|
||||
return DlopenExt(path, flags);
|
||||
void* DlopenMem(int fd, int flags) {
|
||||
auto info = android_dlextinfo{
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fd
|
||||
};
|
||||
|
||||
auto* handle = android_dlopen_ext("/jit-cache", flags, &info);
|
||||
if (handle) {
|
||||
LOGD("dlopen fd %d: %p", fd, handle);
|
||||
} else {
|
||||
LOGE("dlopen fd %d: %s", fd, dlerror());
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,10 @@ namespace socket_utils {
|
||||
return read_exact_or<uint8_t>(fd, 0);
|
||||
}
|
||||
|
||||
uint32_t read_u32(int fd) {
|
||||
return read_exact_or<uint32_t>(fd, 0);
|
||||
}
|
||||
|
||||
size_t read_usize(int fd) {
|
||||
return read_exact_or<size_t>(fd, 0);
|
||||
}
|
||||
@@ -110,6 +114,10 @@ namespace socket_utils {
|
||||
return write_exact<uint8_t>(fd, val);
|
||||
}
|
||||
|
||||
bool write_u32(int fd, uint32_t val) {
|
||||
return write_exact<uint32_t>(fd, val);
|
||||
}
|
||||
|
||||
bool write_string(int fd, std::string_view str) {
|
||||
return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size());
|
||||
}
|
||||
|
||||
9
loader/src/external/Android.mk
vendored
9
loader/src/external/Android.mk
vendored
@@ -3,18 +3,19 @@ LOCAL_PATH := $(call my-dir)
|
||||
# liblsplt.a
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE:= liblsplt
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/liblsplt/lsplt/src/main/jni/include
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/lsplt/lsplt/src/main/jni/include
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden
|
||||
LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden -DLOG_DISABLED
|
||||
LOCAL_CPPFLAGS := -std=c++20
|
||||
LOCAL_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_SRC_FILES := \
|
||||
liblsplt/lsplt/src/main/jni/elf_util.cc \
|
||||
liblsplt/lsplt/src/main/jni/lsplt.cc
|
||||
lsplt/lsplt/src/main/jni/elf_util.cc \
|
||||
lsplt/lsplt/src/main/jni/lsplt.cc
|
||||
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)
|
||||
|
||||
1
loader/src/external/liblsplt
vendored
1
loader/src/external/liblsplt
vendored
Submodule loader/src/external/liblsplt deleted from b254b5b9a5
1
loader/src/external/lsplt
vendored
Submodule
1
loader/src/external/lsplt
vendored
Submodule
Submodule loader/src/external/lsplt added at 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); }
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__LP64__)
|
||||
# define LP_SELECT(lp32, lp64) lp64
|
||||
#else
|
||||
# define LP_SELECT(lp32, lp64) lp32
|
||||
#endif
|
||||
constexpr std::string_view kZygiskSocket = LP_SELECT("zygiskd32", "zygiskd64") "socket_placeholder";
|
||||
constexpr auto kCPSocketPath = "/dev/zygisk/cp.sock";
|
||||
|
||||
class UniqueFd {
|
||||
using Fd = int;
|
||||
@@ -19,7 +14,7 @@ public:
|
||||
|
||||
UniqueFd(Fd fd) : fd_(fd) {}
|
||||
|
||||
~UniqueFd() { close(fd_); }
|
||||
~UniqueFd() { if (fd_ >= 0) close(fd_); }
|
||||
|
||||
// Disallow copy
|
||||
UniqueFd(const UniqueFd&) = delete;
|
||||
@@ -53,7 +48,7 @@ namespace zygiskd {
|
||||
enum class SocketAction {
|
||||
PingHeartBeat,
|
||||
RequestLogcatFd,
|
||||
ReadNativeBridge,
|
||||
GetProcessFlags,
|
||||
ReadModules,
|
||||
RequestCompanionSocket,
|
||||
GetModuleDir,
|
||||
@@ -63,10 +58,10 @@ namespace zygiskd {
|
||||
|
||||
int RequestLogcatFd();
|
||||
|
||||
std::string ReadNativeBridge();
|
||||
|
||||
std::vector<Module> ReadModules();
|
||||
|
||||
uint32_t GetProcessFlags(uid_t uid);
|
||||
|
||||
int ConnectCompanion(size_t index);
|
||||
|
||||
int GetModuleDir(size_t index);
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LOG_TAG
|
||||
#if defined(__LP64__)
|
||||
# define LOG_TAG "zygisksu64"
|
||||
#else
|
||||
# define LOG_TAG "zygisksu32"
|
||||
#endif
|
||||
#define LOG_TAG "zygisk-core"
|
||||
#endif
|
||||
|
||||
#ifdef LOG_DISABLED
|
||||
|
||||
@@ -13,12 +13,16 @@ namespace socket_utils {
|
||||
|
||||
uint8_t read_u8(int fd);
|
||||
|
||||
uint32_t read_u32(int fd);
|
||||
|
||||
size_t read_usize(int fd);
|
||||
|
||||
std::string read_string(int fd);
|
||||
|
||||
bool write_u8(int fd, uint8_t val);
|
||||
|
||||
bool write_u32(int fd, uint32_t val);
|
||||
|
||||
int recv_fd(int fd);
|
||||
|
||||
bool write_usize(int fd, size_t val);
|
||||
|
||||
@@ -7,29 +7,20 @@ using namespace std;
|
||||
|
||||
void *self_handle = nullptr;
|
||||
|
||||
[[gnu::destructor]] [[maybe_unused]]
|
||||
static void zygisk_cleanup_wait() {
|
||||
if (self_handle) {
|
||||
// Wait 10us to make sure none of our code is executing
|
||||
timespec ts = { .tv_sec = 0, .tv_nsec = 10000L };
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void *handle) {
|
||||
void entry(void* handle) {
|
||||
LOGI("Zygisk library injected");
|
||||
self_handle = handle;
|
||||
|
||||
if (!zygiskd::PingHeartbeat()) {
|
||||
LOGE("Zygisk daemon is not running");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
logging::setfd(zygiskd::RequestLogcatFd());
|
||||
#endif
|
||||
self_handle = handle;
|
||||
|
||||
LOGD("Load injector successfully");
|
||||
LOGD("Start hooking");
|
||||
hook_functions();
|
||||
}
|
||||
|
||||
// The following code runs in zygote/app process
|
||||
|
||||
static inline bool should_load_modules(uint32_t flags) {
|
||||
return (flags & UNMOUNT_MASK) != UNMOUNT_MASK &&
|
||||
(flags & PROCESS_IS_MAGISK_APP) != PROCESS_IS_MAGISK_APP;
|
||||
}
|
||||
|
||||
122
loader/src/injector/files.cpp
Normal file
122
loader/src/injector/files.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <sys/sysmacros.h>
|
||||
|
||||
#include "files.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn) {
|
||||
size_t len = 1024;
|
||||
char *buf = (char *) malloc(len);
|
||||
char *start;
|
||||
ssize_t read;
|
||||
while ((read = getline(&buf, &len, fp)) >= 0) {
|
||||
start = buf;
|
||||
if (trim) {
|
||||
while (read && "\n\r "sv.find(buf[read - 1]) != std::string::npos)
|
||||
--read;
|
||||
buf[read] = '\0';
|
||||
while (*start == ' ')
|
||||
++start;
|
||||
}
|
||||
if (!fn(start))
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn) {
|
||||
if (auto fp = open_file(file, "re"))
|
||||
file_readline(trim, fp.get(), fn);
|
||||
}
|
||||
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn) {
|
||||
file_readline(false, file, fn);
|
||||
}
|
||||
|
||||
std::vector<mount_info> parse_mount_info(const char *pid) {
|
||||
char buf[PATH_MAX] = {};
|
||||
snprintf(buf, sizeof(buf), "/proc/%s/mountinfo", pid);
|
||||
std::vector<mount_info> result;
|
||||
|
||||
file_readline(buf, [&result](std::string_view line) -> bool {
|
||||
int root_start = 0, root_end = 0;
|
||||
int target_start = 0, target_end = 0;
|
||||
int vfs_option_start = 0, vfs_option_end = 0;
|
||||
int type_start = 0, type_end = 0;
|
||||
int source_start = 0, source_end = 0;
|
||||
int fs_option_start = 0, fs_option_end = 0;
|
||||
int optional_start = 0, optional_end = 0;
|
||||
unsigned int id, parent, maj, min;
|
||||
sscanf(line.data(),
|
||||
"%u " // (1) id
|
||||
"%u " // (2) parent
|
||||
"%u:%u " // (3) maj:min
|
||||
"%n%*s%n " // (4) mountroot
|
||||
"%n%*s%n " // (5) target
|
||||
"%n%*s%n" // (6) vfs options (fs-independent)
|
||||
"%n%*[^-]%n - " // (7) optional fields
|
||||
"%n%*s%n " // (8) FS type
|
||||
"%n%*s%n " // (9) source
|
||||
"%n%*s%n", // (10) fs options (fs specific)
|
||||
&id, &parent, &maj, &min, &root_start, &root_end, &target_start,
|
||||
&target_end, &vfs_option_start, &vfs_option_end,
|
||||
&optional_start, &optional_end, &type_start, &type_end,
|
||||
&source_start, &source_end, &fs_option_start, &fs_option_end);
|
||||
|
||||
auto root = line.substr(root_start, root_end - root_start);
|
||||
auto target = line.substr(target_start, target_end - target_start);
|
||||
auto vfs_option =
|
||||
line.substr(vfs_option_start, vfs_option_end - vfs_option_start);
|
||||
++optional_start;
|
||||
--optional_end;
|
||||
auto optional = line.substr(
|
||||
optional_start,
|
||||
optional_end - optional_start > 0 ? optional_end - optional_start : 0);
|
||||
|
||||
auto type = line.substr(type_start, type_end - type_start);
|
||||
auto source = line.substr(source_start, source_end - source_start);
|
||||
auto fs_option =
|
||||
line.substr(fs_option_start, fs_option_end - fs_option_start);
|
||||
|
||||
unsigned int shared = 0;
|
||||
unsigned int master = 0;
|
||||
unsigned int propagate_from = 0;
|
||||
if (auto pos = optional.find("shared:"); pos != std::string_view::npos) {
|
||||
shared = parse_int(optional.substr(pos + 7));
|
||||
}
|
||||
if (auto pos = optional.find("master:"); pos != std::string_view::npos) {
|
||||
master = parse_int(optional.substr(pos + 7));
|
||||
}
|
||||
if (auto pos = optional.find("propagate_from:");
|
||||
pos != std::string_view::npos) {
|
||||
propagate_from = parse_int(optional.substr(pos + 15));
|
||||
}
|
||||
|
||||
result.emplace_back(mount_info {
|
||||
.id = id,
|
||||
.parent = parent,
|
||||
.device = static_cast<dev_t>(makedev(maj, min)),
|
||||
.root {root},
|
||||
.target {target},
|
||||
.vfs_option {vfs_option},
|
||||
.optional {
|
||||
.shared = shared,
|
||||
.master = master,
|
||||
.propagate_from = propagate_from,
|
||||
},
|
||||
.type {type},
|
||||
.source {source},
|
||||
.fs_option {fs_option},
|
||||
});
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
sDIR make_dir(DIR *dp) {
|
||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||
}
|
||||
|
||||
sFILE make_file(FILE *fp) {
|
||||
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
|
||||
}
|
||||
56
loader/src/injector/files.hpp
Normal file
56
loader/src/injector/files.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <dirent.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct mount_info {
|
||||
unsigned int id;
|
||||
unsigned int parent;
|
||||
dev_t device;
|
||||
std::string root;
|
||||
std::string target;
|
||||
std::string vfs_option;
|
||||
struct {
|
||||
unsigned int shared;
|
||||
unsigned int master;
|
||||
unsigned int propagate_from;
|
||||
} optional;
|
||||
std::string type;
|
||||
std::string source;
|
||||
std::string fs_option;
|
||||
};
|
||||
|
||||
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn);
|
||||
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn);
|
||||
|
||||
std::vector<mount_info> parse_mount_info(const char *pid);
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
sFILE make_file(FILE *fp);
|
||||
|
||||
static inline sDIR open_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(int dirfd) {
|
||||
return make_dir(fdopendir(dirfd));
|
||||
}
|
||||
|
||||
static inline sFILE open_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(int fd, const char *mode) {
|
||||
return make_file(fdopen(fd, mode));
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "misc.hpp"
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
static void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
||||
LOGD("Unmounted (%s)", mountpoint);
|
||||
}
|
||||
|
||||
void revert_unmount() {
|
||||
parse_mnt("/proc/self/mounts", [](mntent* mentry) {
|
||||
if (mentry->mnt_fsname == "/data/adb/ksu/modules"sv ||
|
||||
std::string_view(mentry->mnt_opts).find("/data/adb/ksu/modules") != std::string_view::npos) {
|
||||
lazy_unmount(mentry->mnt_fsname);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@@ -17,14 +17,15 @@
|
||||
#include "zygisk.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "module.hpp"
|
||||
#include "misc.hpp"
|
||||
#include "files.hpp"
|
||||
|
||||
using namespace std;
|
||||
using jni_hook::hash_map;
|
||||
using jni_hook::tree_map;
|
||||
using xstring = jni_hook::string;
|
||||
|
||||
static bool unhook_functions();
|
||||
static void hook_unloader();
|
||||
static void unhook_functions();
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -34,7 +35,6 @@ enum {
|
||||
APP_SPECIALIZE,
|
||||
SERVER_FORK_AND_SPECIALIZE,
|
||||
DO_REVERT_UNMOUNT,
|
||||
CAN_UNLOAD_ZYGISK,
|
||||
SKIP_FD_SANITIZATION,
|
||||
|
||||
FLAG_MAX
|
||||
@@ -46,6 +46,11 @@ void name##_post();
|
||||
|
||||
#define MAX_FD_SIZE 1024
|
||||
|
||||
struct HookContext;
|
||||
|
||||
// Current context
|
||||
HookContext *g_ctx;
|
||||
|
||||
struct HookContext {
|
||||
JNIEnv *env;
|
||||
union {
|
||||
@@ -80,7 +85,8 @@ struct HookContext {
|
||||
vector<IgnoreInfo> ignore_info;
|
||||
|
||||
HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0),
|
||||
hook_info_lock(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
hook_info_lock(PTHREAD_MUTEX_INITIALIZER) { g_ctx = this; }
|
||||
~HookContext();
|
||||
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void run_modules_pre();
|
||||
@@ -91,9 +97,9 @@ struct HookContext {
|
||||
DCL_PRE_POST(nativeSpecializeAppProcess)
|
||||
DCL_PRE_POST(nativeForkSystemServer)
|
||||
|
||||
void unload_zygisk();
|
||||
void sanitize_fds();
|
||||
bool exempt_fd(int fd);
|
||||
bool is_child() const { return pid <= 0; }
|
||||
|
||||
// Compatibility shim
|
||||
void plt_hook_register(const char *regex, const char *symbol, void *fn, void **backup);
|
||||
@@ -109,9 +115,8 @@ struct HookContext {
|
||||
vector<tuple<dev_t, ino_t, const char *, void **>> *plt_hook_list;
|
||||
map<string, vector<JNINativeMethod>, StringCmp> *jni_hook_list;
|
||||
hash_map<xstring, tree_map<xstring, tree_map<xstring, void *>>> *jni_method_map;
|
||||
bool should_unmap_zygisk = false;
|
||||
|
||||
// Current context
|
||||
HookContext *g_ctx;
|
||||
const JNINativeInterface *old_functions = nullptr;
|
||||
JNINativeInterface *new_functions = nullptr;
|
||||
|
||||
@@ -153,10 +158,6 @@ string get_class_name(JNIEnv *env, jclass clazz) {
|
||||
return className;
|
||||
}
|
||||
|
||||
#define DCL_HOOK_FUNC(ret, func, ...) \
|
||||
ret (*old_##func)(__VA_ARGS__); \
|
||||
ret new_##func(__VA_ARGS__)
|
||||
|
||||
jint env_RegisterNatives(
|
||||
JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint numMethods) {
|
||||
auto className = get_class_name(env, clazz);
|
||||
@@ -165,46 +166,50 @@ jint env_RegisterNatives(
|
||||
return old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
|
||||
}
|
||||
|
||||
DCL_HOOK_FUNC(void, androidSetCreateThreadFunc, void* func) {
|
||||
LOGD("androidSetCreateThreadFunc\n");
|
||||
do {
|
||||
auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>(
|
||||
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"));
|
||||
if (!get_created_java_vms) {
|
||||
for (auto &map: lsplt::MapInfo::Scan()) {
|
||||
if (!map.path.ends_with("/libnativehelper.so")) continue;
|
||||
void *h = dlopen(map.path.data(), RTLD_LAZY);
|
||||
if (!h) {
|
||||
LOGW("cannot dlopen libnativehelper.so: %s\n", dlerror());
|
||||
break;
|
||||
}
|
||||
get_created_java_vms = reinterpret_cast<decltype(get_created_java_vms)>(dlsym(h, "JNI_GetCreatedJavaVMs"));
|
||||
dlclose(h);
|
||||
break;
|
||||
}
|
||||
if (!get_created_java_vms) {
|
||||
LOGW("JNI_GetCreatedJavaVMs not found\n");
|
||||
void replace_jni_methods() {
|
||||
auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>(
|
||||
dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs"));
|
||||
if (!get_created_java_vms) {
|
||||
for (auto &map: lsplt::MapInfo::Scan()) {
|
||||
if (!map.path.ends_with("/libnativehelper.so")) continue;
|
||||
void *h = dlopen(map.path.data(), RTLD_LAZY);
|
||||
if (!h) {
|
||||
LOGW("cannot dlopen libnativehelper.so: %s\n", dlerror());
|
||||
break;
|
||||
}
|
||||
get_created_java_vms = reinterpret_cast<decltype(get_created_java_vms)>(dlsym(h, "JNI_GetCreatedJavaVMs"));
|
||||
dlclose(h);
|
||||
break;
|
||||
}
|
||||
JavaVM *vm = nullptr;
|
||||
jsize num = 0;
|
||||
jint res = get_created_java_vms(&vm, 1, &num);
|
||||
if (res != JNI_OK || vm == nullptr) break;
|
||||
JNIEnv *env = nullptr;
|
||||
res = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (res != JNI_OK || env == nullptr) break;
|
||||
default_new(new_functions);
|
||||
memcpy(new_functions, env->functions, sizeof(*new_functions));
|
||||
new_functions->RegisterNatives = &env_RegisterNatives;
|
||||
if (!get_created_java_vms) {
|
||||
LOGW("JNI_GetCreatedJavaVMs not found\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
JavaVM *vm = nullptr;
|
||||
jsize num = 0;
|
||||
jint res = get_created_java_vms(&vm, 1, &num);
|
||||
if (res != JNI_OK || vm == nullptr) return;
|
||||
JNIEnv *env = nullptr;
|
||||
res = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (res != JNI_OK || env == nullptr) return;
|
||||
default_new(new_functions);
|
||||
memcpy(new_functions, env->functions, sizeof(*new_functions));
|
||||
new_functions->RegisterNatives = &env_RegisterNatives;
|
||||
|
||||
// Replace the function table in JNIEnv to hook RegisterNatives
|
||||
old_functions = env->functions;
|
||||
env->functions = new_functions;
|
||||
} while (false);
|
||||
old_androidSetCreateThreadFunc(func);
|
||||
// Replace the function table in JNIEnv to hook RegisterNatives
|
||||
old_functions = env->functions;
|
||||
env->functions = new_functions;
|
||||
|
||||
// Re-run register_com_android_internal_os_Zygote to hook JNI methods
|
||||
auto register_zygote = dlsym(RTLD_DEFAULT, "_ZN7android39register_com_android_internal_os_ZygoteEP7_JNIEnv");
|
||||
reinterpret_cast<void (*)(JNIEnv *)>(register_zygote)(env);
|
||||
}
|
||||
|
||||
#define DCL_HOOK_FUNC(ret, func, ...) \
|
||||
ret (*old_##func)(__VA_ARGS__); \
|
||||
ret new_##func(__VA_ARGS__)
|
||||
|
||||
// Skip actual fork and return cached result if applicable
|
||||
DCL_HOOK_FUNC(int, fork) {
|
||||
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
|
||||
@@ -219,7 +224,11 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
// Simply avoid doing any unmounts for SysUI to avoid potential issues.
|
||||
(g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) {
|
||||
if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
|
||||
revert_unmount();
|
||||
if (g_ctx->info_flags & PROCESS_ROOT_IS_KSU) {
|
||||
revert_unmount_ksu();
|
||||
} else if (g_ctx->info_flags & PROCESS_ROOT_IS_MAGISK) {
|
||||
revert_unmount_magisk();
|
||||
}
|
||||
}
|
||||
|
||||
/* Zygisksu changed: No umount app_process */
|
||||
@@ -242,13 +251,28 @@ DCL_HOOK_FUNC(void, android_log_close) {
|
||||
old_android_log_close();
|
||||
}
|
||||
|
||||
// Last point before process secontext changes
|
||||
DCL_HOOK_FUNC(int, selinux_android_setcontext,
|
||||
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
|
||||
if (g_ctx) {
|
||||
g_ctx->flags[CAN_UNLOAD_ZYGISK] = unhook_functions();
|
||||
// We cannot directly call `dlclose` to unload ourselves, otherwise when `dlclose` returns,
|
||||
// it will return to our code which has been unmapped, causing segmentation fault.
|
||||
// Instead, we hook `pthread_attr_destroy` which will be called when VM daemon threads start.
|
||||
DCL_HOOK_FUNC(int, pthread_attr_destroy, void *target) {
|
||||
int res = old_pthread_attr_destroy((pthread_attr_t *)target);
|
||||
|
||||
// Only perform unloading on the main thread
|
||||
if (gettid() != getpid())
|
||||
return res;
|
||||
|
||||
LOGD("pthread_attr_destroy\n");
|
||||
if (should_unmap_zygisk) {
|
||||
unhook_functions();
|
||||
if (should_unmap_zygisk) {
|
||||
// Because both `pthread_attr_destroy` and `dlclose` have the same function signature,
|
||||
// we can use `musttail` to let the compiler reuse our stack frame and thus
|
||||
// `dlclose` will directly return to the caller of `pthread_attr_destroy`.
|
||||
[[clang::musttail]] return dlclose(self_handle);
|
||||
}
|
||||
}
|
||||
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#undef DCL_HOOK_FUNC
|
||||
@@ -327,6 +351,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;
|
||||
@@ -444,7 +469,6 @@ int sigmask(int how, int signum) {
|
||||
}
|
||||
|
||||
void HookContext::fork_pre() {
|
||||
g_ctx = this;
|
||||
// Do our own fork before loading any 3rd party code
|
||||
// First block SIGCHLD, unblock after original fork is done
|
||||
sigmask(SIG_BLOCK, SIGCHLD);
|
||||
@@ -526,7 +550,6 @@ void HookContext::fork_post() {
|
||||
// Unblock SIGCHLD in case the original method didn't
|
||||
sigmask(SIG_UNBLOCK, SIGCHLD);
|
||||
g_ctx = nullptr;
|
||||
unload_zygisk();
|
||||
}
|
||||
|
||||
/* Zygisksu changed: Load module fds */
|
||||
@@ -566,15 +589,13 @@ void HookContext::run_modules_post() {
|
||||
/* Zygisksu changed: Load module fds */
|
||||
void HookContext::app_specialize_pre() {
|
||||
flags[APP_SPECIALIZE] = true;
|
||||
info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid);
|
||||
run_modules_pre();
|
||||
}
|
||||
|
||||
|
||||
void HookContext::app_specialize_post() {
|
||||
run_modules_post();
|
||||
if (info_flags & PROCESS_IS_MAGISK_APP) {
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
}
|
||||
|
||||
// Cleanups
|
||||
env->ReleaseStringUTFChars(args.app->nice_name, process);
|
||||
@@ -582,22 +603,6 @@ void HookContext::app_specialize_post() {
|
||||
logging::setfd(-1);
|
||||
}
|
||||
|
||||
void HookContext::unload_zygisk() {
|
||||
if (flags[CAN_UNLOAD_ZYGISK]) {
|
||||
// Do NOT call the destructor
|
||||
operator delete(jni_method_map);
|
||||
// Directly unmap the whole memory block
|
||||
jni_hook::memory_block::release();
|
||||
|
||||
// Strip out all API function pointers
|
||||
for (auto &m : modules) {
|
||||
m.clearApi();
|
||||
}
|
||||
|
||||
new_daemon_thread(reinterpret_cast<thread_entry>(&dlclose), self_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool HookContext::exempt_fd(int fd) {
|
||||
if (flags[POST_SPECIALIZE] || flags[SKIP_FD_SANITIZATION])
|
||||
return true;
|
||||
@@ -612,7 +617,6 @@ bool HookContext::exempt_fd(int fd) {
|
||||
void HookContext::nativeSpecializeAppProcess_pre() {
|
||||
process = env->GetStringUTFChars(args.app->nice_name, nullptr);
|
||||
LOGV("pre specialize [%s]\n", process);
|
||||
g_ctx = this;
|
||||
// App specialize does not check FD
|
||||
flags[SKIP_FD_SANITIZATION] = true;
|
||||
app_specialize_pre();
|
||||
@@ -621,7 +625,6 @@ void HookContext::nativeSpecializeAppProcess_pre() {
|
||||
void HookContext::nativeSpecializeAppProcess_post() {
|
||||
LOGV("post specialize [%s]\n", process);
|
||||
app_specialize_post();
|
||||
unload_zygisk();
|
||||
}
|
||||
|
||||
/* Zygisksu changed: No system_server status write back */
|
||||
@@ -671,6 +674,43 @@ void HookContext::nativeForkAndSpecialize_post() {
|
||||
fork_post();
|
||||
}
|
||||
|
||||
HookContext::~HookContext() {
|
||||
// This global pointer points to a variable on the stack.
|
||||
// Set this to nullptr to prevent leaking local variable.
|
||||
// This also disables most plt hooked functions.
|
||||
g_ctx = nullptr;
|
||||
|
||||
if (!is_child())
|
||||
return;
|
||||
|
||||
should_unmap_zygisk = true;
|
||||
|
||||
// Restore JNIEnv
|
||||
if (env->functions == new_functions) {
|
||||
env->functions = old_functions;
|
||||
delete new_functions;
|
||||
}
|
||||
|
||||
// Unhook JNI methods
|
||||
for (const auto &[clz, methods] : *jni_hook_list) {
|
||||
if (!methods.empty() && env->RegisterNatives(
|
||||
env->FindClass(clz.data()), methods.data(),
|
||||
static_cast<int>(methods.size())) != 0) {
|
||||
LOGE("Failed to restore JNI hook of class [%s]\n", clz.data());
|
||||
should_unmap_zygisk = false;
|
||||
}
|
||||
}
|
||||
delete jni_hook_list;
|
||||
jni_hook_list = nullptr;
|
||||
|
||||
// Strip out all API function pointers
|
||||
for (auto &m : modules) {
|
||||
m.clearApi();
|
||||
}
|
||||
|
||||
hook_unloader();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static bool hook_commit() {
|
||||
@@ -713,8 +753,6 @@ void hook_functions() {
|
||||
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare);
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, selinux_android_setcontext);
|
||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, androidSetCreateThreadFunc);
|
||||
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close);
|
||||
hook_commit();
|
||||
|
||||
@@ -723,40 +761,36 @@ void hook_functions() {
|
||||
std::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
|
||||
[](auto &t) { return *std::get<3>(t) == nullptr;}),
|
||||
plt_hook_list->end());
|
||||
|
||||
replace_jni_methods();
|
||||
}
|
||||
|
||||
static bool unhook_functions() {
|
||||
bool success = true;
|
||||
static void hook_unloader() {
|
||||
ino_t art_inode = 0;
|
||||
dev_t art_dev = 0;
|
||||
|
||||
// Restore JNIEnv
|
||||
if (g_ctx->env->functions == new_functions) {
|
||||
g_ctx->env->functions = old_functions;
|
||||
delete new_functions;
|
||||
}
|
||||
|
||||
// Unhook JNI methods
|
||||
for (const auto &[clz, methods] : *jni_hook_list) {
|
||||
if (!methods.empty() && g_ctx->env->RegisterNatives(
|
||||
g_ctx->env->FindClass(clz.data()), methods.data(),
|
||||
static_cast<int>(methods.size())) != 0) {
|
||||
LOGE("Failed to restore JNI hook of class [%s]\n", clz.data());
|
||||
success = false;
|
||||
for (auto &map : lsplt::MapInfo::Scan()) {
|
||||
if (map.path.ends_with("/libart.so")) {
|
||||
art_inode = map.inode;
|
||||
art_dev = map.dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete jni_hook_list;
|
||||
|
||||
PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_destroy);
|
||||
hook_commit();
|
||||
}
|
||||
|
||||
static void unhook_functions() {
|
||||
// Unhook plt_hook
|
||||
for (const auto &[dev, inode, sym, old_func] : *plt_hook_list) {
|
||||
if (!lsplt::RegisterHook(dev, inode, sym, *old_func, nullptr)) {
|
||||
LOGE("Failed to register plt_hook [%s]\n", sym);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
delete plt_hook_list;
|
||||
if (!hook_commit()) {
|
||||
LOGE("Failed to restore plt_hook\n");
|
||||
success = false;
|
||||
should_unmap_zygisk = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -23,22 +23,27 @@ int parse_int(std::string_view s) {
|
||||
return val;
|
||||
}
|
||||
|
||||
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn) {
|
||||
auto fp = sFILE(setmntent(file, "re"), endmntent);
|
||||
if (fp) {
|
||||
mntent mentry{};
|
||||
char buf[PATH_MAX];
|
||||
while (getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) {
|
||||
if (!fn(&mentry))
|
||||
break;
|
||||
std::list<std::string> split_str(std::string_view s, std::string_view delimiter) {
|
||||
std::list<std::string> ret;
|
||||
size_t pos = 0;
|
||||
while (pos < s.size()) {
|
||||
auto next = s.find(delimiter, pos);
|
||||
if (next == std::string_view::npos) {
|
||||
ret.emplace_back(s.substr(pos));
|
||||
break;
|
||||
}
|
||||
ret.emplace_back(s.substr(pos, next - pos));
|
||||
pos = next + delimiter.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
sDIR make_dir(DIR *dp) {
|
||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||
}
|
||||
|
||||
sFILE make_file(FILE *fp) {
|
||||
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
|
||||
std::string join_str(const std::list<std::string>& list, std::string_view delimiter) {
|
||||
std::string ret;
|
||||
for (auto& s : list) {
|
||||
if (!ret.empty())
|
||||
ret += delimiter;
|
||||
ret += s;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mntent.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "logging.h"
|
||||
@@ -34,6 +32,10 @@ private:
|
||||
using thread_entry = void *(*)(void *);
|
||||
int new_daemon_thread(thread_entry entry, void *arg);
|
||||
|
||||
static inline bool str_contains(std::string_view s, std::string_view ss) {
|
||||
return s.find(ss) != std::string_view::npos;
|
||||
}
|
||||
|
||||
template<typename T, typename Impl>
|
||||
class stateless_allocator {
|
||||
public:
|
||||
@@ -49,33 +51,23 @@ public:
|
||||
bool operator!=(const stateless_allocator&) { return false; }
|
||||
};
|
||||
|
||||
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
sDIR make_dir(DIR *dp);
|
||||
sFILE make_file(FILE *fp);
|
||||
template <typename T>
|
||||
class reversed_container {
|
||||
public:
|
||||
reversed_container(T &base) : base(base) {}
|
||||
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
|
||||
decltype(std::declval<T>().rend()) end() { return base.rend(); }
|
||||
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
|
||||
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
|
||||
private:
|
||||
T &base;
|
||||
};
|
||||
|
||||
static inline sDIR open_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(const char *path) {
|
||||
return make_dir(opendir(path));
|
||||
}
|
||||
|
||||
static inline sDIR xopen_dir(int dirfd) {
|
||||
return make_dir(fdopendir(dirfd));
|
||||
}
|
||||
|
||||
static inline sFILE open_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(const char *path, const char *mode) {
|
||||
return make_file(fopen(path, mode));
|
||||
}
|
||||
|
||||
static inline sFILE xopen_file(int fd, const char *mode) {
|
||||
return make_file(fdopen(fd, mode));
|
||||
template <typename T>
|
||||
reversed_container<T> reversed(T &base) {
|
||||
return reversed_container<T>(base);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
@@ -95,7 +87,9 @@ struct StringCmp {
|
||||
*/
|
||||
int parse_int(std::string_view s);
|
||||
|
||||
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn);
|
||||
std::list<std::string> split_str(std::string_view s, std::string_view delimiter);
|
||||
|
||||
std::string join_str(const std::list<std::string>& list, std::string_view delimiter);
|
||||
|
||||
template <typename T>
|
||||
static inline T align_to(T v, int a) {
|
||||
|
||||
@@ -111,12 +111,11 @@ namespace {
|
||||
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
||||
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
||||
|
||||
PROCESS_IS_SYS_UI = (1u << 29),
|
||||
DENYLIST_ENFORCING = (1u << 30),
|
||||
PROCESS_IS_MAGISK_APP = (1u << 31),
|
||||
PROCESS_ROOT_IS_KSU = (1u << 29),
|
||||
PROCESS_ROOT_IS_MAGISK = (1u << 30),
|
||||
PROCESS_IS_SYS_UI = (1u << 31),
|
||||
|
||||
UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING),
|
||||
PRIVATE_MASK = (PROCESS_IS_SYS_UI | DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP)
|
||||
PRIVATE_MASK = PROCESS_IS_SYS_UI
|
||||
};
|
||||
|
||||
struct api_abi_base {
|
||||
|
||||
91
loader/src/injector/unmount.cpp
Normal file
91
loader/src/injector/unmount.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "files.hpp"
|
||||
#include "logging.h"
|
||||
#include "misc.hpp"
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
namespace {
|
||||
constexpr auto MODULE_DIR = "/data/adb/modules";
|
||||
constexpr auto KSU_OVERLAY_SOURCE = "KSU";
|
||||
constexpr auto ZYGISK_FUSE_SOURCE = "zygisk";
|
||||
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) {
|
||||
LOGD("Unmounted (%s)", mountpoint);
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
PLOGE("Unmount (%s)", mountpoint);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_ksu() {
|
||||
std::string ksu_loop;
|
||||
std::vector<std::string> targets;
|
||||
|
||||
// Unmount ksu module dir last
|
||||
targets.emplace_back(MODULE_DIR);
|
||||
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.target == MODULE_DIR) {
|
||||
ksu_loop = info.source;
|
||||
continue;
|
||||
}
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
// Unmount ksu overlays
|
||||
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);
|
||||
}
|
||||
// Unmount fuse
|
||||
if (info.type == "fuse" && info.source == ZYGISK_FUSE_SOURCE) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
// Unmount everything from ksu loop except ksu module dir
|
||||
if (info.source == ksu_loop && info.target != MODULE_DIR) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
// Do unmount
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_magisk() {
|
||||
std::vector<std::string> targets;
|
||||
|
||||
// Unmount dummy skeletons and MAGISKTMP
|
||||
// since mirror nodes are always mounted under skeleton, we don't have to specifically unmount
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.source == "magisk" || info.source == "worker" || // magisktmp tmpfs
|
||||
info.root.starts_with("/adb/modules")) { // bind mount from data partition
|
||||
targets.push_back(info.target);
|
||||
}
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
// Unmount fuse
|
||||
if (info.type == "fuse" && info.source == ZYGISK_FUSE_SOURCE) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,7 @@ extern void *self_handle;
|
||||
|
||||
void hook_functions();
|
||||
|
||||
void revert_unmount();
|
||||
void revert_unmount_ksu();
|
||||
|
||||
void revert_unmount_magisk();
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
#include <string_view>
|
||||
#include <sys/system_properties.h>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "dl.h"
|
||||
#include "logging.h"
|
||||
#include "native_bridge_callbacks.h"
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
uint8_t NativeBridgeItf[sizeof(NativeBridgeCallbacks<__ANDROID_API_R__>) * 2]{0};
|
||||
|
||||
namespace {
|
||||
constexpr auto kZygoteProcesses = {"zygote", "zygote32", "zygote64", "usap32", "usap64"};
|
||||
constexpr auto kInjector = "/system/" LP_SELECT("lib", "lib64") "/libinjector.so";
|
||||
|
||||
void* sOriginalBridge = nullptr;
|
||||
}
|
||||
|
||||
__used __attribute__((destructor))
|
||||
void Destructor() {
|
||||
if (sOriginalBridge) {
|
||||
dlclose(sOriginalBridge);
|
||||
}
|
||||
}
|
||||
|
||||
__used __attribute__((constructor))
|
||||
void Constructor() {
|
||||
if (getuid() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string_view cmdline = getprogname();
|
||||
if (std::none_of(
|
||||
kZygoteProcesses.begin(), kZygoteProcesses.end(),
|
||||
[&](const char* p) { return cmdline == p; }
|
||||
)) {
|
||||
LOGW("Not started as zygote (cmdline=%s)", cmdline.data());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string native_bridge;
|
||||
do {
|
||||
if (!zygiskd::PingHeartbeat()) break;
|
||||
#ifdef NDEBUG
|
||||
logging::setfd(zygiskd::RequestLogcatFd());
|
||||
#endif
|
||||
LOGI("Read native bridge");
|
||||
native_bridge = zygiskd::ReadNativeBridge();
|
||||
|
||||
LOGI("Load injector");
|
||||
auto handle = DlopenExt(kInjector, RTLD_NOW);
|
||||
if (handle == nullptr) {
|
||||
LOGE("Failed to dlopen injector: %s", dlerror());
|
||||
break;
|
||||
}
|
||||
auto entry = dlsym(handle, "entry");
|
||||
if (entry == nullptr) {
|
||||
LOGE("Failed to dlsym injector entry: %s", dlerror());
|
||||
dlclose(handle);
|
||||
break;
|
||||
}
|
||||
reinterpret_cast<void (*)(void*)>(entry)(handle);
|
||||
} while (false);
|
||||
|
||||
do {
|
||||
if (native_bridge.empty() || native_bridge == "0") break;
|
||||
|
||||
LOGI("Load original native bridge: %s", native_bridge.data());
|
||||
sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW);
|
||||
if (sOriginalBridge == nullptr) {
|
||||
LOGE("dlopen failed: %s", dlerror());
|
||||
break;
|
||||
}
|
||||
|
||||
auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf");
|
||||
if (original_native_bridge_itf == nullptr) {
|
||||
LOGE("dlsym failed: %s", dlerror());
|
||||
break;
|
||||
}
|
||||
|
||||
long sdk = 0;
|
||||
char value[PROP_VALUE_MAX + 1];
|
||||
if (__system_property_get("ro.build.version.sdk", value) > 0) {
|
||||
sdk = strtol(value, nullptr, 10);
|
||||
}
|
||||
|
||||
auto callbacks_size = 0;
|
||||
if (sdk >= __ANDROID_API_R__) {
|
||||
callbacks_size = sizeof(NativeBridgeCallbacks<__ANDROID_API_R__>);
|
||||
} else if (sdk == __ANDROID_API_Q__) {
|
||||
callbacks_size = sizeof(NativeBridgeCallbacks<__ANDROID_API_Q__>);
|
||||
}
|
||||
|
||||
memcpy(NativeBridgeItf, original_native_bridge_itf, callbacks_size);
|
||||
} while (false);
|
||||
|
||||
logging::setfd(-1);
|
||||
}
|
||||
@@ -4,13 +4,18 @@ import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
alias(libs.plugins.agp.lib)
|
||||
}
|
||||
|
||||
val moduleId: String by rootProject.extra
|
||||
val moduleName: String by rootProject.extra
|
||||
val verCode: Int by rootProject.extra
|
||||
val verName: String by rootProject.extra
|
||||
val minKsuVersion: Int by rootProject.extra
|
||||
val minKsudVersion: Int by rootProject.extra
|
||||
val maxKsuVersion: Int by rootProject.extra
|
||||
val minMagiskVersion: Int by rootProject.extra
|
||||
val commitHash: String by rootProject.extra
|
||||
|
||||
android.buildFeatures {
|
||||
androidResources = false
|
||||
@@ -23,7 +28,7 @@ androidComponents.onVariants { variant ->
|
||||
val buildTypeLowered = variant.buildType?.toLowerCase()
|
||||
|
||||
val moduleDir = "$buildDir/outputs/module/$variantLowered"
|
||||
val zipFileName = "$moduleName-$verName-$verCode-$buildTypeLowered.zip".replace(' ', '-')
|
||||
val zipFileName = "$moduleName-$verName-$verCode-$commitHash-$buildTypeLowered.zip".replace(' ', '-')
|
||||
|
||||
val prepareModuleFilesTask = task<Sync>("prepareModuleFiles$variantCapped") {
|
||||
group = "module"
|
||||
@@ -34,7 +39,7 @@ androidComponents.onVariants { variant ->
|
||||
into(moduleDir)
|
||||
from("${rootProject.projectDir}/README.md")
|
||||
from("$projectDir/src") {
|
||||
exclude("module.prop", "customize.sh", "daemon.sh")
|
||||
exclude("module.prop", "customize.sh", "service.sh")
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
}
|
||||
from("$projectDir/src") {
|
||||
@@ -42,14 +47,18 @@ androidComponents.onVariants { variant ->
|
||||
expand(
|
||||
"moduleId" to moduleId,
|
||||
"moduleName" to moduleName,
|
||||
"versionName" to "$verName ($verCode)",
|
||||
"versionCode" to verCode,
|
||||
"versionName" to "$verName ($verCode-$commitHash-$variantLowered)",
|
||||
"versionCode" to verCode
|
||||
)
|
||||
}
|
||||
from("$projectDir/src") {
|
||||
include("customize.sh", "daemon.sh")
|
||||
include("customize.sh", "service.sh")
|
||||
val tokens = mapOf(
|
||||
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false"
|
||||
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
|
||||
"MIN_KSU_VERSION" to "$minKsuVersion",
|
||||
"MIN_KSUD_VERSION" to "$minKsudVersion",
|
||||
"MAX_KSU_VERSION" to "$maxKsuVersion",
|
||||
"MIN_MAGISK_VERSION" to "$minMagiskVersion",
|
||||
)
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||
@@ -106,7 +115,7 @@ androidComponents.onVariants { variant ->
|
||||
val installMagiskTask = task<Exec>("installMagisk$variantCapped") {
|
||||
group = "module"
|
||||
dependsOn(pushTask)
|
||||
commandLine("adb", "shell", "su", "-c", "KSU=true magisk --install-module /data/local/tmp/$zipFileName")
|
||||
commandLine("adb", "shell", "su", "-c", "magisk --install-module /data/local/tmp/$zipFileName")
|
||||
}
|
||||
|
||||
task<Exec>("installKsuAndReboot$variantCapped") {
|
||||
|
||||
@@ -2,28 +2,56 @@
|
||||
SKIPUNZIP=1
|
||||
|
||||
DEBUG=@DEBUG@
|
||||
MIN_KSU_VERSION=@MIN_KSU_VERSION@
|
||||
MIN_KSUD_VERSION=@MIN_KSUD_VERSION@
|
||||
MAX_KSU_VERSION=@MAX_KSU_VERSION@
|
||||
MIN_MAGISK_VERSION=@MIN_MAGISK_VERSION@
|
||||
|
||||
if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then
|
||||
if [ "$BOOTMODE" ] && [ "$KSU" ]; then
|
||||
ui_print "- Installing from KernelSU app"
|
||||
ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
|
||||
if ! [ "$KSU_KERNEL_VER_CODE" ] || [ "$KSU_KERNEL_VER_CODE" -lt "$MIN_KSU_VERSION" ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! KernelSU version is too old!"
|
||||
ui_print "! Please update KernelSU to latest version"
|
||||
abort "*********************************************************"
|
||||
elif [ "$KSU_KERNEL_VER_CODE" -ge "$MAX_KSU_VERSION" ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! KernelSU version abnormal!"
|
||||
ui_print "! Please integrate KernelSU into your kernel"
|
||||
ui_print " as submodule instead of copying the source code"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
if ! [ "$KSU_VER_CODE" ] || [ "$KSU_VER_CODE" -lt "$MIN_KSUD_VERSION" ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! ksud version is too old!"
|
||||
ui_print "! Please update KernelSU Manager to latest version"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
if [ "$(which magisk)" ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Multiple root implementation is NOT supported!"
|
||||
ui_print "! Please uninstall Magisk before installing Zygisksu"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then
|
||||
ui_print "- Installing from Magisk app"
|
||||
if [ "$MAGISK_VER_CODE" -lt "$MIN_MAGISK_VERSION" ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Magisk version is too old!"
|
||||
ui_print "! Please update Magisk to latest version"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
else
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Install from recovery or Magisk is NOT supported"
|
||||
ui_print "! Please install from KernelSU app"
|
||||
abort "*********************************************************"
|
||||
ui_print "! Install from recovery is not supported"
|
||||
ui_print "! Please install from KernelSU or Magisk app"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
|
||||
VERSION=$(grep_prop version "${TMPDIR}/module.prop")
|
||||
ui_print "- Installing Zygisksu $VERSION"
|
||||
|
||||
# check KernelSU
|
||||
ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
|
||||
if [ "$KSU_KERNEL_VER_CODE" -lt 10575 ]; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! KernelSU version is too old!"
|
||||
ui_print "! Please update KernelSU to latest version"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
|
||||
# check android
|
||||
if [ "$API" -lt 29 ]; then
|
||||
ui_print "! Unsupported sdk: $API"
|
||||
@@ -33,7 +61,7 @@ else
|
||||
fi
|
||||
|
||||
# check architecture
|
||||
if [ "$ARCH" != "arm" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x86" ] && [ "$ARCH" != "x64" ]; then
|
||||
if [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x64" ]; then
|
||||
abort "! Unsupported platform: $ARCH"
|
||||
else
|
||||
ui_print "- Device platform: $ARCH"
|
||||
@@ -52,85 +80,49 @@ extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip"
|
||||
extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
|
||||
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
||||
|
||||
ui_print "- Checking SELinux patches"
|
||||
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Unable to apply SELinux patches!"
|
||||
ui_print "! Your kernel may not support SELinux patch fully"
|
||||
abort "*********************************************************"
|
||||
if [ "$DEBUG" = true ]; then
|
||||
ui_print "- Add debug SELinux policy"
|
||||
echo "allow crash_dump adb_data_file dir search" >> "$TMPDIR/sepolicy.rule"
|
||||
fi
|
||||
|
||||
if [ "$KSU" ]; then
|
||||
ui_print "- Checking SELinux patches"
|
||||
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Unable to apply SELinux patches!"
|
||||
ui_print "! Your kernel may not support SELinux patch fully"
|
||||
abort "*********************************************************"
|
||||
fi
|
||||
fi
|
||||
|
||||
ui_print "- Extracting module files"
|
||||
extract "$ZIPFILE" 'daemon.sh' "$MODPATH"
|
||||
extract "$ZIPFILE" 'module.prop' "$MODPATH"
|
||||
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
|
||||
extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH"
|
||||
|
||||
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
|
||||
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true
|
||||
extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
||||
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
||||
|
||||
mkdir "$MODPATH/bin"
|
||||
mkdir "$MODPATH/system"
|
||||
[ "$HAS32BIT" = true ] && mkdir "$MODPATH/system/lib"
|
||||
[ "$HAS64BIT" = true ] && mkdir "$MODPATH/system/lib64"
|
||||
mkdir "$MODPATH/system/lib64"
|
||||
|
||||
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
||||
if [ "$HAS32BIT" = true ]; then
|
||||
ui_print "- Extracting x86 libraries"
|
||||
extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true
|
||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
|
||||
extract "$ZIPFILE" 'lib/x86/libinjector.so' "$MODPATH/system/lib" true
|
||||
extract "$ZIPFILE" 'lib/x86/libzygiskloader.so' "$MODPATH/system/lib" true
|
||||
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
|
||||
fi
|
||||
|
||||
if [ "$HAS64BIT" = true ]; then
|
||||
ui_print "- Extracting x64 libraries"
|
||||
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
|
||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
|
||||
extract "$ZIPFILE" 'lib/x86_64/libinjector.so' "$MODPATH/system/lib64" true
|
||||
extract "$ZIPFILE" 'lib/x86_64/libzygiskloader.so' "$MODPATH/system/lib64" true
|
||||
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
|
||||
fi
|
||||
if [ "$ARCH" = "x64" ]; then
|
||||
ui_print "- Extracting x64 libraries"
|
||||
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
|
||||
extract "$ZIPFILE" 'lib/x86_64/libzygisk.so' "$MODPATH/system/lib64" true
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp"
|
||||
else
|
||||
if [ "$HAS32BIT" = true ]; then
|
||||
ui_print "- Extracting arm libraries"
|
||||
extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true
|
||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
|
||||
extract "$ZIPFILE" 'lib/armeabi-v7a/libinjector.so' "$MODPATH/system/lib" true
|
||||
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygiskloader.so' "$MODPATH/system/lib" true
|
||||
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
|
||||
fi
|
||||
|
||||
if [ "$HAS64BIT" = true ]; then
|
||||
ui_print "- Extracting arm64 libraries"
|
||||
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
|
||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
|
||||
extract "$ZIPFILE" 'lib/arm64-v8a/libinjector.so' "$MODPATH/system/lib64" true
|
||||
extract "$ZIPFILE" 'lib/arm64-v8a/libzygiskloader.so' "$MODPATH/system/lib64" true
|
||||
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
|
||||
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/libinjector.so"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.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/libinjector.so"
|
||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
|
||||
fi
|
||||
ui_print "- Extracting arm64 libraries"
|
||||
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
|
||||
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk.so' "$MODPATH/system/lib64" true
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
|
||||
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp"
|
||||
fi
|
||||
|
||||
ui_print "- Setting permissions"
|
||||
chmod 0744 "$MODPATH/daemon.sh"
|
||||
set_perm_recursive "$MODPATH/bin" 0 2000 0755 0755
|
||||
set_perm_recursive "$MODPATH/system/lib" 0 0 0755 0644 u:object_r:system_lib_file:s0
|
||||
set_perm_recursive "$MODPATH/bin" 0 0 0755 0755
|
||||
set_perm_recursive "$MODPATH/system/lib64" 0 0 0755 0644 u:object_r:system_lib_file:s0
|
||||
|
||||
# If Huawei's Maple is enabled, system_server is created with a special way which is out of Zygisk's control
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
DEBUG=@DEBUG@
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||
|
||||
log -p i -t "zygisksu" "Start watchdog"
|
||||
resetprop ro.dalvik.vm.native.bridge libzygiskloader.so
|
||||
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1
|
||||
@@ -4,3 +4,4 @@ version=${versionName}
|
||||
versionCode=${versionCode}
|
||||
author=Nullptr, 5ec1cff
|
||||
description=Run Zygisk on KernelSU.
|
||||
updateJson=https://api.nullptr.icu/android/zygisk-on-kernelsu/static/update.json
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
MODDIR=${0%/*}
|
||||
if [ "$ZYGISK_ENABLED" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd $MODDIR
|
||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||
cd "$MODDIR"
|
||||
|
||||
unshare -m sh -c "./daemon.sh $@&"
|
||||
if [ "$(which magisk)" ]; then
|
||||
for file in ../*; do
|
||||
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
|
||||
if [ -f "$file/post-fs-data.sh" ]; then
|
||||
cd "$file"
|
||||
log -p i -t "zygisksu" "Manually trigger post-fs-data.sh for $file"
|
||||
sh "$(realpath ./post-fs-data.sh)"
|
||||
cd "$MODDIR"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||
unshare -m sh -c "bin/zygisk-fuse &"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
deny vold fusectlfs file write
|
||||
|
||||
allow * tmpfs * *
|
||||
allow zygote appdomain_tmpfs dir *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
|
||||
type magisk_file file_type
|
||||
typeattribute magisk_file mlstrustedobject
|
||||
@@ -10,3 +14,8 @@ allow * magisk_file lnk_file *
|
||||
allow * magisk_file sock_file *
|
||||
|
||||
allow system_server system_server process execmem
|
||||
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
|
||||
|
||||
26
module/src/service.sh
Normal file
26
module/src/service.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
DEBUG=@DEBUG@
|
||||
|
||||
MODDIR=${0%/*}
|
||||
if [ "$ZYGISK_ENABLED" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$MODDIR"
|
||||
|
||||
if [ "$(which magisk)" ]; then
|
||||
for file in ../*; do
|
||||
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
|
||||
if [ -f "$file/service.sh" ]; then
|
||||
cd "$file"
|
||||
log -p i -t "zygisksu" "Manually trigger service.sh for $file"
|
||||
sh "$(realpath ./service.sh)"
|
||||
cd "$MODDIR"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||
unshare -m sh -c "bin/zygisk-wd &"
|
||||
@@ -6,10 +6,6 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("com.android.library") version "7.4.1"
|
||||
id("com.android.application") version "7.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
|
||||
@@ -3,22 +3,28 @@ name = "zygiskd"
|
||||
authors = ["Nullptr"]
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.67"
|
||||
rust-version = "1.69"
|
||||
|
||||
[dependencies]
|
||||
android_logger = "0.12.0"
|
||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
const_format = "0.2.5"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.17"
|
||||
memfd = "0.6.2"
|
||||
nix = "0.26.2"
|
||||
num_enum = "0.5.9"
|
||||
passfd = "0.1.5"
|
||||
rand = "0.8.5"
|
||||
android_logger = "0.13"
|
||||
anyhow = { version = "1.0", features = ["backtrace"] }
|
||||
bitflags = { version = "2.3" }
|
||||
const_format = "0.2"
|
||||
futures = "0.3"
|
||||
konst = "0.3"
|
||||
lazy_static = "1.4"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
memfd = "0.6"
|
||||
num_enum = "0.5"
|
||||
passfd = "0.1"
|
||||
proc-maps = "0.3"
|
||||
ptrace-do = "0.1"
|
||||
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
|
||||
tokio = { version = "1.28", features = ["full"] }
|
||||
|
||||
binder = { path = "src/external/binder_rs/binder" }
|
||||
binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" }
|
||||
fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false }
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.mozilla.rust-android-gradle.rust-android")
|
||||
alias(libs.plugins.agp.lib)
|
||||
alias(libs.plugins.rust.android)
|
||||
}
|
||||
|
||||
val verName: String by rootProject.extra
|
||||
val verCode: Int by rootProject.extra
|
||||
val minKsuVersion: Int by rootProject.extra
|
||||
val maxKsuVersion: Int by rootProject.extra
|
||||
val minMagiskVersion: Int by rootProject.extra
|
||||
|
||||
android.buildFeatures {
|
||||
androidResources = false
|
||||
@@ -15,7 +18,7 @@ cargo {
|
||||
module = "."
|
||||
libname = "zygiskd"
|
||||
targetIncludes = arrayOf("zygiskd")
|
||||
targets = listOf("arm64", "arm", "x86", "x86_64")
|
||||
targets = listOf("arm64", "x86_64")
|
||||
targetDirectory = "build/intermediates/rust"
|
||||
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
|
||||
profile = if (isDebug) "debug" else "release"
|
||||
@@ -23,5 +26,8 @@ cargo {
|
||||
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
|
||||
spec.environment("VERSION_CODE", verCode)
|
||||
spec.environment("VERSION_NAME", verName)
|
||||
spec.environment("MIN_KSU_VERSION", minKsuVersion)
|
||||
spec.environment("MAX_KSU_VERSION", maxKsuVersion)
|
||||
spec.environment("MIN_MAGISK_VERSION", minMagiskVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::thread;
|
||||
use anyhow::Result;
|
||||
use nix::libc;
|
||||
use passfd::FdPassingExt;
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::dl;
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
pub fn entry(fd: i32) -> Result<()> {
|
||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
||||
let mut stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||
let name = stream.read_string()?;
|
||||
let library = stream.recv_fd()?;
|
||||
let entry = load_module(library)?;
|
||||
unsafe { libc::close(library) };
|
||||
|
||||
let entry = match entry {
|
||||
Some(entry) => {
|
||||
log::debug!("Companion process created for `{name}`");
|
||||
stream.write_u8(1)?;
|
||||
entry
|
||||
}
|
||||
None => {
|
||||
log::debug!("No companion entry for `{name}`");
|
||||
stream.write_u8(0)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let fd = stream.recv_fd()?;
|
||||
log::trace!("New companion request from module `{name}`");
|
||||
thread::spawn(move || {
|
||||
unsafe {
|
||||
let mut s = UnixStream::from_raw_fd(fd);
|
||||
match s.write_u8(1) { // Ack
|
||||
Ok(_) => entry(fd),
|
||||
Err(_) => log::warn!("Ack failed?"),
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn load_module(fd: RawFd) -> Result<Option<ZygiskCompanionEntryFn>> {
|
||||
unsafe {
|
||||
let path = format!("/proc/self/fd/{fd}");
|
||||
let handle = dl::dlopen(&path, libc::RTLD_NOW)?;
|
||||
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
||||
let entry = libc::dlsym(handle, symbol.as_ptr());
|
||||
if entry.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
||||
Ok(Some(fnptr))
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,63 @@
|
||||
use bitflags::bitflags;
|
||||
use const_format::concatcp;
|
||||
use konst::primitive::parse_i32;
|
||||
use konst::unwrap_ctx;
|
||||
use log::LevelFilter;
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
pub const VERSION_NAME: &str = env!("VERSION_NAME");
|
||||
pub const VERSION_CODE: &str = env!("VERSION_CODE");
|
||||
pub const VERSION_FULL: &str = concatcp!(VERSION_NAME, " (", VERSION_CODE, ")");
|
||||
pub const MIN_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_KSU_VERSION")));
|
||||
pub const MAX_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MAX_KSU_VERSION")));
|
||||
pub const MIN_MAGISK_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_MAGISK_VERSION")));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[macro_export]
|
||||
macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp64 };
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[macro_export]
|
||||
macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp32 };
|
||||
}
|
||||
pub const PROP_CTL_RESTART: &str = "ctl.restart";
|
||||
pub const ZYGISK_LIBRARY: &str = "libzygisk.so";
|
||||
|
||||
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
|
||||
pub const PROP_SVC_ZYGOTE: &str = "init.svc.zygote";
|
||||
pub const ZYGISK_LOADER: &str = "libzygiskloader.so";
|
||||
pub const PATH_PCL: &str = "/system/etc/preloaded-classes";
|
||||
pub const PATH_SYSTEM_LIB: &str = "/system/lib64";
|
||||
pub const PATH_WORK_DIR: &str = "/dev/zygisk"; // TODO: Replace with /debug_ramdisk/zygisk
|
||||
pub const PATH_PROP_OVERLAY: &str = concatcp!(PATH_WORK_DIR, "/module.prop");
|
||||
pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, "/cp.sock");
|
||||
pub const PATH_FUSE_DIR: &str = concatcp!(PATH_WORK_DIR, "/fuse");
|
||||
pub const PATH_FUSE_PCL: &str = concatcp!(PATH_FUSE_DIR, "/preloaded-classes");
|
||||
|
||||
pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder";
|
||||
pub const PATH_MODULES_DIR: &str = "..";
|
||||
pub const PATH_MODULE_PROP: &str = "module.prop";
|
||||
pub const PATH_CP_BIN: &str = "bin/zygisk-cp";
|
||||
|
||||
pub const PATH_MODULE_DIR: &str = "..";
|
||||
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
||||
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
||||
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
|
||||
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
|
||||
pub const STATUS_ROOT_IMPL_NONE: &str = "❌ Unknown root implementation";
|
||||
pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too old";
|
||||
pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version";
|
||||
pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed";
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum DaemonSocketAction {
|
||||
PingHeartbeat,
|
||||
RequestLogcatFd,
|
||||
ReadNativeBridge,
|
||||
GetProcessFlags,
|
||||
ReadModules,
|
||||
RequestCompanionSocket,
|
||||
GetModuleDir,
|
||||
}
|
||||
|
||||
// Zygisk process flags
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ProcessFlags: u32 {
|
||||
const PROCESS_GRANTED_ROOT = 1 << 0;
|
||||
const PROCESS_ON_DENYLIST = 1 << 1;
|
||||
const PROCESS_ROOT_IS_KSU = 1 << 29;
|
||||
const PROCESS_ROOT_IS_MAGISK = 1 << 30;
|
||||
const PROCESS_IS_SYSUI = 1 << 31;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
use anyhow::{bail, Result};
|
||||
use std::ffi::{c_char, c_void};
|
||||
use nix::libc;
|
||||
|
||||
const ANDROID_NAMESPACE_TYPE_SHARED: u64 = 0x2;
|
||||
const ANDROID_DLEXT_USE_NAMESPACE: u64 = 0x200;
|
||||
pub const ANDROID_NAMESPACE_TYPE_SHARED: u64 = 0x2;
|
||||
pub const ANDROID_DLEXT_USE_NAMESPACE: u64 = 0x200;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct AndroidNamespace {
|
||||
pub struct AndroidNamespace {
|
||||
_unused: [u8; 0],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct AndroidDlextinfo {
|
||||
pub struct AndroidDlextinfo {
|
||||
pub flags: u64,
|
||||
pub reserved_addr: *mut c_void,
|
||||
pub reserved_size: libc::size_t,
|
||||
@@ -23,7 +22,7 @@ struct AndroidDlextinfo {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn android_dlopen_ext(
|
||||
pub fn android_dlopen_ext(
|
||||
filename: *const c_char,
|
||||
flags: libc::c_int,
|
||||
extinfo: *const AndroidDlextinfo,
|
||||
@@ -76,7 +75,7 @@ pub unsafe fn dlopen(path: &str, flags: i32) -> Result<*mut c_void> {
|
||||
let result = android_dlopen_ext(filename, flags, &info);
|
||||
if result.is_null() {
|
||||
let e = std::ffi::CStr::from_ptr(libc::dlerror()).to_string_lossy();
|
||||
bail!("dlopen failed: {}", e);
|
||||
bail!(e);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
1
zygiskd/src/external/binder_rs
vendored
1
zygiskd/src/external/binder_rs
vendored
Submodule zygiskd/src/external/binder_rs deleted from 6d958bb94a
328
zygiskd/src/fuse.rs
Normal file
328
zygiskd/src/fuse.rs
Normal file
@@ -0,0 +1,328 @@
|
||||
use std::cmp::min;
|
||||
use anyhow::{bail, Result};
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::{fs, thread};
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::time::{Duration, SystemTime};
|
||||
use fuser::{FileAttr, Filesystem, FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, ReplyOpen, Request};
|
||||
use libc::ENOENT;
|
||||
use log::{debug, error, info};
|
||||
use proc_maps::{get_process_maps, MapRange, Pid};
|
||||
use ptrace_do::{RawProcess, TracedProcess};
|
||||
use rustix::mount::mount_bind;
|
||||
use rustix::path::Arg;
|
||||
use rustix::process::getpid;
|
||||
use crate::{constants, dl};
|
||||
use crate::utils::LateInit;
|
||||
|
||||
pub struct DelegateFilesystem;
|
||||
|
||||
const fn attr(inode: u64, size: u64, kind: FileType) -> FileAttr {
|
||||
FileAttr {
|
||||
ino: inode,
|
||||
size,
|
||||
blocks: 0,
|
||||
atime: SystemTime::UNIX_EPOCH,
|
||||
mtime: SystemTime::UNIX_EPOCH,
|
||||
ctime: SystemTime::UNIX_EPOCH,
|
||||
crtime: SystemTime::UNIX_EPOCH,
|
||||
kind,
|
||||
perm: 0o644,
|
||||
nlink: 0,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
blksize: 0,
|
||||
flags: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const ANDROID_LIBC: &str = "bionic/libc.so";
|
||||
const ANDROID_LIBDL: &str = "bionic/libdl.so";
|
||||
|
||||
const INO_DIR: u64 = 1;
|
||||
const INO_PCL: u64 = 2;
|
||||
|
||||
static ATTR_DIR: FileAttr = attr(INO_DIR, 0, FileType::Directory);
|
||||
static ATTR_PCL: LateInit<FileAttr> = LateInit::new();
|
||||
|
||||
static PCL_CONTENT: LateInit<Vec<u8>> = LateInit::new();
|
||||
|
||||
const ENTRIES: &[(u64, FileType, &str)] = &[
|
||||
(INO_DIR, FileType::Directory, "."),
|
||||
(INO_DIR, FileType::Directory, ".."),
|
||||
(INO_PCL, FileType::RegularFile, "preloaded-classes"),
|
||||
];
|
||||
|
||||
const TTL: Duration = Duration::from_secs(1);
|
||||
|
||||
impl Filesystem for DelegateFilesystem {
|
||||
fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
||||
if parent != INO_DIR {
|
||||
reply.error(ENOENT);
|
||||
return;
|
||||
}
|
||||
match name.as_str().unwrap() {
|
||||
"preloaded-classes" => reply.entry(&TTL, &ATTR_PCL, 0),
|
||||
_ => reply.error(ENOENT),
|
||||
}
|
||||
}
|
||||
|
||||
fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) {
|
||||
match ino {
|
||||
INO_DIR => reply.attr(&TTL, &ATTR_DIR),
|
||||
INO_PCL => reply.attr(&TTL, &ATTR_PCL),
|
||||
_ => reply.error(ENOENT),
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&mut self, req: &Request<'_>, ino: u64, _flags: i32, reply: ReplyOpen) {
|
||||
if ino == INO_PCL {
|
||||
let pid = req.pid();
|
||||
let process = format!("/proc/{}/cmdline", pid);
|
||||
let process = fs::read_to_string(process).unwrap();
|
||||
let process = &process[..process.find('\0').unwrap()];
|
||||
info!("Process {} is reading preloaded-classes", process);
|
||||
if process == "zygote64" {
|
||||
ptrace_zygote(pid).unwrap();
|
||||
}
|
||||
}
|
||||
reply.opened(0, 0);
|
||||
}
|
||||
|
||||
fn read(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, size: u32, _flags: i32, _lock_owner: Option<u64>, reply: ReplyData) {
|
||||
let offset = offset as usize;
|
||||
let size = size as usize;
|
||||
if ino == INO_PCL {
|
||||
let len = PCL_CONTENT.len();
|
||||
if offset >= len {
|
||||
reply.data(&[]);
|
||||
} else {
|
||||
let end = min(offset + size, len);
|
||||
reply.data(&PCL_CONTENT[offset..end]);
|
||||
}
|
||||
} else {
|
||||
reply.error(ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
fn readdir(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) {
|
||||
if ino != INO_DIR {
|
||||
reply.error(ENOENT);
|
||||
return;
|
||||
}
|
||||
for (i, entry) in ENTRIES.iter().enumerate().skip(offset as usize) {
|
||||
if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply.ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn find_module_for_pid(pid: Pid, library: &str) -> Result<MapRange> {
|
||||
let maps = get_process_maps(pid)?;
|
||||
for map in maps.into_iter() {
|
||||
if let Some(p) = map.filename() {
|
||||
if p.as_str()?.contains(library) {
|
||||
return Ok(map);
|
||||
}
|
||||
}
|
||||
}
|
||||
bail!("Cannot find module {library} for pid {pid}");
|
||||
}
|
||||
|
||||
fn find_remote_procedure(
|
||||
pid: Pid,
|
||||
library: &str,
|
||||
local_addr: usize,
|
||||
) -> Result<usize> {
|
||||
let local_module = find_module_for_pid(getpid().as_raw_nonzero().get(), library)?;
|
||||
debug!(
|
||||
"Identifed local range {library} ({:?}) at {:x}",
|
||||
local_module.filename(),
|
||||
local_module.start()
|
||||
);
|
||||
|
||||
let remote_module = find_module_for_pid(pid, library)?;
|
||||
debug!(
|
||||
"Identifed remote range {library} ({:?}) at {:x}",
|
||||
remote_module.filename(),
|
||||
remote_module.start()
|
||||
);
|
||||
|
||||
Ok(local_addr - local_module.start() + remote_module.start())
|
||||
}
|
||||
|
||||
fn ptrace_zygote(pid: u32) -> Result<()> {
|
||||
static LAST: Mutex<u32> = Mutex::new(0);
|
||||
|
||||
let mut last = LAST.lock().unwrap();
|
||||
if *last == pid {
|
||||
return Ok(());
|
||||
}
|
||||
*last = pid;
|
||||
let (sender, receiver) = mpsc::channel::<()>();
|
||||
|
||||
let worker = move || -> Result<()> {
|
||||
info!("Injecting into pid {}", pid);
|
||||
let zygisk_lib = format!("{}/{}", constants::PATH_SYSTEM_LIB, constants::ZYGISK_LIBRARY);
|
||||
let lib_dir = CString::new(constants::PATH_SYSTEM_LIB)?;
|
||||
let zygisk_lib = CString::new(zygisk_lib)?;
|
||||
let libc_base = find_module_for_pid(pid as i32, ANDROID_LIBC)?.start();
|
||||
let mmap_remote = find_remote_procedure(
|
||||
pid as i32,
|
||||
ANDROID_LIBC,
|
||||
libc::mmap as usize,
|
||||
)?;
|
||||
let munmap_remote = find_remote_procedure(
|
||||
pid as i32,
|
||||
ANDROID_LIBC,
|
||||
libc::munmap as usize,
|
||||
)?;
|
||||
let dlopen_remote = find_remote_procedure(
|
||||
pid as i32,
|
||||
ANDROID_LIBDL,
|
||||
dl::android_dlopen_ext as usize,
|
||||
)?;
|
||||
let dlsym_remote = find_remote_procedure(
|
||||
pid as i32,
|
||||
ANDROID_LIBDL,
|
||||
libc::dlsym as usize,
|
||||
)?;
|
||||
|
||||
let tracer = TracedProcess::attach(RawProcess::new(pid as i32))?;
|
||||
sender.send(())?;
|
||||
let frame = tracer.next_frame()?;
|
||||
debug!("Waited for a frame");
|
||||
|
||||
// Map a buffer in the remote process
|
||||
let mmap_params: [usize; 6] = [
|
||||
0,
|
||||
0x1000,
|
||||
(libc::PROT_READ | libc::PROT_WRITE) as usize,
|
||||
(libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as usize,
|
||||
0,
|
||||
0,
|
||||
];
|
||||
let (regs, mut frame) = frame.invoke_remote(
|
||||
mmap_remote,
|
||||
libc_base,
|
||||
&mmap_params,
|
||||
)?;
|
||||
let buf_addr = regs.return_value();
|
||||
debug!("Buffer addr: {:x}", buf_addr);
|
||||
|
||||
// Find the address of __loader_android_create_namespace
|
||||
let sym = CString::new("__loader_android_create_namespace")?;
|
||||
frame.write_memory(buf_addr, sym.as_bytes_with_nul())?;
|
||||
let (regs, mut frame) = frame.invoke_remote(
|
||||
dlsym_remote,
|
||||
libc_base,
|
||||
&[libc::RTLD_DEFAULT as usize, buf_addr],
|
||||
)?;
|
||||
let android_create_namespace_remote = regs.return_value();
|
||||
debug!("__loader_android_create_namespace addr: {:x}", android_create_namespace_remote);
|
||||
|
||||
// Create a linker namespace for remote process
|
||||
frame.write_memory(buf_addr, zygisk_lib.as_bytes_with_nul())?;
|
||||
frame.write_memory(buf_addr + 0x100, lib_dir.as_bytes_with_nul())?;
|
||||
let ns_params: [usize; 7] = [
|
||||
buf_addr, // name
|
||||
buf_addr + 0x100, // ld_library_path
|
||||
0, // default_library_path
|
||||
dl::ANDROID_NAMESPACE_TYPE_SHARED as usize, // type
|
||||
0, // permitted_when_isolated_path
|
||||
0, // parent
|
||||
dlopen_remote, // caller_addr
|
||||
];
|
||||
let (regs, mut frame) = frame.invoke_remote(
|
||||
android_create_namespace_remote,
|
||||
libc_base,
|
||||
&ns_params,
|
||||
)?;
|
||||
let ns_addr = regs.return_value();
|
||||
debug!("Linker namespace addr: {:x}", ns_addr);
|
||||
|
||||
// Load zygisk into remote process
|
||||
let info = dl::AndroidDlextinfo {
|
||||
flags: dl::ANDROID_DLEXT_USE_NAMESPACE,
|
||||
reserved_addr: std::ptr::null_mut(),
|
||||
reserved_size: 0,
|
||||
relro_fd: 0,
|
||||
library_fd: 0,
|
||||
library_fd_offset: 0,
|
||||
library_namespace: ns_addr as *mut _,
|
||||
};
|
||||
let info = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
&info as *const _ as *const u8,
|
||||
std::mem::size_of::<dl::AndroidDlextinfo>(),
|
||||
)
|
||||
};
|
||||
frame.write_memory(buf_addr + 0x200, info)?;
|
||||
let (regs, mut frame) = frame.invoke_remote(
|
||||
dlopen_remote,
|
||||
libc_base,
|
||||
&[buf_addr, libc::RTLD_NOW as usize, buf_addr + 0x200],
|
||||
)?;
|
||||
let handle = regs.return_value();
|
||||
debug!("Load zygisk into remote process: {:x}", handle);
|
||||
|
||||
let entry = CString::new("entry")?;
|
||||
frame.write_memory(buf_addr, entry.as_bytes_with_nul())?;
|
||||
let (regs, frame) = frame.invoke_remote(
|
||||
dlsym_remote,
|
||||
libc_base,
|
||||
&[handle, buf_addr],
|
||||
)?;
|
||||
let entry = regs.return_value();
|
||||
debug!("Call zygisk entry: {:x}", entry);
|
||||
let (_, frame) = frame.invoke_remote(
|
||||
entry,
|
||||
libc_base,
|
||||
&[handle],
|
||||
)?;
|
||||
|
||||
// Cleanup
|
||||
let _ = frame.invoke_remote(
|
||||
munmap_remote,
|
||||
libc_base,
|
||||
&[buf_addr],
|
||||
)?;
|
||||
debug!("Cleaned up");
|
||||
Ok(())
|
||||
};
|
||||
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = worker() {
|
||||
error!("Crashed: {:?}", e);
|
||||
}
|
||||
});
|
||||
|
||||
receiver.recv()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
info!("Start zygisk fuse");
|
||||
fs::create_dir(constants::PATH_WORK_DIR)?;
|
||||
fs::create_dir(constants::PATH_FUSE_DIR)?;
|
||||
PCL_CONTENT.init(fs::read(constants::PATH_PCL)?);
|
||||
ATTR_PCL.init(attr(INO_PCL, PCL_CONTENT.len() as u64, FileType::RegularFile));
|
||||
let options = [
|
||||
fuser::MountOption::FSName(String::from("zygisk")),
|
||||
fuser::MountOption::AllowOther,
|
||||
fuser::MountOption::RO,
|
||||
];
|
||||
let session = fuser::spawn_mount2(
|
||||
DelegateFilesystem,
|
||||
constants::PATH_FUSE_DIR,
|
||||
&options,
|
||||
)?;
|
||||
mount_bind(constants::PATH_FUSE_PCL, constants::PATH_PCL)?;
|
||||
match session.guard.join() {
|
||||
Err(e) => bail!("Fuse mount crashed: {:?}", e),
|
||||
_ => bail!("Fuse mount exited unexpectedly"),
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,16 @@
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod companion;
|
||||
mod constants;
|
||||
mod dl;
|
||||
mod fuse;
|
||||
mod root_impl;
|
||||
mod utils;
|
||||
mod watchdog;
|
||||
mod zygiskd;
|
||||
|
||||
use clap::{Subcommand, Parser};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version = constants::VERSION_FULL, about, long_about = None)]
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Commands {
|
||||
/// Start zygisk watchdog
|
||||
Watchdog,
|
||||
/// Start zygisk daemon
|
||||
Daemon,
|
||||
/// Start zygisk companion
|
||||
Companion { fd: i32 },
|
||||
}
|
||||
use std::future::Future;
|
||||
use anyhow::Result;
|
||||
|
||||
|
||||
fn init_android_logger(tag: &str) {
|
||||
@@ -35,19 +21,29 @@ fn init_android_logger(tag: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
fn async_start<F: Future>(future: F) -> F::Output {
|
||||
let async_runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
async_runtime.block_on(future)
|
||||
}
|
||||
|
||||
fn start(name: &str) -> Result<()> {
|
||||
utils::switch_mount_namespace(1)?;
|
||||
root_impl::setup();
|
||||
match name.trim_start_matches("zygisk-") {
|
||||
"wd" => async_start(watchdog::main())?,
|
||||
"fuse" => fuse::main()?,
|
||||
"cp" => zygiskd::main()?,
|
||||
_ => println!("Available commands: wd, fuse, cp"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let process = std::env::args().next().unwrap();
|
||||
let nice_name = process.split('/').last().unwrap();
|
||||
init_android_logger(nice_name);
|
||||
|
||||
let cli = Args::parse();
|
||||
let result = match cli.command {
|
||||
Commands::Watchdog => watchdog::entry(),
|
||||
Commands::Daemon => zygiskd::entry(),
|
||||
Commands::Companion { fd } => companion::entry(fd),
|
||||
};
|
||||
|
||||
if let Err(e) = &result {
|
||||
if let Err(e) = start(nice_name) {
|
||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
||||
}
|
||||
}
|
||||
|
||||
68
zygiskd/src/root_impl/kernelsu.rs
Normal file
68
zygiskd/src/root_impl/kernelsu.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use crate::constants::{MAX_KSU_VERSION, MIN_KSU_VERSION};
|
||||
|
||||
const KERNEL_SU_OPTION: u32 = 0xdeadbeefu32;
|
||||
|
||||
const CMD_GET_VERSION: usize = 2;
|
||||
const CMD_UID_GRANTED_ROOT: usize = 12;
|
||||
const CMD_UID_SHOULD_UMOUNT: usize = 13;
|
||||
|
||||
pub enum Version {
|
||||
Supported,
|
||||
TooOld,
|
||||
Abnormal,
|
||||
}
|
||||
|
||||
pub fn get_kernel_su() -> Option<Version> {
|
||||
let mut version = 0;
|
||||
unsafe {
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32,
|
||||
CMD_GET_VERSION,
|
||||
&mut version as *mut i32,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
match version {
|
||||
0 => None,
|
||||
MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
|
||||
1..MIN_KSU_VERSION => Some(Version::TooOld),
|
||||
_ => Some(Version::Abnormal),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
let mut result: u32 = 0;
|
||||
let mut granted = false;
|
||||
unsafe {
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32,
|
||||
CMD_UID_GRANTED_ROOT,
|
||||
uid,
|
||||
&mut granted as *mut bool,
|
||||
&mut result as *mut u32,
|
||||
)
|
||||
};
|
||||
if result != KERNEL_SU_OPTION {
|
||||
log::warn!("uid_granted_root failed");
|
||||
}
|
||||
granted
|
||||
}
|
||||
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
let mut result: u32 = 0;
|
||||
let mut umount = false;
|
||||
unsafe {
|
||||
libc::prctl(
|
||||
KERNEL_SU_OPTION as i32,
|
||||
CMD_UID_SHOULD_UMOUNT,
|
||||
uid,
|
||||
&mut umount as *mut bool,
|
||||
&mut result as *mut u32,
|
||||
)
|
||||
};
|
||||
if result != KERNEL_SU_OPTION {
|
||||
log::warn!("uid_granted_root failed");
|
||||
}
|
||||
umount
|
||||
}
|
||||
63
zygiskd/src/root_impl/magisk.rs
Normal file
63
zygiskd/src/root_impl/magisk.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use std::process::{Command, Stdio};
|
||||
use crate::constants::MIN_MAGISK_VERSION;
|
||||
|
||||
pub enum Version {
|
||||
Supported,
|
||||
TooOld,
|
||||
}
|
||||
|
||||
pub fn get_magisk() -> Option<Version> {
|
||||
let version: Option<i32> = Command::new("magisk")
|
||||
.arg("-V")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.and_then(|output| output.trim().parse().ok());
|
||||
version.map(|version| {
|
||||
if version >= MIN_MAGISK_VERSION {
|
||||
Version::Supported
|
||||
} else {
|
||||
Version::TooOld
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg(format!("select 1 from policies where uid={uid} and policy=2 limit 1"))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|output| output.is_empty()) == Some(false)
|
||||
}
|
||||
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
let output = Command::new("pm")
|
||||
.args(["list", "packages", "--uid", &uid.to_string()])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok());
|
||||
let line = match output {
|
||||
Some(line) => line,
|
||||
None => return false,
|
||||
};
|
||||
let pkg = line
|
||||
.strip_prefix("package:")
|
||||
.and_then(|line| line.split(' ').next());
|
||||
let pkg = match pkg {
|
||||
Some(pkg) => pkg,
|
||||
None => return false,
|
||||
};
|
||||
Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg(format!("select 1 from denylist where package_name=\"{pkg}\" limit 1"))
|
||||
.stdout(Stdio::piped())
|
||||
.spawn().ok()
|
||||
.and_then(|child| child.wait_with_output().ok())
|
||||
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||
.map(|output| output.is_empty()) == Some(false)
|
||||
}
|
||||
58
zygiskd/src/root_impl/mod.rs
Normal file
58
zygiskd/src/root_impl/mod.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
mod kernelsu;
|
||||
mod magisk;
|
||||
|
||||
pub enum RootImpl {
|
||||
None,
|
||||
TooOld,
|
||||
Abnormal,
|
||||
Multiple,
|
||||
KernelSU,
|
||||
Magisk,
|
||||
}
|
||||
|
||||
// FIXME: OnceCell bugs on 32 bit
|
||||
static mut ROOT_IMPL: RootImpl = RootImpl::None;
|
||||
|
||||
pub fn setup() {
|
||||
let ksu_version = kernelsu::get_kernel_su();
|
||||
let magisk_version = magisk::get_magisk();
|
||||
|
||||
let impl_ = match (ksu_version, magisk_version) {
|
||||
(None, None) => RootImpl::None,
|
||||
(Some(_), Some(_)) => RootImpl::Multiple,
|
||||
(Some(ksu_version), None) => {
|
||||
match ksu_version {
|
||||
kernelsu::Version::Supported => RootImpl::KernelSU,
|
||||
kernelsu::Version::TooOld => RootImpl::TooOld,
|
||||
kernelsu::Version::Abnormal => RootImpl::Abnormal,
|
||||
}
|
||||
}
|
||||
(None, Some(magisk_version)) => {
|
||||
match magisk_version {
|
||||
magisk::Version::Supported => RootImpl::Magisk,
|
||||
magisk::Version::TooOld => RootImpl::TooOld,
|
||||
}
|
||||
}
|
||||
};
|
||||
unsafe { ROOT_IMPL = impl_; }
|
||||
}
|
||||
|
||||
pub fn get_impl() -> &'static RootImpl {
|
||||
unsafe { &ROOT_IMPL }
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
match get_impl() {
|
||||
RootImpl::KernelSU => kernelsu::uid_granted_root(uid),
|
||||
RootImpl::Magisk => magisk::uid_granted_root(uid),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
match get_impl() {
|
||||
RootImpl::KernelSU => kernelsu::uid_should_umount(uid),
|
||||
RootImpl::Magisk => magisk::uid_should_umount(uid),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,43 @@
|
||||
use anyhow::Result;
|
||||
use nix::unistd::gettid;
|
||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
|
||||
use std::ffi::{c_char, CStr, CString};
|
||||
use std::os::fd::AsFd;
|
||||
use std::os::unix::net::UnixListener;
|
||||
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
use rustix::net::{AddressFamily, bind_unix, listen, socket, SocketAddrUnix, SocketType};
|
||||
use rustix::thread::gettid;
|
||||
|
||||
pub fn random_string() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $debug };
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $release };
|
||||
}
|
||||
|
||||
pub struct LateInit<T> {
|
||||
cell: OnceLock<T>,
|
||||
}
|
||||
|
||||
impl<T> LateInit<T> {
|
||||
pub const fn new() -> Self {
|
||||
LateInit { cell: OnceLock::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 set_socket_create_context(context: &str) -> Result<()> {
|
||||
@@ -15,22 +45,51 @@ pub fn set_socket_create_context(context: &str) -> Result<()> {
|
||||
match fs::write(path, context) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => {
|
||||
let path = format!("/proc/self/task/{}/attr/sockcreate", gettid().as_raw());
|
||||
let path = format!("/proc/self/task/{}/attr/sockcreate", gettid().as_raw_nonzero());
|
||||
fs::write(path, context)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_native_bridge() -> String {
|
||||
std::env::var("NATIVE_BRIDGE").unwrap_or_default()
|
||||
pub fn chcon(path: &str, context: &str) -> Result<()> {
|
||||
Command::new("chcon").arg(context).arg(path).status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn log_raw(level: i32, tag: &str, message: &str) -> Result<()> {
|
||||
let tag = CString::new(tag)?;
|
||||
let message = CString::new(message)?;
|
||||
unsafe {
|
||||
__android_log_print(level, tag.as_ptr(), message.as_ptr());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_property(name: &str) -> Result<String> {
|
||||
let name = CString::new(name)?;
|
||||
let mut buf = vec![0u8; 92];
|
||||
let prop = unsafe {
|
||||
__system_property_get(name.as_ptr(), buf.as_mut_ptr() as *mut c_char);
|
||||
CStr::from_bytes_until_nul(&buf)?
|
||||
};
|
||||
Ok(prop.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
pub fn set_property(name: &str, value: &str) -> Result<()> {
|
||||
Command::new("resetprop")
|
||||
.arg(name)
|
||||
.arg(value)
|
||||
.spawn()?.wait()?;
|
||||
let name = CString::new(name)?;
|
||||
let value = CString::new(value)?;
|
||||
unsafe {
|
||||
__system_property_set(name.as_ptr(), value.as_ptr());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn switch_mount_namespace(pid: i32) -> Result<()> {
|
||||
let cwd = std::env::current_dir()?;
|
||||
let mnt = fs::File::open(format!("/proc/{}/ns/mnt", pid))?;
|
||||
rustix::thread::move_into_link_name_space(mnt.as_fd(), None)?;
|
||||
std::env::set_current_dir(cwd)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -40,6 +99,7 @@ pub trait UnixStreamExt {
|
||||
fn read_usize(&mut self) -> Result<usize>;
|
||||
fn read_string(&mut self) -> Result<String>;
|
||||
fn write_u8(&mut self, value: u8) -> Result<()>;
|
||||
fn write_u32(&mut self, value: u32) -> Result<()>;
|
||||
fn write_usize(&mut self, value: usize) -> Result<()>;
|
||||
fn write_string(&mut self, value: &str) -> Result<()>;
|
||||
}
|
||||
@@ -75,6 +135,11 @@ impl UnixStreamExt for UnixStream {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<()> {
|
||||
self.write_all(&value.to_ne_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_usize(&mut self, value: usize) -> Result<()> {
|
||||
self.write_all(&value.to_ne_bytes())?;
|
||||
Ok(())
|
||||
@@ -87,12 +152,18 @@ impl UnixStreamExt for UnixStream {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with SockAddrExt::from_abstract_name when it's stable
|
||||
pub fn abstract_namespace_socket(name: &str) -> Result<UnixListener> {
|
||||
let addr = UnixAddr::new_abstract(name.as_bytes())?;
|
||||
let socket = nix::sys::socket::socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?;
|
||||
nix::sys::socket::bind(socket, &addr)?;
|
||||
nix::sys::socket::listen(socket, 2)?;
|
||||
let listener = unsafe { UnixListener::from_raw_fd(socket) };
|
||||
Ok(listener)
|
||||
pub fn unix_listener_from_path(path: &str) -> Result<UnixListener> {
|
||||
let _ = fs::remove_file(path);
|
||||
let addr = SocketAddrUnix::new(path)?;
|
||||
let socket = socket(AddressFamily::UNIX, SocketType::STREAM, None)?;
|
||||
bind_unix(&socket, &addr)?;
|
||||
listen(&socket, 2)?;
|
||||
chcon(path, "u:object_r:magisk_file:s0")?;
|
||||
Ok(UnixListener::from(socket))
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
||||
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
||||
fn __system_property_set(name: *const c_char, value: *const c_char) -> u32;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
use crate::{constants, utils};
|
||||
use crate::{constants, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use nix::unistd::{getgid, getuid, Pid};
|
||||
use std::process::{Child, Command};
|
||||
use std::sync::mpsc;
|
||||
use std::{fs, thread};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::fs;
|
||||
use std::future::Future;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
use binder::IBinder;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, error, info};
|
||||
use rustix::mount::mount_bind;
|
||||
use rustix::process::{getgid, getuid, kill_process, Pid, Signal};
|
||||
use tokio::process::{Child, Command};
|
||||
use crate::utils::LateInit;
|
||||
|
||||
static mut LOCK: Option<UnixListener> = None;
|
||||
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
||||
|
||||
pub fn entry() -> Result<()> {
|
||||
log::info!("Start zygisksu watchdog");
|
||||
pub async fn main() -> Result<()> {
|
||||
let result = run().await;
|
||||
set_prop_hint(constants::STATUS_CRASHED)?;
|
||||
result
|
||||
}
|
||||
|
||||
async fn run() -> Result<()> {
|
||||
info!("Start zygisk watchdog");
|
||||
check_permission()?;
|
||||
ensure_single_instance()?;
|
||||
spawn_daemon()
|
||||
mount_prop().await?;
|
||||
if check_and_set_hint()? == false {
|
||||
log::warn!("Requirements not met, exiting");
|
||||
return Ok(());
|
||||
}
|
||||
spawn_daemon().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_fuse() -> Result<()> {
|
||||
Command::new("bin/zygisk-fuse").spawn()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_permission() -> Result<()> {
|
||||
log::info!("Check permission");
|
||||
info!("Check permission");
|
||||
let uid = getuid();
|
||||
if uid.as_raw() != 0 {
|
||||
if !uid.is_root() {
|
||||
bail!("UID is not 0");
|
||||
}
|
||||
|
||||
let gid = getgid();
|
||||
if gid.as_raw() != 0 {
|
||||
if !gid.is_root() {
|
||||
bail!("GID is not 0");
|
||||
}
|
||||
|
||||
@@ -39,67 +60,103 @@ fn check_permission() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_single_instance() -> Result<()> {
|
||||
log::info!("Ensure single instance");
|
||||
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
|
||||
match utils::abstract_namespace_socket(&name) {
|
||||
Ok(socket) => unsafe { LOCK = Some(socket) },
|
||||
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
|
||||
async fn mount_prop() -> Result<()> {
|
||||
let module_prop_file = fs::File::open(constants::PATH_MODULE_PROP)?;
|
||||
let mut section = 0;
|
||||
let mut sections: [String; 2] = [String::new(), String::new()];
|
||||
let lines = BufReader::new(module_prop_file).lines();
|
||||
for line in lines {
|
||||
let line = line?;
|
||||
if line.starts_with("description=") {
|
||||
sections[0].push_str("description=");
|
||||
sections[1].push_str(line.trim_start_matches("description="));
|
||||
sections[1].push('\n');
|
||||
section = 1;
|
||||
} else {
|
||||
sections[section].push_str(&line);
|
||||
sections[section].push('\n');
|
||||
}
|
||||
}
|
||||
PROP_SECTIONS.init(sections);
|
||||
|
||||
info!("Mount {} -> {}", constants::PATH_PROP_OVERLAY, constants::PATH_MODULE_PROP);
|
||||
fs::File::create(constants::PATH_PROP_OVERLAY)?;
|
||||
mount_bind(constants::PATH_PROP_OVERLAY, constants::PATH_MODULE_PROP)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_daemon() -> Result<()> {
|
||||
fn set_prop_hint(hint: &str) -> Result<()> {
|
||||
let mut file = fs::File::create(constants::PATH_PROP_OVERLAY)?;
|
||||
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(PROP_SECTIONS[1].as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_and_set_hint() -> Result<bool> {
|
||||
let root_impl = root_impl::get_impl();
|
||||
match root_impl {
|
||||
root_impl::RootImpl::None => set_prop_hint(constants::STATUS_ROOT_IMPL_NONE)?,
|
||||
root_impl::RootImpl::TooOld => set_prop_hint(constants::STATUS_ROOT_IMPL_TOO_OLD)?,
|
||||
root_impl::RootImpl::Abnormal => set_prop_hint(constants::STATUS_ROOT_IMPL_ABNORMAL)?,
|
||||
root_impl::RootImpl::Multiple => set_prop_hint(constants::STATUS_ROOT_IMPL_MULTIPLE)?,
|
||||
_ => {
|
||||
set_prop_hint(constants::STATUS_LOADED)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn spawn_daemon() -> Result<()> {
|
||||
let mut lives = 5;
|
||||
loop {
|
||||
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
||||
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
||||
let mut child_ids = vec![];
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let mut spawn = |mut daemon: Child| {
|
||||
child_ids.push(daemon.id());
|
||||
let sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let result = daemon.wait().unwrap();
|
||||
log::error!("Daemon process {} died: {}", daemon.id(), result);
|
||||
drop(daemon);
|
||||
let _ = sender.send(());
|
||||
});
|
||||
};
|
||||
if let Ok(it) = daemon32 { spawn(it) }
|
||||
if let Ok(it) = daemon64 { spawn(it) }
|
||||
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
|
||||
let daemon = Command::new(constants::PATH_CP_BIN).spawn()?;
|
||||
let daemon_pid = daemon.id().unwrap();
|
||||
|
||||
let mut binder = loop {
|
||||
if receiver.try_recv().is_ok() {
|
||||
bail!("Daemon died before system server ready");
|
||||
}
|
||||
match binder::get_service("activity") {
|
||||
Some(binder) => break binder,
|
||||
None => {
|
||||
log::trace!("System server not ready, wait for 1s...");
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
async fn daemon_holder(mut daemon: Child) -> Result<()> {
|
||||
let result = daemon.wait().await?;
|
||||
bail!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
||||
}
|
||||
futures.push(Box::pin(daemon_holder(daemon)));
|
||||
|
||||
async fn binder_listener() -> Result<()> {
|
||||
let mut binder = loop {
|
||||
match binder::get_service("activity") {
|
||||
Some(binder) => break binder,
|
||||
None => {
|
||||
log::trace!("System server not ready, wait for 1s...");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
log::info!("System server ready, restore native bridge");
|
||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
||||
|
||||
loop {
|
||||
if receiver.try_recv().is_ok() || binder.ping_binder().is_err() { break; }
|
||||
thread::sleep(Duration::from_secs(1))
|
||||
info!("System server ready");
|
||||
|
||||
loop {
|
||||
if binder.ping_binder().is_err() { break; }
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
bail!("System server died");
|
||||
}
|
||||
for child in child_ids {
|
||||
let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL);
|
||||
futures.push(Box::pin(binder_listener()));
|
||||
|
||||
if let Err(e) = futures.next().await.unwrap() {
|
||||
error!("{}", e);
|
||||
}
|
||||
|
||||
debug!("Killing child process {}", daemon_pid);
|
||||
let _ = kill_process(Pid::from_raw(daemon_pid as i32).unwrap(), Signal::Kill);
|
||||
|
||||
lives -= 1;
|
||||
if lives == 0 {
|
||||
bail!("Too many crashes, abort");
|
||||
}
|
||||
|
||||
log::error!("Restarting zygote...");
|
||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
|
||||
utils::set_property(constants::PROP_SVC_ZYGOTE, "restart")?;
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
error!("Restarting zygote...");
|
||||
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
use crate::constants::DaemonSocketAction;
|
||||
use std::ffi::c_void;
|
||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::{constants, lp_select, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use memfd::Memfd;
|
||||
use nix::{
|
||||
fcntl::{fcntl, FcntlArg, FdFlag},
|
||||
libc::self,
|
||||
};
|
||||
use crate::{constants, dl, root_impl, utils};
|
||||
use anyhow::Result;
|
||||
use passfd::FdPassingExt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::ffi::c_char;
|
||||
use std::fs;
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::os::unix::{
|
||||
net::{UnixListener, UnixStream},
|
||||
prelude::AsRawFd,
|
||||
};
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use rustix::fs::fstat;
|
||||
use rustix::process::{set_parent_process_death_signal, Signal};
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
struct Module {
|
||||
name: String,
|
||||
memfd: Memfd,
|
||||
companion: Mutex<Option<UnixStream>>,
|
||||
lib_fd: OwnedFd,
|
||||
entry: Option<ZygiskCompanionEntryFn>,
|
||||
}
|
||||
|
||||
struct Context {
|
||||
native_bridge: String,
|
||||
modules: Vec<Module>,
|
||||
}
|
||||
|
||||
pub fn entry() -> Result<()> {
|
||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
||||
pub fn main() -> Result<()> {
|
||||
log::info!("Start zygisk companion");
|
||||
set_parent_process_death_signal(Some(Signal::Kill))?;
|
||||
|
||||
let arch = get_arch()?;
|
||||
let arch = utils::get_property("ro.product.cpu.abi")?;
|
||||
log::debug!("Daemon architecture: {arch}");
|
||||
|
||||
log::info!("Load modules");
|
||||
let modules = load_modules(arch)?;
|
||||
let modules = load_modules(&arch)?;
|
||||
|
||||
let context = Context {
|
||||
native_bridge: utils::get_native_bridge(),
|
||||
modules,
|
||||
};
|
||||
let context = Arc::new(context);
|
||||
@@ -63,29 +60,17 @@ pub fn entry() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_arch() -> Result<&'static str> {
|
||||
let output = Command::new("getprop").arg("ro.product.cpu.abi").output()?;
|
||||
let system_arch = String::from_utf8(output.stdout)?;
|
||||
if system_arch.contains("arm") {
|
||||
return Ok(lp_select!("armeabi-v7a", "arm64-v8a"))
|
||||
}
|
||||
if system_arch.contains("x86") {
|
||||
return Ok(lp_select!("x86", "x86_64"))
|
||||
}
|
||||
bail!("Unsupported system architecture: {}", system_arch);
|
||||
}
|
||||
|
||||
fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
let mut modules = Vec::new();
|
||||
let dir = match fs::read_dir(constants::PATH_MODULE_DIR) {
|
||||
let dir = match fs::read_dir(constants::PATH_MODULES_DIR) {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => {
|
||||
log::warn!("Failed reading modules directory: {}", e);
|
||||
return Ok(modules);
|
||||
}
|
||||
};
|
||||
for entry_result in dir.into_iter() {
|
||||
let entry = entry_result?;
|
||||
for entry in dir.into_iter() {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name().into_string().unwrap();
|
||||
let so_path = entry.path().join(format!("zygisk/{arch}.so"));
|
||||
let disabled = entry.path().join("disable");
|
||||
@@ -93,33 +78,30 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
continue;
|
||||
}
|
||||
log::info!(" Loading module `{name}`...");
|
||||
let memfd = match create_memfd(&name, &so_path) {
|
||||
Ok(memfd) => memfd,
|
||||
let lib_fd = match create_library_fd(&so_path) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to create memfd for `{name}`: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let companion = match spawn_companion(&name, &memfd) {
|
||||
Ok(companion) => companion,
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to spawn companion for `{name}`: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let companion = Mutex::new(companion);
|
||||
let module = Module { name, memfd, companion };
|
||||
let entry = resolve_module(&so_path.to_string_lossy())?;
|
||||
let module = Module { name, lib_fd, entry };
|
||||
modules.push(module);
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
}
|
||||
|
||||
fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
|
||||
let opts = memfd::MemfdOptions::default().allow_sealing(true);
|
||||
let memfd = opts.create(name)?;
|
||||
#[cfg(debug_assertions)]
|
||||
fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
Ok(OwnedFd::from(fs::File::open(so_path)?))
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
let opts = memfd::MemfdOptions::default().allow_sealing(true);
|
||||
let memfd = opts.create("jit-cache")?;
|
||||
let file = fs::File::open(so_path)?;
|
||||
let mut reader = std::io::BufReader::new(file);
|
||||
let mut writer = memfd.as_file();
|
||||
@@ -132,38 +114,26 @@ fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
|
||||
seals.insert(memfd::FileSeal::SealSeal);
|
||||
memfd.add_seals(&seals)?;
|
||||
|
||||
Ok(memfd)
|
||||
Ok(OwnedFd::from(memfd.into_file()))
|
||||
}
|
||||
|
||||
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 listener = utils::abstract_namespace_socket(&name)?;
|
||||
log::debug!("Daemon socket: {name}");
|
||||
log::debug!("Daemon socket: {}", constants::PATH_CP_SOCKET);
|
||||
let listener = utils::unix_listener_from_path(constants::PATH_CP_SOCKET)?;
|
||||
Ok(listener)
|
||||
}
|
||||
|
||||
fn spawn_companion(name: &str, memfd: &Memfd) -> Result<Option<UnixStream>> {
|
||||
let (mut daemon, companion) = UnixStream::pair()?;
|
||||
// Remove FD_CLOEXEC flag
|
||||
fcntl(companion.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
|
||||
|
||||
let process = std::env::args().next().unwrap();
|
||||
let nice_name = process.split('/').last().unwrap();
|
||||
Command::new(&process)
|
||||
.arg0(format!("{}-{}", nice_name, name))
|
||||
.arg("companion")
|
||||
.arg(format!("{}", companion.as_raw_fd()))
|
||||
.spawn()?;
|
||||
drop(companion);
|
||||
|
||||
daemon.write_string(name)?;
|
||||
daemon.send_fd(memfd.as_raw_fd())?;
|
||||
match daemon.read_u8()? {
|
||||
0 => Ok(None),
|
||||
1 => Ok(Some(daemon)),
|
||||
_ => bail!("Invalid companion response"),
|
||||
fn resolve_module(path: &str) -> Result<Option<ZygiskCompanionEntryFn>> {
|
||||
unsafe {
|
||||
let handle = dl::dlopen(path, libc::RTLD_NOW)?;
|
||||
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
||||
let entry = libc::dlsym(handle, symbol.as_ptr());
|
||||
if entry.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
||||
Ok(Some(fnptr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,53 +152,66 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
Err(_) => break,
|
||||
};
|
||||
let tag = stream.read_string()?;
|
||||
let tag = std::ffi::CString::new(tag)?;
|
||||
let message = stream.read_string()?;
|
||||
let message = std::ffi::CString::new(message)?;
|
||||
unsafe {
|
||||
__android_log_print(level as i32, tag.as_ptr(), message.as_ptr());
|
||||
}
|
||||
utils::log_raw(level as i32, &tag, &message)?;
|
||||
}
|
||||
}
|
||||
DaemonSocketAction::ReadNativeBridge => {
|
||||
stream.write_string(&context.native_bridge)?;
|
||||
DaemonSocketAction::GetProcessFlags => {
|
||||
let uid = stream.read_u32()? as i32;
|
||||
let mut flags = ProcessFlags::empty();
|
||||
if root_impl::uid_granted_root(uid) {
|
||||
flags |= ProcessFlags::PROCESS_GRANTED_ROOT;
|
||||
}
|
||||
if root_impl::uid_should_umount(uid) {
|
||||
flags |= ProcessFlags::PROCESS_ON_DENYLIST;
|
||||
}
|
||||
match root_impl::get_impl() {
|
||||
root_impl::RootImpl::KernelSU => flags |= ProcessFlags::PROCESS_ROOT_IS_KSU,
|
||||
root_impl::RootImpl::Magisk => flags |= ProcessFlags::PROCESS_ROOT_IS_MAGISK,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
log::trace!("Uid {} granted root: {}", uid, flags.contains(ProcessFlags::PROCESS_GRANTED_ROOT));
|
||||
log::trace!("Uid {} on denylist: {}", uid, flags.contains(ProcessFlags::PROCESS_ON_DENYLIST));
|
||||
stream.write_u32(flags.bits())?;
|
||||
}
|
||||
DaemonSocketAction::ReadModules => {
|
||||
stream.write_usize(context.modules.len())?;
|
||||
for module in context.modules.iter() {
|
||||
stream.write_string(&module.name)?;
|
||||
stream.send_fd(module.memfd.as_raw_fd())?;
|
||||
stream.send_fd(module.lib_fd.as_raw_fd())?;
|
||||
}
|
||||
}
|
||||
DaemonSocketAction::RequestCompanionSocket => {
|
||||
let index = stream.read_usize()?;
|
||||
let module = &context.modules[index];
|
||||
let mut companion = module.companion.lock().unwrap();
|
||||
match companion.as_ref() {
|
||||
Some(sock) => {
|
||||
if let Err(_) = sock.send_fd(stream.as_raw_fd()) {
|
||||
log::error!("Companion of module `{}` crashed", module.name);
|
||||
companion.take();
|
||||
stream.write_u8(0)?;
|
||||
}
|
||||
// Ok: Send by companion
|
||||
}
|
||||
match module.entry {
|
||||
None => {
|
||||
stream.write_u8(0)?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(companion) => {
|
||||
stream.write_u8(1)?;
|
||||
let st0 = fstat(&stream)?;
|
||||
unsafe { companion(stream.as_raw_fd()); }
|
||||
// Only close client if it is the same file so we don't
|
||||
// accidentally close a re-used file descriptor.
|
||||
// This check is required because the module companion
|
||||
// handler could've closed the file descriptor already.
|
||||
if let Ok(st1) = fstat(&stream) {
|
||||
if st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino {
|
||||
std::mem::forget(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DaemonSocketAction::GetModuleDir => {
|
||||
let index = stream.read_usize()?;
|
||||
let module = &context.modules[index];
|
||||
let dir = format!("{}/{}", constants::PATH_MODULE_DIR, module.name);
|
||||
let dir = format!("{}/{}", constants::PATH_MODULES_DIR, module.name);
|
||||
let dir = fs::File::open(dir)?;
|
||||
stream.send_fd(dir.as_raw_fd())?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user