You've already forked ZygiskNext
mirror of
https://github.com/Dr-TSNG/ZygiskNext.git
synced 2025-08-27 23:46:34 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abbca19c82 | ||
|
|
4587e39964 | ||
|
|
9df4fb64cd | ||
|
|
9f28e0a7ad | ||
|
|
8a80586fb8 | ||
|
|
218659dcbf | ||
|
|
8c0d5b5395 | ||
|
|
9051f59bf6 | ||
|
|
5f2dd50703 | ||
|
|
77cb323506 | ||
|
|
2b41a8336c | ||
|
|
e730ccd9b2 | ||
|
|
db47f03728 | ||
|
|
42503e7cfe | ||
|
|
14c920c553 | ||
|
|
ceaa2d431c | ||
|
|
ccb5764b72 |
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -7,12 +7,13 @@ body:
|
||||
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/复现步骤
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -3,6 +3,12 @@ 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?/你的请求是否与某个问题相关?
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -43,10 +43,9 @@ jobs:
|
||||
|
||||
- name: Setup Rust
|
||||
run: |
|
||||
rustup target add armv7-linux-androideabi
|
||||
rustup override set nightly
|
||||
rustup target add aarch64-linux-android
|
||||
rustup target add x86_64-linux-android
|
||||
rustup target add i686-linux-android
|
||||
|
||||
- name: Set up ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
@@ -9,6 +9,7 @@ Also works as standalone loader for Magisk.
|
||||
### General
|
||||
|
||||
+ No multiple root implementation installed
|
||||
+ SELinux enforcing: We now rely on SELinux to prevent `vold` from aborting our fuse connection
|
||||
|
||||
### KernelSU
|
||||
|
||||
@@ -18,7 +19,7 @@ Also works as standalone loader for Magisk.
|
||||
|
||||
### Magisk
|
||||
|
||||
+ Minimal version: 25208
|
||||
+ Minimal version: 26300
|
||||
+ Original Zygisk turned off
|
||||
|
||||
## Compatibility
|
||||
|
||||
@@ -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,18 +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.7.1")
|
||||
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(25208)
|
||||
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)
|
||||
|
||||
|
||||
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.1-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
|
||||
|
||||
@@ -2,7 +2,7 @@ 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? {
|
||||
@@ -35,6 +35,7 @@ android {
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
abiFilters("arm64-v8a", "x86_64")
|
||||
ccachePatch?.let {
|
||||
arguments += "NDK_CCACHE=$it"
|
||||
}
|
||||
|
||||
@@ -13,16 +13,7 @@ LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := zygisk_loader
|
||||
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 := zygisk_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)/%=%)
|
||||
|
||||
@@ -8,32 +8,14 @@
|
||||
|
||||
namespace zygiskd {
|
||||
|
||||
bool sMagicRead = false;
|
||||
static std::string sSocketName;
|
||||
|
||||
void ReadMagic() {
|
||||
sMagicRead = true;
|
||||
char magic[PATH_MAX]{0};
|
||||
auto fp = fopen(kZygiskMagic, "r");
|
||||
if (fp == nullptr) {
|
||||
PLOGE("Open magic file");
|
||||
return;
|
||||
}
|
||||
fgets(magic, PATH_MAX, fp);
|
||||
fclose(fp);
|
||||
sSocketName.append(LP_SELECT("zygiskd32", "zygiskd64")).append(magic);
|
||||
LOGD("Socket name: %s", sSocketName.data());
|
||||
}
|
||||
|
||||
int Connect(uint8_t retry) {
|
||||
if (!sMagicRead) ReadMagic();
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
struct sockaddr_un addr{
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path={0},
|
||||
};
|
||||
strcpy(addr.sun_path + 1, sSocketName.data());
|
||||
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);
|
||||
@@ -66,16 +48,6 @@ namespace zygiskd {
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::string ReadNativeBridge() {
|
||||
UniqueFd fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
PLOGE("ReadNativeBridge");
|
||||
return "";
|
||||
}
|
||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::ReadNativeBridge);
|
||||
return socket_utils::read_string(fd);
|
||||
}
|
||||
|
||||
uint32_t GetProcessFlags(uid_t uid) {
|
||||
UniqueFd fd = Connect(1);
|
||||
if (fd == -1) {
|
||||
|
||||
@@ -5,13 +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 auto kZygiskMagic = "/system/zygisk_magic";
|
||||
constexpr auto kCPSocketPath = "/dev/zygisk/cp.sock";
|
||||
|
||||
class UniqueFd {
|
||||
using Fd = int;
|
||||
@@ -54,7 +48,6 @@ namespace zygiskd {
|
||||
enum class SocketAction {
|
||||
PingHeartBeat,
|
||||
RequestLogcatFd,
|
||||
ReadNativeBridge,
|
||||
GetProcessFlags,
|
||||
ReadModules,
|
||||
RequestCompanionSocket,
|
||||
@@ -65,8 +58,6 @@ namespace zygiskd {
|
||||
|
||||
int RequestLogcatFd();
|
||||
|
||||
std::string ReadNativeBridge();
|
||||
|
||||
std::vector<Module> ReadModules();
|
||||
|
||||
uint32_t GetProcessFlags(uid_t uid);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,22 +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();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ 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();
|
||||
@@ -246,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
|
||||
@@ -449,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);
|
||||
@@ -531,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 */
|
||||
@@ -585,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;
|
||||
@@ -615,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();
|
||||
@@ -624,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 */
|
||||
@@ -674,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() {
|
||||
@@ -716,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();
|
||||
|
||||
@@ -726,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;
|
||||
}
|
||||
|
||||
@@ -11,13 +11,16 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +37,7 @@ void revert_unmount_ksu() {
|
||||
ksu_loop = info.source;
|
||||
continue;
|
||||
}
|
||||
// Unmount everything on /data/adb except ksu module dir
|
||||
// Unmount everything mounted to /data/adb
|
||||
if (info.target.starts_with("/data/adb")) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
@@ -44,6 +47,10 @@ void revert_unmount_ksu() {
|
||||
&& 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
|
||||
@@ -68,6 +75,14 @@ void revert_unmount_magisk() {
|
||||
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)) {
|
||||
|
||||
@@ -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") "/libzygisk_injector.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("%s", dlerror());
|
||||
break;
|
||||
}
|
||||
|
||||
auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf");
|
||||
if (original_native_bridge_itf == nullptr) {
|
||||
LOGE("%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,7 +4,7 @@ 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
|
||||
@@ -15,6 +15,7 @@ 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
|
||||
@@ -27,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"
|
||||
@@ -46,8 +47,8 @@ androidComponents.onVariants { variant ->
|
||||
expand(
|
||||
"moduleId" to moduleId,
|
||||
"moduleName" to moduleName,
|
||||
"versionName" to "$verName ($verCode-$variantLowered)",
|
||||
"versionCode" to verCode,
|
||||
"versionName" to "$verName ($verCode-$commitHash-$variantLowered)",
|
||||
"versionCode" to verCode
|
||||
)
|
||||
}
|
||||
from("$projectDir/src") {
|
||||
|
||||
@@ -61,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"
|
||||
@@ -101,60 +101,28 @@ extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
|
||||
extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
||||
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
||||
|
||||
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
|
||||
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true
|
||||
|
||||
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/libzygisk_injector.so' "$MODPATH/system/lib" true
|
||||
extract "$ZIPFILE" 'lib/x86/libzygisk_loader.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/libzygisk_injector.so' "$MODPATH/system/lib64" true
|
||||
extract "$ZIPFILE" 'lib/x86_64/libzygisk_loader.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/libzygisk_injector.so' "$MODPATH/system/lib" true
|
||||
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_loader.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/libzygisk_injector.so' "$MODPATH/system/lib64" true
|
||||
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_loader.so' "$MODPATH/system/lib64" true
|
||||
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
|
||||
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 "- Generating magic"
|
||||
MAGIC=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
||||
echo -n "$MAGIC" > "$MODPATH/system/zygisk_magic"
|
||||
|
||||
ui_print "- Setting permissions"
|
||||
chmod 0744 "$MODPATH/daemon.sh"
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,8 +6,6 @@ if [ "$ZYGISK_ENABLED" ]; then
|
||||
fi
|
||||
|
||||
cd "$MODDIR"
|
||||
getprop ro.dalvik.vm.native.bridge > /dev/.native_bridge
|
||||
resetprop ro.dalvik.vm.native.bridge libzygisk_loader.so
|
||||
|
||||
if [ "$(which magisk)" ]; then
|
||||
for file in ../*; do
|
||||
@@ -21,3 +19,6 @@ if [ "$(which magisk)" ]; then
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||
unshare -m sh -c "bin/zygisk-fuse &"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
deny vold fusectlfs file write
|
||||
|
||||
allow * tmpfs * *
|
||||
allow zygote appdomain_tmpfs dir *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
|
||||
@@ -8,8 +8,6 @@ if [ "$ZYGISK_ENABLED" ]; then
|
||||
fi
|
||||
|
||||
cd "$MODDIR"
|
||||
export NATIVE_BRIDGE=$(cat /dev/.native_bridge)
|
||||
rm /dev/.native_bridge
|
||||
|
||||
if [ "$(which magisk)" ]; then
|
||||
for file in ../*; do
|
||||
@@ -24,6 +22,5 @@ if [ "$(which magisk)" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
log -p i -t "zygisksu" "Start watchdog"
|
||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1
|
||||
unshare -m sh -c "bin/zygisk-wd &"
|
||||
|
||||
@@ -6,10 +6,6 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("com.android.library") version "7.4.2"
|
||||
id("com.android.application") version "7.4.2"
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
|
||||
@@ -9,21 +9,22 @@ rust-version = "1.69"
|
||||
android_logger = "0.13"
|
||||
anyhow = { version = "1.0", features = ["backtrace"] }
|
||||
bitflags = { version = "2.3" }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
const_format = "0.2"
|
||||
futures = "0.3"
|
||||
konst = "0.3"
|
||||
lazy_static = "1.4"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
memfd = "0.6"
|
||||
nix = { version = "0.26", features = ["process","poll"] }
|
||||
num_enum = "0.5"
|
||||
once_cell = "1.17"
|
||||
passfd = "0.1"
|
||||
rand = "0.8"
|
||||
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 = { git = "https://github.com/Kernel-SU/binder_rs" }
|
||||
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,6 +1,6 @@
|
||||
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
|
||||
@@ -18,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"
|
||||
|
||||
@@ -17,15 +17,20 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||
|
||||
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
|
||||
pub const PROP_CTL_RESTART: &str = "ctl.restart";
|
||||
pub const ZYGISK_LOADER: &str = "libzygisk_loader.so";
|
||||
pub const ZYGISK_MAGIC: &str = "/system/zygisk_magic";
|
||||
pub const ZYGISK_LIBRARY: &str = "libzygisk.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 PATH_MODULES_DIR: &str = "..";
|
||||
pub const PATH_MODULE_PROP: &str = "module.prop";
|
||||
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
||||
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
||||
pub const PATH_CP_BIN: &str = "bin/zygisk-cp";
|
||||
|
||||
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
|
||||
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
|
||||
@@ -39,7 +44,6 @@ pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations i
|
||||
pub enum DaemonSocketAction {
|
||||
PingHeartbeat,
|
||||
RequestLogcatFd,
|
||||
ReadNativeBridge,
|
||||
GetProcessFlags,
|
||||
ReadModules,
|
||||
RequestCompanionSocket,
|
||||
|
||||
@@ -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,
|
||||
|
||||
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,19 +0,0 @@
|
||||
use std::fs;
|
||||
use anyhow::Result;
|
||||
use crate::constants;
|
||||
use crate::utils::LateInit;
|
||||
|
||||
pub static MAGIC: LateInit<String> = LateInit::new();
|
||||
pub static PATH_TMP_DIR: LateInit<String> = LateInit::new();
|
||||
pub static PATH_TMP_PROP: LateInit<String> = LateInit::new();
|
||||
|
||||
pub fn setup() -> Result<()> {
|
||||
let name = fs::read_to_string(constants::ZYGISK_MAGIC)?;
|
||||
let path_tmp_dir = format!("/dev/{}", name);
|
||||
let path_tmp_prop = format!("{}/module.prop", path_tmp_dir);
|
||||
|
||||
MAGIC.init(name);
|
||||
PATH_TMP_DIR.init(path_tmp_dir);
|
||||
PATH_TMP_PROP.init(path_tmp_prop);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,30 +1,16 @@
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod constants;
|
||||
mod dl;
|
||||
mod magic;
|
||||
mod fuse;
|
||||
mod root_impl;
|
||||
mod utils;
|
||||
mod watchdog;
|
||||
mod zygiskd;
|
||||
|
||||
use std::future::Future;
|
||||
use anyhow::Result;
|
||||
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,
|
||||
}
|
||||
|
||||
|
||||
fn init_android_logger(tag: &str) {
|
||||
@@ -35,24 +21,29 @@ fn init_android_logger(tag: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
async fn start() -> Result<()> {
|
||||
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();
|
||||
magic::setup()?;
|
||||
let cli = Args::parse();
|
||||
match cli.command {
|
||||
Commands::Watchdog => watchdog::entry().await?,
|
||||
Commands::Daemon => zygiskd::entry()?,
|
||||
};
|
||||
match name.trim_start_matches("zygisk-") {
|
||||
"wd" => async_start(watchdog::main())?,
|
||||
"fuse" => fuse::main()?,
|
||||
"cp" => zygiskd::main()?,
|
||||
_ => println!("Available commands: wd, fuse, cp"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
fn main() {
|
||||
let process = std::env::args().next().unwrap();
|
||||
let nice_name = process.split('/').last().unwrap();
|
||||
init_android_logger(nice_name);
|
||||
|
||||
if let Err(e) = start().await {
|
||||
if let Err(e) = start(nice_name) {
|
||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use nix::libc::prctl;
|
||||
use crate::constants::{MIN_KSU_VERSION, MAX_KSU_VERSION};
|
||||
use crate::constants::{MAX_KSU_VERSION, MIN_KSU_VERSION};
|
||||
|
||||
const KERNEL_SU_OPTION: i32 = 0xdeadbeefu32 as i32;
|
||||
const KERNEL_SU_OPTION: u32 = 0xdeadbeefu32;
|
||||
|
||||
const CMD_GET_VERSION: usize = 2;
|
||||
const CMD_UID_GRANTED_ROOT: usize = 12;
|
||||
@@ -15,23 +14,55 @@ pub enum Version {
|
||||
|
||||
pub fn get_kernel_su() -> Option<Version> {
|
||||
let mut version = 0;
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32, 0, 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)
|
||||
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 { prctl(KERNEL_SU_OPTION, CMD_UID_GRANTED_ROOT, uid, &mut granted as *mut bool, 0) };
|
||||
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 { prctl(KERNEL_SU_OPTION, CMD_UID_SHOULD_UMOUNT, uid, &mut umount as *mut bool, 0) };
|
||||
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
|
||||
}
|
||||
|
||||
@@ -24,23 +24,40 @@ pub fn get_magisk() -> Option<Version> {
|
||||
}
|
||||
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
let output: Option<String> = Command::new("magisk")
|
||||
Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg("select uid from policies where policy=2")
|
||||
.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 lines = match &output {
|
||||
Some(output) => output.lines(),
|
||||
let line = match output {
|
||||
Some(line) => line,
|
||||
None => return false,
|
||||
};
|
||||
lines.into_iter().any(|line| {
|
||||
line.trim().strip_prefix("uid=").and_then(|uid| uid.parse().ok()) == Some(uid)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
// TODO: uid_should_umount
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
use anyhow::Result;
|
||||
use nix::unistd::gettid;
|
||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
|
||||
use std::ffi::c_char;
|
||||
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 once_cell::sync::OnceCell;
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
|
||||
#[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 };
|
||||
}
|
||||
use std::process::Command;
|
||||
use std::sync::OnceLock;
|
||||
use rustix::net::{AddressFamily, bind_unix, listen, socket, SocketAddrUnix, SocketType};
|
||||
use rustix::thread::gettid;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
@@ -31,12 +20,12 @@ macro_rules! debug_select {
|
||||
}
|
||||
|
||||
pub struct LateInit<T> {
|
||||
cell: OnceCell<T>,
|
||||
cell: OnceLock<T>,
|
||||
}
|
||||
|
||||
impl<T> LateInit<T> {
|
||||
pub const fn new() -> Self {
|
||||
LateInit { cell: OnceCell::new() }
|
||||
LateInit { cell: OnceLock::new() }
|
||||
}
|
||||
|
||||
pub fn init(&self, value: T) {
|
||||
@@ -51,49 +40,56 @@ impl<T> std::ops::Deref for LateInit<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random_string() -> String {
|
||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
|
||||
}
|
||||
|
||||
pub fn set_socket_create_context(context: &str) -> Result<()> {
|
||||
let path = "/proc/thread-self/attr/sockcreate";
|
||||
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 = std::ffi::CString::new(tag)?;
|
||||
let message = std::ffi::CString::new(message)?;
|
||||
let tag = CString::new(tag)?;
|
||||
let message = CString::new(message)?;
|
||||
unsafe {
|
||||
__android_log_print(level as i32, tag.as_ptr(), message.as_ptr());
|
||||
__android_log_print(level, tag.as_ptr(), message.as_ptr());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_property(name: &str) -> Result<String> {
|
||||
let name = std::ffi::CString::new(name)?;
|
||||
let name = CString::new(name)?;
|
||||
let mut buf = vec![0u8; 92];
|
||||
unsafe {
|
||||
let prop = unsafe {
|
||||
__system_property_get(name.as_ptr(), buf.as_mut_ptr() as *mut c_char);
|
||||
}
|
||||
Ok(String::from_utf8(buf)?)
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -156,17 +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,49 +1,53 @@
|
||||
use crate::{constants, magic, root_impl, utils};
|
||||
use crate::{constants, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use nix::unistd::{getgid, getuid, Pid};
|
||||
use std::{fs, io};
|
||||
use std::ffi::CString;
|
||||
use std::fs;
|
||||
use std::future::Future;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
use binder::IBinder;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use nix::errno::Errno;
|
||||
use nix::libc;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
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 LOCK: LateInit<UnixListener> = LateInit::new();
|
||||
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
||||
|
||||
pub async 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()?;
|
||||
mount_prop().await?;
|
||||
if check_and_set_hint()? == false {
|
||||
log::warn!("Requirements not met, exiting");
|
||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
||||
return Ok(());
|
||||
}
|
||||
let end = spawn_daemon().await;
|
||||
set_prop_hint(constants::STATUS_CRASHED)?;
|
||||
end
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -56,32 +60,11 @@ fn check_permission() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_single_instance() -> Result<()> {
|
||||
log::info!("Ensure single instance");
|
||||
let name = format!("zygiskwd{}", magic::MAGIC.as_str());
|
||||
match utils::abstract_namespace_socket(&name) {
|
||||
Ok(socket) => LOCK.init(socket),
|
||||
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn mount_prop() -> Result<()> {
|
||||
let module_prop = if let root_impl::RootImpl::Magisk = root_impl::get_impl() {
|
||||
let magisk_path = Command::new("magisk").arg("--path").output().await?;
|
||||
let mut magisk_path = String::from_utf8(magisk_path.stdout)?;
|
||||
magisk_path.pop(); // Removing '\n'
|
||||
let cwd = std::env::current_dir()?;
|
||||
let dir = cwd.file_name().unwrap().to_string_lossy();
|
||||
format!("{magisk_path}/.magisk/modules/{dir}/{}", constants::PATH_MODULE_PROP)
|
||||
} else {
|
||||
constants::PATH_MODULE_PROP.to_string()
|
||||
};
|
||||
log::info!("Mount {module_prop}");
|
||||
let module_prop_file = fs::File::open(&module_prop)?;
|
||||
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 = io::BufReader::new(module_prop_file).lines();
|
||||
let lines = BufReader::new(module_prop_file).lines();
|
||||
for line in lines {
|
||||
let line = line?;
|
||||
if line.starts_with("description=") {
|
||||
@@ -96,26 +79,14 @@ async fn mount_prop() -> Result<()> {
|
||||
}
|
||||
PROP_SECTIONS.init(sections);
|
||||
|
||||
fs::create_dir(magic::PATH_TMP_DIR.as_str())?;
|
||||
fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
||||
|
||||
// FIXME: sys_mount cannot be compiled on 32 bit
|
||||
unsafe {
|
||||
let r = libc::mount(
|
||||
CString::new(magic::PATH_TMP_PROP.as_str())?.as_ptr(),
|
||||
CString::new(module_prop)?.as_ptr(),
|
||||
std::ptr::null(),
|
||||
libc::MS_BIND,
|
||||
std::ptr::null(),
|
||||
);
|
||||
Errno::result(r)?;
|
||||
}
|
||||
|
||||
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 set_prop_hint(hint: &str) -> Result<()> {
|
||||
let mut file = fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
||||
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())?;
|
||||
@@ -143,23 +114,14 @@ async fn spawn_daemon() -> Result<()> {
|
||||
let mut lives = 5;
|
||||
loop {
|
||||
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
|
||||
let mut child_ids = vec![];
|
||||
let daemon = Command::new(constants::PATH_CP_BIN).spawn()?;
|
||||
let daemon_pid = daemon.id().unwrap();
|
||||
|
||||
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
||||
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
||||
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
|
||||
async fn daemon_holder(mut daemon: Child) -> Result<()> {
|
||||
let result = daemon.wait().await?;
|
||||
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
||||
Ok(())
|
||||
}
|
||||
if let Ok(it) = daemon32 {
|
||||
child_ids.push(it.id().unwrap());
|
||||
futures.push(Box::pin(spawn_daemon(it)));
|
||||
}
|
||||
if let Ok(it) = daemon64 {
|
||||
child_ids.push(it.id().unwrap());
|
||||
futures.push(Box::pin(spawn_daemon(it)));
|
||||
bail!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
||||
}
|
||||
futures.push(Box::pin(daemon_holder(daemon)));
|
||||
|
||||
async fn binder_listener() -> Result<()> {
|
||||
let mut binder = loop {
|
||||
@@ -172,34 +134,29 @@ async fn spawn_daemon() -> Result<()> {
|
||||
};
|
||||
};
|
||||
|
||||
log::info!("System server ready, restore native bridge");
|
||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
||||
info!("System server ready");
|
||||
|
||||
loop {
|
||||
if binder.ping_binder().is_err() { break; }
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
log::error!("System server died");
|
||||
Ok(())
|
||||
bail!("System server died");
|
||||
}
|
||||
futures.push(Box::pin(binder_listener()));
|
||||
|
||||
if let Err(e) = futures.next().await.unwrap() {
|
||||
log::error!("{}", e);
|
||||
error!("{}", e);
|
||||
}
|
||||
|
||||
for child in child_ids {
|
||||
log::debug!("Killing child process {}", child);
|
||||
let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL);
|
||||
}
|
||||
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)?;
|
||||
error!("Restarting zygote...");
|
||||
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
use std::ffi::c_void;
|
||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::{constants, dl, lp_select, magic, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use crate::{constants, dl, root_impl, utils};
|
||||
use anyhow::Result;
|
||||
use passfd::FdPassingExt;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::fs;
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use std::os::fd::OwnedFd;
|
||||
use std::os::unix::{
|
||||
net::{UnixListener, UnixStream},
|
||||
prelude::AsRawFd,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use nix::libc;
|
||||
use nix::sys::stat::fstat;
|
||||
use nix::unistd::close;
|
||||
use rustix::fs::fstat;
|
||||
use rustix::process::{set_parent_process_death_signal, Signal};
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
@@ -26,21 +25,20 @@ struct Module {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -62,17 +60,6 @@ pub fn entry() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_arch() -> Result<&'static str> {
|
||||
let system_arch = utils::get_property("ro.product.cpu.abi")?;
|
||||
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_MODULES_DIR) {
|
||||
@@ -132,10 +119,8 @@ fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
|
||||
fn create_daemon_socket() -> Result<UnixListener> {
|
||||
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||
let prefix = lp_select!("zygiskd32", "zygiskd64");
|
||||
let name = format!("{}{}", prefix, magic::MAGIC.as_str());
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -171,9 +156,6 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
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();
|
||||
@@ -209,16 +191,15 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
}
|
||||
Some(companion) => {
|
||||
stream.write_u8(1)?;
|
||||
let fd = stream.into_raw_fd();
|
||||
let st0 = fstat(fd)?;
|
||||
unsafe { companion(fd); }
|
||||
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(fd) {
|
||||
if st0.st_dev == st1.st_dev && st0.st_ino == st1.st_ino {
|
||||
close(fd)?;
|
||||
if let Ok(st1) = fstat(&stream) {
|
||||
if st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino {
|
||||
std::mem::forget(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user