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
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8ad933388 | ||
|
|
baf444228d | ||
|
|
5c00071fed | ||
|
|
fc9bc3b28f | ||
|
|
8cac525aa9 | ||
|
|
2bcb36ab4a | ||
|
|
150be54ff0 | ||
|
|
ab9ce993eb |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[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
|
||||
|
||||
14
README.md
14
README.md
@@ -2,12 +2,12 @@
|
||||
|
||||
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
|
||||
|
||||
Warning: The current version of Zygisksu is UNSTABLE. You may suffer boot loop or even data loss so use with caution.
|
||||
Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD. (Coming soon)
|
||||
|
||||
## Requirements
|
||||
|
||||
+ Minimal KernelSU version: 10575
|
||||
+ Minimal ksud version: 10200
|
||||
+ Minimal ksud version: 10616
|
||||
+ Full SELinux patch support (If non-gki kernel)
|
||||
|
||||
## Compatibility
|
||||
@@ -22,11 +22,5 @@ Should work with everything except those rely on Magisk internal behaviors.
|
||||
- [x] [Daemon] Linker namespace
|
||||
- [x] [Daemon] Separate zygiskd process
|
||||
- [x] [Daemon] Handle 64 bit only devices
|
||||
- [ ] [Daemon] Handle zygote death
|
||||
|
||||
## Running on Magisk
|
||||
|
||||
It is possible to run Zygisksu on Magisk with a few steps:
|
||||
|
||||
1. `mkdir /data/adb/ksu`
|
||||
2. `ln -s /data/adb/modules /data/adb/ksu/`
|
||||
- [x] [Daemon] Handle zygote death
|
||||
- [ ] [ Misc ] Support Magisk out of box
|
||||
|
||||
@@ -31,14 +31,14 @@ 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.2.0")
|
||||
val verName by extra("v4-0.4.0")
|
||||
val verCode by extra(gitCommitCount)
|
||||
|
||||
val androidMinSdkVersion by extra(29)
|
||||
val androidTargetSdkVersion by extra(33)
|
||||
val androidCompileSdkVersion by extra(33)
|
||||
val androidBuildToolsVersion by extra("33.0.1")
|
||||
val androidCompileNdkVersion by extra("25.1.8937393")
|
||||
val androidBuildToolsVersion by extra("33.0.2")
|
||||
val androidCompileNdkVersion by extra("25.2.9519653")
|
||||
val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
|
||||
val androidTargetCompatibility by extra(JavaVersion.VERSION_11)
|
||||
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
2
loader/src/external/liblsplt
vendored
2
loader/src/external/liblsplt
vendored
Submodule loader/src/external/liblsplt updated: 204a163688...b254b5b9a5
@@ -18,7 +18,9 @@ static void zygisk_cleanup_wait() {
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void *handle) {
|
||||
#ifdef NDEBUG
|
||||
logging::setfd(zygiskd::RequestLogcatFd());
|
||||
#endif
|
||||
self_handle = handle;
|
||||
|
||||
LOGD("Load injector successfully");
|
||||
|
||||
@@ -112,8 +112,8 @@ hash_map<xstring, tree_map<xstring, tree_map<xstring, void *>>> *jni_method_map;
|
||||
|
||||
// Current context
|
||||
HookContext *g_ctx;
|
||||
const JNINativeInterface *old_functions;
|
||||
JNINativeInterface *new_functions;
|
||||
const JNINativeInterface *old_functions = nullptr;
|
||||
JNINativeInterface *new_functions = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -143,15 +143,8 @@ if (methods[i].name == #method##sv) {
|
||||
|
||||
namespace {
|
||||
|
||||
jclass gClassRef;
|
||||
jmethodID class_getName;
|
||||
string get_class_name(JNIEnv *env, jclass clazz) {
|
||||
if (!gClassRef) {
|
||||
jclass cls = env->FindClass("java/lang/Class");
|
||||
gClassRef = (jclass) env->NewGlobalRef(cls);
|
||||
env->DeleteLocalRef(cls);
|
||||
class_getName = env->GetMethodID(gClassRef, "getName", "()Ljava/lang/String;");
|
||||
}
|
||||
static auto class_getName = env->GetMethodID(env->FindClass("java/lang/Class"), "getName", "()Ljava/lang/String;");
|
||||
auto nameRef = (jstring) env->CallObjectMethod(clazz, class_getName);
|
||||
const char *name = env->GetStringUTFChars(nameRef, nullptr);
|
||||
string className(name);
|
||||
@@ -172,11 +165,44 @@ jint env_RegisterNatives(
|
||||
return old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
|
||||
}
|
||||
|
||||
DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
|
||||
JNIEnv *env, const char *className, const JNINativeMethod *methods, int numMethods) {
|
||||
LOGV("jniRegisterNativeMethods [%s]\n", className);
|
||||
auto newMethods = hookAndSaveJNIMethods(className, methods, numMethods);
|
||||
return old_jniRegisterNativeMethods(env, className, 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");
|
||||
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;
|
||||
|
||||
// Replace the function table in JNIEnv to hook RegisterNatives
|
||||
old_functions = env->functions;
|
||||
env->functions = new_functions;
|
||||
} while (false);
|
||||
old_androidSetCreateThreadFunc(func);
|
||||
}
|
||||
|
||||
// Skip actual fork and return cached result if applicable
|
||||
@@ -225,58 +251,6 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext,
|
||||
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// The original android::AppRuntime virtual table
|
||||
void **gAppRuntimeVTable;
|
||||
|
||||
// This method is a trampoline for hooking JNIEnv->RegisterNatives
|
||||
void onVmCreated(void *self, JNIEnv* env) {
|
||||
LOGD("AppRuntime::onVmCreated\n");
|
||||
|
||||
// Restore virtual table
|
||||
auto new_table = *reinterpret_cast<void***>(self);
|
||||
*reinterpret_cast<void***>(self) = gAppRuntimeVTable;
|
||||
delete[] new_table;
|
||||
|
||||
new_functions = new JNINativeInterface();
|
||||
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;
|
||||
}
|
||||
|
||||
template<int N>
|
||||
void vtable_entry(void *self, JNIEnv* env) {
|
||||
// The first invocation will be onVmCreated. It will also restore the vtable.
|
||||
onVmCreated(self, env);
|
||||
// Call original function
|
||||
reinterpret_cast<decltype(&onVmCreated)>(gAppRuntimeVTable[N])(self, env);
|
||||
}
|
||||
|
||||
/* Zygisksu changed: AndroidRuntime setArgv0 before native bridge loaded */
|
||||
void hookVirtualTable(void *self) {
|
||||
LOGD("hook AndroidRuntime virtual table\n");
|
||||
|
||||
// We don't know which entry is onVmCreated, so overwrite every one
|
||||
// We also don't know the size of the vtable, but 8 is more than enough
|
||||
auto new_table = new void*[8];
|
||||
new_table[0] = reinterpret_cast<void*>(&vtable_entry<0>);
|
||||
new_table[1] = reinterpret_cast<void*>(&vtable_entry<1>);
|
||||
new_table[2] = reinterpret_cast<void*>(&vtable_entry<2>);
|
||||
new_table[3] = reinterpret_cast<void*>(&vtable_entry<3>);
|
||||
new_table[4] = reinterpret_cast<void*>(&vtable_entry<4>);
|
||||
new_table[5] = reinterpret_cast<void*>(&vtable_entry<5>);
|
||||
new_table[6] = reinterpret_cast<void*>(&vtable_entry<6>);
|
||||
new_table[7] = reinterpret_cast<void*>(&vtable_entry<7>);
|
||||
|
||||
// Swizzle C++ vtable to hook virtual function
|
||||
gAppRuntimeVTable = *reinterpret_cast<void***>(self);
|
||||
*reinterpret_cast<void***>(self) = new_table;
|
||||
}
|
||||
|
||||
#undef DCL_HOOK_FUNC
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
@@ -312,7 +286,7 @@ void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods
|
||||
if (hooks.empty())
|
||||
return;
|
||||
|
||||
old_jniRegisterNativeMethods(env, clz, hooks.data(), hooks.size());
|
||||
old_functions->RegisterNatives(env, env->FindClass(clz), hooks.data(), static_cast<int>(hooks.size()));
|
||||
}
|
||||
|
||||
ZygiskModule::ZygiskModule(int id, void *handle, void *entry)
|
||||
@@ -501,11 +475,11 @@ void HookContext::sanitize_fds() {
|
||||
if (exempted_fds.empty())
|
||||
return nullptr;
|
||||
|
||||
jintArray array = env->NewIntArray(off + exempted_fds.size());
|
||||
jintArray array = env->NewIntArray(static_cast<int>(off + exempted_fds.size()));
|
||||
if (array == nullptr)
|
||||
return nullptr;
|
||||
|
||||
env->SetIntArrayRegion(array, off, exempted_fds.size(), exempted_fds.data());
|
||||
env->SetIntArrayRegion(array, off, static_cast<int>(exempted_fds.size()), exempted_fds.data());
|
||||
for (int fd : exempted_fds) {
|
||||
if (fd >= 0 && fd < MAX_FD_SIZE) {
|
||||
allowed_fds[fd] = true;
|
||||
@@ -739,35 +713,16 @@ 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, jniRegisterNativeMethods);
|
||||
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();
|
||||
|
||||
// Remove unhooked methods
|
||||
plt_hook_list->erase(
|
||||
std::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
|
||||
[](auto &t) { return *std::get<3>(t) == nullptr;}),
|
||||
[](auto &t) { return *std::get<3>(t) == nullptr;}),
|
||||
plt_hook_list->end());
|
||||
|
||||
/* Zygisksu changed: AndroidRuntime setArgv0 before native bridge loaded */
|
||||
if (old_jniRegisterNativeMethods == nullptr) {
|
||||
do {
|
||||
LOGD("jniRegisterNativeMethods not hooked, using fallback\n");
|
||||
constexpr char sig[] = "_ZN7android14AndroidRuntime10getRuntimeEv";
|
||||
auto *GetRuntime = (void*(*)()) dlsym(RTLD_DEFAULT, sig);
|
||||
if (GetRuntime == nullptr) {
|
||||
LOGE("GetRuntime is nullptr");
|
||||
break;
|
||||
}
|
||||
hookVirtualTable(GetRuntime());
|
||||
} while (false);
|
||||
|
||||
// We still need old_jniRegisterNativeMethods as other code uses it
|
||||
// android::AndroidRuntime::registerNativeMethods(_JNIEnv*, const char*, const JNINativeMethod*, int)
|
||||
constexpr char sig[] = "_ZN7android14AndroidRuntime21registerNativeMethodsEP7_JNIEnvPKcPK15JNINativeMethodi";
|
||||
*(void **) &old_jniRegisterNativeMethods = dlsym(RTLD_DEFAULT, sig);
|
||||
}
|
||||
}
|
||||
|
||||
static bool unhook_functions() {
|
||||
@@ -776,17 +731,14 @@ static bool unhook_functions() {
|
||||
// Restore JNIEnv
|
||||
if (g_ctx->env->functions == new_functions) {
|
||||
g_ctx->env->functions = old_functions;
|
||||
if (gClassRef) {
|
||||
g_ctx->env->DeleteGlobalRef(gClassRef);
|
||||
gClassRef = nullptr;
|
||||
class_getName = nullptr;
|
||||
}
|
||||
delete new_functions;
|
||||
}
|
||||
|
||||
// Unhook JNI methods
|
||||
for (const auto &[clz, methods] : *jni_hook_list) {
|
||||
if (!methods.empty() && old_jniRegisterNativeMethods(
|
||||
g_ctx->env, clz.data(), methods.data(), methods.size()) != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ void Constructor() {
|
||||
std::string native_bridge;
|
||||
do {
|
||||
if (!zygiskd::PingHeartbeat()) break;
|
||||
|
||||
#ifdef NDEBUG
|
||||
logging::setfd(zygiskd::RequestLogcatFd());
|
||||
|
||||
#endif
|
||||
LOGI("Read native bridge");
|
||||
native_bridge = zygiskd::ReadNativeBridge();
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# shellcheck disable=SC2034
|
||||
SKIPUNZIP=1
|
||||
|
||||
DEBUG=@DEBUG@
|
||||
|
||||
if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then
|
||||
ui_print "- Installing from KernelSU app"
|
||||
else
|
||||
@@ -14,8 +16,8 @@ VERSION=$(grep_prop version "${TMPDIR}/module.prop")
|
||||
ui_print "- Installing Zygisksu $VERSION"
|
||||
|
||||
# check KernelSU
|
||||
ui_print "- KernelSU version: $KSU_VER ($KSU_VER_CODE)"
|
||||
if [ "$KSU_VER_CODE" -lt 10200 ]; then
|
||||
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"
|
||||
@@ -25,7 +27,7 @@ fi
|
||||
# check android
|
||||
if [ "$API" -lt 29 ]; then
|
||||
ui_print "! Unsupported sdk: $API"
|
||||
abort "! Minimal supported sdk is 29 (Android 10.0)"
|
||||
abort "! Minimal supported sdk is 29 (Android 10)"
|
||||
else
|
||||
ui_print "- Device sdk: $API"
|
||||
fi
|
||||
@@ -51,7 +53,7 @@ extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
|
||||
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
||||
|
||||
ui_print "- Checking SELinux patches"
|
||||
if ! /data/adb/ksud sepolicy check "$TMPDIR/sepolicy.rule"; then
|
||||
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"
|
||||
@@ -63,7 +65,6 @@ extract "$ZIPFILE" 'daemon.sh' "$MODPATH"
|
||||
extract "$ZIPFILE" 'module.prop' "$MODPATH"
|
||||
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
|
||||
extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH"
|
||||
extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
||||
|
||||
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
|
||||
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true
|
||||
@@ -111,17 +112,19 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
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"
|
||||
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
|
||||
fi
|
||||
|
||||
ui_print "- Setting permissions"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
DEBUG=@DEBUG@
|
||||
MODDIR=${0%/*}
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||
@@ -9,4 +8,4 @@ export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||
|
||||
log -p i -t "zygisksu" "Start watchdog"
|
||||
resetprop ro.dalvik.vm.native.bridge libzygiskloader.so
|
||||
exec "$MODDIR/bin/zygiskwd" "watchdog" >/dev/null 2>&1
|
||||
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
MODDIR=${0%/*}
|
||||
|
||||
cd $MODDIR
|
||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||
unshare -m sh -c "$MODDIR/daemon.sh $@&"
|
||||
|
||||
unshare -m sh -c "./daemon.sh $@&"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
#!/system/bin/sh
|
||||
@@ -7,8 +7,8 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("com.android.library") version "7.4.0"
|
||||
id("com.android.application") version "7.4.0"
|
||||
id("com.android.library") version "7.4.1"
|
||||
id("com.android.application") version "7.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ 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"
|
||||
@@ -17,6 +18,8 @@ num_enum = "0.5.9"
|
||||
passfd = "0.1.5"
|
||||
rand = "0.8.5"
|
||||
|
||||
binder = { path = "src/external/binder_rs/binder" }
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
|
||||
@@ -20,6 +20,7 @@ cargo {
|
||||
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
|
||||
profile = if (isDebug) "debug" else "release"
|
||||
exec = { spec, _ ->
|
||||
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
|
||||
spec.environment("VERSION_CODE", verCode)
|
||||
spec.environment("VERSION_NAME", verName)
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ macro_rules! lp_select {
|
||||
}
|
||||
|
||||
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 SOCKET_PLACEHOLDER: &str = "socket_placeholder";
|
||||
|
||||
pub const PATH_KSU_MODULE_DIR: &str = "/data/adb/ksu/modules";
|
||||
pub const PATH_ZYGISKSU_DIR: &str = concatcp!(PATH_KSU_MODULE_DIR, "/zygisksu");
|
||||
pub const PATH_ZYGISKD32: &str = concatcp!(PATH_ZYGISKSU_DIR, "/bin/zygiskd32");
|
||||
pub const PATH_ZYGISKD64: &str = concatcp!(PATH_ZYGISKSU_DIR, "/bin/zygiskd64");
|
||||
pub const PATH_DAEMON_LOCK: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd.lock");
|
||||
pub const PATH_MODULE_DIR: &str = "..";
|
||||
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
||||
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
|
||||
1
zygiskd/src/external/binder_rs
vendored
Submodule
1
zygiskd/src/external/binder_rs
vendored
Submodule
Submodule zygiskd/src/external/binder_rs added at 6d958bb94a
@@ -43,7 +43,7 @@ fn main() {
|
||||
let cli = Args::parse();
|
||||
let result = match cli.command {
|
||||
Commands::Watchdog => watchdog::entry(),
|
||||
Commands::Daemon => zygiskd::entry(lp_select!(false, true)),
|
||||
Commands::Daemon => zygiskd::entry(),
|
||||
Commands::Companion { fd } => companion::entry(fd),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::constants;
|
||||
use anyhow::Result;
|
||||
use nix::unistd::gettid;
|
||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
|
||||
@@ -27,10 +26,10 @@ pub fn get_native_bridge() -> String {
|
||||
std::env::var("NATIVE_BRIDGE").unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn restore_native_bridge() -> Result<()> {
|
||||
pub fn set_property(name: &str, value: &str) -> Result<()> {
|
||||
Command::new("resetprop")
|
||||
.arg(constants::PROP_NATIVE_BRIDGE)
|
||||
.arg(get_native_bridge())
|
||||
.arg(name)
|
||||
.arg(value)
|
||||
.spawn()?.wait()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::constants;
|
||||
use crate::{constants, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use nix::fcntl::{flock, FlockArg};
|
||||
use nix::unistd::{getgid, getuid};
|
||||
use std::os::unix::prelude::AsRawFd;
|
||||
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::time::Duration;
|
||||
use binder::IBinder;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
|
||||
static mut LOCK_FILE: Option<fs::File> = None;
|
||||
static mut LOCK: Option<UnixListener> = None;
|
||||
|
||||
pub fn entry() -> Result<()> {
|
||||
log::info!("Start zygisksu watchdog");
|
||||
@@ -30,7 +32,6 @@ fn check_permission() -> Result<()> {
|
||||
|
||||
let context = fs::read_to_string("/proc/self/attr/current")?;
|
||||
let context = context.trim_end_matches('\0');
|
||||
//TODO: remove magisk context after debug finished
|
||||
if context != "u:r:su:s0" && context != "u:r:magisk:s0" {
|
||||
bail!("SELinux context incorrect: {context}");
|
||||
}
|
||||
@@ -40,38 +41,65 @@ fn check_permission() -> Result<()> {
|
||||
|
||||
fn ensure_single_instance() -> Result<()> {
|
||||
log::info!("Ensure single instance");
|
||||
let metadata = fs::metadata(constants::PATH_ZYGISKSU_DIR);
|
||||
if metadata.is_err() || !metadata.unwrap().is_dir() {
|
||||
bail!("Zygisksu is not installed");
|
||||
}
|
||||
unsafe {
|
||||
match fs::File::create(constants::PATH_DAEMON_LOCK) {
|
||||
Ok(file) => LOCK_FILE = Some(file),
|
||||
Err(e) => bail!("Failed to open lock file: {e}"),
|
||||
};
|
||||
let fd = LOCK_FILE.as_ref().unwrap().as_raw_fd();
|
||||
if let Err(e) = flock(fd, FlockArg::LockExclusiveNonblock) {
|
||||
bail!("Failed to acquire lock: {e}. Maybe another instance is running?");
|
||||
}
|
||||
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?")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_daemon() -> Result<()> {
|
||||
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
||||
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let spawn = |mut daemon: Child| {
|
||||
let sender = sender.clone();
|
||||
thread::spawn(move || {
|
||||
let result = daemon.wait().unwrap();
|
||||
log::error!("Daemon process {} died: {}", daemon.id(), result);
|
||||
drop(daemon);
|
||||
sender.send(()).unwrap();
|
||||
});
|
||||
};
|
||||
if let Ok(it) = daemon32 { spawn(it) }
|
||||
if let Ok(it) = daemon64 { spawn(it) }
|
||||
let _ = receiver.recv();
|
||||
bail!("Daemon process died");
|
||||
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 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));
|
||||
}
|
||||
};
|
||||
};
|
||||
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))
|
||||
}
|
||||
for child in child_ids {
|
||||
let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::constants::DaemonSocketAction;
|
||||
use crate::utils::{restore_native_bridge, UnixStreamExt};
|
||||
use crate::{constants, utils};
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::{constants, lp_select, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use memfd::Memfd;
|
||||
use nix::{
|
||||
@@ -31,10 +31,10 @@ struct Context {
|
||||
modules: Vec<Module>,
|
||||
}
|
||||
|
||||
pub fn entry(is64: bool) -> Result<()> {
|
||||
pub fn entry() -> Result<()> {
|
||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
||||
|
||||
let arch = get_arch(is64)?;
|
||||
let arch = get_arch()?;
|
||||
log::debug!("Daemon architecture: {arch}");
|
||||
|
||||
log::info!("Load modules");
|
||||
@@ -47,7 +47,7 @@ pub fn entry(is64: bool) -> Result<()> {
|
||||
let context = Arc::new(context);
|
||||
|
||||
log::info!("Create socket");
|
||||
let listener = create_daemon_socket(is64)?;
|
||||
let listener = create_daemon_socket()?;
|
||||
|
||||
log::info!("Handle zygote connections");
|
||||
for stream in listener.incoming() {
|
||||
@@ -63,23 +63,21 @@ pub fn entry(is64: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_arch(is64: bool) -> Result<&'static str> {
|
||||
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)?;
|
||||
let is_arm = system_arch.contains("arm");
|
||||
let is_x86 = system_arch.contains("x86");
|
||||
match (is_arm, is_x86, is64) {
|
||||
(true, _, false) => Ok("armeabi-v7a"),
|
||||
(true, _, true) => Ok("arm64-v8a"),
|
||||
(_, true, false) => Ok("x86"),
|
||||
(_, true, true) => Ok("x86_64"),
|
||||
_ => bail!("Unsupported system architecture: {}", system_arch),
|
||||
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_KSU_MODULE_DIR) {
|
||||
let dir = match fs::read_dir(constants::PATH_MODULE_DIR) {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => {
|
||||
log::warn!("Failed reading modules directory: {}", e);
|
||||
@@ -137,10 +135,10 @@ fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
|
||||
Ok(memfd)
|
||||
}
|
||||
|
||||
fn create_daemon_socket(is64: bool) -> Result<UnixListener> {
|
||||
fn create_daemon_socket() -> Result<UnixListener> {
|
||||
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||
let suffix = if is64 { "zygiskd64" } else { "zygiskd32" };
|
||||
let name = String::from(suffix) + constants::SOCKET_PLACEHOLDER;
|
||||
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}");
|
||||
Ok(listener)
|
||||
@@ -175,7 +173,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
log::trace!("New daemon action {:?}", action);
|
||||
match action {
|
||||
DaemonSocketAction::PingHeartbeat => {
|
||||
restore_native_bridge()?;
|
||||
// Do nothing
|
||||
}
|
||||
DaemonSocketAction::RequestLogcatFd => {
|
||||
loop {
|
||||
@@ -223,7 +221,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
DaemonSocketAction::GetModuleDir => {
|
||||
let index = stream.read_usize()?;
|
||||
let module = &context.modules[index];
|
||||
let dir = format!("{}/{}", constants::PATH_KSU_MODULE_DIR, module.name);
|
||||
let dir = format!("{}/{}", constants::PATH_MODULE_DIR, module.name);
|
||||
let dir = fs::File::open(dir)?;
|
||||
stream.send_fd(dir.as_raw_fd())?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user