You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d9cc560cc | ||
|
|
f395cfb490 | ||
|
|
03575edd96 | ||
|
|
915749e59b | ||
|
|
d08b415577 | ||
|
|
f27aed5068 | ||
|
|
5365ab1f12 | ||
|
|
b99d042002 | ||
|
|
57d3d8a0ba | ||
|
|
e69aa5c527 | ||
|
|
9b5eb1bac7 | ||
|
|
c8ad933388 | ||
|
|
baf444228d | ||
|
|
5c00071fed | ||
|
|
fc9bc3b28f | ||
|
|
8cac525aa9 | ||
|
|
2bcb36ab4a | ||
|
|
150be54ff0 | ||
|
|
ab9ce993eb |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
|||||||
[submodule "loader/src/external/parallel-hashmap"]
|
[submodule "loader/src/external/parallel-hashmap"]
|
||||||
path = loader/src/external/parallel-hashmap
|
path = loader/src/external/parallel-hashmap
|
||||||
url = https://github.com/greg7mdp/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
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -2,31 +2,15 @@
|
|||||||
|
|
||||||
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
|
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.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
+ Minimal KernelSU version: 10575
|
+ Minimal KernelSU version: 10654
|
||||||
+ Minimal ksud version: 10200
|
+ Minimal ksud version: 10647
|
||||||
+ Full SELinux patch support (If non-gki kernel)
|
+ Full SELinux patch support (If non-gki kernel)
|
||||||
|
+ No multiple root implementation installed
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
Should work with everything except those rely on Magisk internal behaviors.
|
Should work with everything except those rely on Magisk internal behaviors.
|
||||||
|
|
||||||
## Development road map
|
|
||||||
|
|
||||||
- [x] [Inject] Basic Zygisk loader
|
|
||||||
- [x] [Inject] Stabilize injector
|
|
||||||
- [x] [Inject] Unload
|
|
||||||
- [x] [Daemon] Linker namespace
|
|
||||||
- [x] [Daemon] Separate zygiskd process
|
|
||||||
- [x] [Daemon] Handle 64 bit only devices
|
|
||||||
- [ ] [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/`
|
|
||||||
|
|||||||
@@ -31,14 +31,18 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
|
|||||||
|
|
||||||
val moduleId by extra("zygisksu")
|
val moduleId by extra("zygisksu")
|
||||||
val moduleName by extra("Zygisk on KernelSU")
|
val moduleName by extra("Zygisk on KernelSU")
|
||||||
val verName by extra("v4-0.2.0")
|
val verName by extra("v4-0.5.1")
|
||||||
val verCode by extra(gitCommitCount)
|
val verCode by extra(gitCommitCount)
|
||||||
|
val minKsuVersion by extra(10654)
|
||||||
|
val minKsudVersion by extra(10647)
|
||||||
|
val maxKsuVersion by extra(20000)
|
||||||
|
val minMagiskVersion by extra(25000)
|
||||||
|
|
||||||
val androidMinSdkVersion by extra(29)
|
val androidMinSdkVersion by extra(29)
|
||||||
val androidTargetSdkVersion by extra(33)
|
val androidTargetSdkVersion by extra(33)
|
||||||
val androidCompileSdkVersion by extra(33)
|
val androidCompileSdkVersion by extra(33)
|
||||||
val androidBuildToolsVersion by extra("33.0.1")
|
val androidBuildToolsVersion by extra("33.0.2")
|
||||||
val androidCompileNdkVersion by extra("25.1.8937393")
|
val androidCompileNdkVersion by extra("25.2.9519653")
|
||||||
val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
|
val androidSourceCompatibility by extra(JavaVersion.VERSION_11)
|
||||||
val androidTargetCompatibility 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
|
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
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -58,6 +58,17 @@ namespace zygiskd {
|
|||||||
return socket_utils::read_string(fd);
|
return socket_utils::read_string(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t GetProcessFlags(uid_t uid) {
|
||||||
|
UniqueFd fd = Connect(1);
|
||||||
|
if (fd == -1) {
|
||||||
|
PLOGE("GetProcessFlags");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
|
||||||
|
socket_utils::write_u32(fd, uid);
|
||||||
|
return socket_utils::read_u32(fd);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Module> ReadModules() {
|
std::vector<Module> ReadModules() {
|
||||||
std::vector<Module> modules;
|
std::vector<Module> modules;
|
||||||
UniqueFd fd = Connect(1);
|
UniqueFd fd = Connect(1);
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ namespace socket_utils {
|
|||||||
return read_exact_or<uint8_t>(fd, 0);
|
return read_exact_or<uint8_t>(fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t read_u32(int fd) {
|
||||||
|
return read_exact_or<uint32_t>(fd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
size_t read_usize(int fd) {
|
size_t read_usize(int fd) {
|
||||||
return read_exact_or<size_t>(fd, 0);
|
return read_exact_or<size_t>(fd, 0);
|
||||||
}
|
}
|
||||||
@@ -110,6 +114,10 @@ namespace socket_utils {
|
|||||||
return write_exact<uint8_t>(fd, val);
|
return write_exact<uint8_t>(fd, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool write_u32(int fd, uint32_t val) {
|
||||||
|
return write_exact<uint32_t>(fd, val);
|
||||||
|
}
|
||||||
|
|
||||||
bool write_string(int fd, std::string_view str) {
|
bool write_string(int fd, std::string_view str) {
|
||||||
return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size());
|
return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size());
|
||||||
}
|
}
|
||||||
|
|||||||
2
loader/src/external/liblsplt
vendored
2
loader/src/external/liblsplt
vendored
Submodule loader/src/external/liblsplt updated: 204a163688...b254b5b9a5
@@ -54,6 +54,7 @@ namespace zygiskd {
|
|||||||
PingHeartBeat,
|
PingHeartBeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
ReadNativeBridge,
|
ReadNativeBridge,
|
||||||
|
GetProcessFlags,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
GetModuleDir,
|
GetModuleDir,
|
||||||
@@ -67,6 +68,8 @@ namespace zygiskd {
|
|||||||
|
|
||||||
std::vector<Module> ReadModules();
|
std::vector<Module> ReadModules();
|
||||||
|
|
||||||
|
uint32_t GetProcessFlags(uid_t uid);
|
||||||
|
|
||||||
int ConnectCompanion(size_t index);
|
int ConnectCompanion(size_t index);
|
||||||
|
|
||||||
int GetModuleDir(size_t index);
|
int GetModuleDir(size_t index);
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ namespace socket_utils {
|
|||||||
|
|
||||||
uint8_t read_u8(int fd);
|
uint8_t read_u8(int fd);
|
||||||
|
|
||||||
|
uint32_t read_u32(int fd);
|
||||||
|
|
||||||
size_t read_usize(int fd);
|
size_t read_usize(int fd);
|
||||||
|
|
||||||
std::string read_string(int fd);
|
std::string read_string(int fd);
|
||||||
|
|
||||||
bool write_u8(int fd, uint8_t val);
|
bool write_u8(int fd, uint8_t val);
|
||||||
|
|
||||||
|
bool write_u32(int fd, uint32_t val);
|
||||||
|
|
||||||
int recv_fd(int fd);
|
int recv_fd(int fd);
|
||||||
|
|
||||||
bool write_usize(int fd, size_t val);
|
bool write_usize(int fd, size_t val);
|
||||||
|
|||||||
@@ -18,16 +18,11 @@ static void zygisk_cleanup_wait() {
|
|||||||
|
|
||||||
extern "C" [[gnu::visibility("default")]]
|
extern "C" [[gnu::visibility("default")]]
|
||||||
void entry(void *handle) {
|
void entry(void *handle) {
|
||||||
|
#ifdef NDEBUG
|
||||||
logging::setfd(zygiskd::RequestLogcatFd());
|
logging::setfd(zygiskd::RequestLogcatFd());
|
||||||
|
#endif
|
||||||
self_handle = handle;
|
self_handle = handle;
|
||||||
|
|
||||||
LOGD("Load injector successfully");
|
LOGD("Load injector successfully");
|
||||||
hook_functions();
|
hook_functions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following code runs in zygote/app process
|
|
||||||
|
|
||||||
static inline bool should_load_modules(uint32_t flags) {
|
|
||||||
return (flags & UNMOUNT_MASK) != UNMOUNT_MASK &&
|
|
||||||
(flags & PROCESS_IS_MAGISK_APP) != PROCESS_IS_MAGISK_APP;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#include <sys/mount.h>
|
|
||||||
|
|
||||||
#include "logging.h"
|
|
||||||
#include "misc.hpp"
|
|
||||||
#include "zygisk.hpp"
|
|
||||||
|
|
||||||
using namespace std::string_view_literals;
|
|
||||||
|
|
||||||
static void lazy_unmount(const char* mountpoint) {
|
|
||||||
if (umount2(mountpoint, MNT_DETACH) != -1)
|
|
||||||
LOGD("Unmounted (%s)", mountpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
void revert_unmount() {
|
|
||||||
parse_mnt("/proc/self/mounts", [](mntent* mentry) {
|
|
||||||
if (mentry->mnt_fsname == "/data/adb/ksu/modules"sv ||
|
|
||||||
std::string_view(mentry->mnt_opts).find("/data/adb/ksu/modules") != std::string_view::npos) {
|
|
||||||
lazy_unmount(mentry->mnt_fsname);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -112,8 +112,8 @@ hash_map<xstring, tree_map<xstring, tree_map<xstring, void *>>> *jni_method_map;
|
|||||||
|
|
||||||
// Current context
|
// Current context
|
||||||
HookContext *g_ctx;
|
HookContext *g_ctx;
|
||||||
const JNINativeInterface *old_functions;
|
const JNINativeInterface *old_functions = nullptr;
|
||||||
JNINativeInterface *new_functions;
|
JNINativeInterface *new_functions = nullptr;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -143,15 +143,8 @@ if (methods[i].name == #method##sv) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
jclass gClassRef;
|
|
||||||
jmethodID class_getName;
|
|
||||||
string get_class_name(JNIEnv *env, jclass clazz) {
|
string get_class_name(JNIEnv *env, jclass clazz) {
|
||||||
if (!gClassRef) {
|
static auto class_getName = env->GetMethodID(env->FindClass("java/lang/Class"), "getName", "()Ljava/lang/String;");
|
||||||
jclass cls = env->FindClass("java/lang/Class");
|
|
||||||
gClassRef = (jclass) env->NewGlobalRef(cls);
|
|
||||||
env->DeleteLocalRef(cls);
|
|
||||||
class_getName = env->GetMethodID(gClassRef, "getName", "()Ljava/lang/String;");
|
|
||||||
}
|
|
||||||
auto nameRef = (jstring) env->CallObjectMethod(clazz, class_getName);
|
auto nameRef = (jstring) env->CallObjectMethod(clazz, class_getName);
|
||||||
const char *name = env->GetStringUTFChars(nameRef, nullptr);
|
const char *name = env->GetStringUTFChars(nameRef, nullptr);
|
||||||
string className(name);
|
string className(name);
|
||||||
@@ -172,11 +165,44 @@ jint env_RegisterNatives(
|
|||||||
return old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
|
return old_functions->RegisterNatives(env, clazz, newMethods.get() ?: methods, numMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
|
DCL_HOOK_FUNC(void, androidSetCreateThreadFunc, void* func) {
|
||||||
JNIEnv *env, const char *className, const JNINativeMethod *methods, int numMethods) {
|
LOGD("androidSetCreateThreadFunc\n");
|
||||||
LOGV("jniRegisterNativeMethods [%s]\n", className);
|
do {
|
||||||
auto newMethods = hookAndSaveJNIMethods(className, methods, numMethods);
|
auto get_created_java_vms = reinterpret_cast<jint (*)(JavaVM **, jsize, jsize *)>(
|
||||||
return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods);
|
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
|
// 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);
|
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
|
#undef DCL_HOOK_FUNC
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
@@ -312,7 +286,7 @@ void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods
|
|||||||
if (hooks.empty())
|
if (hooks.empty())
|
||||||
return;
|
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)
|
ZygiskModule::ZygiskModule(int id, void *handle, void *entry)
|
||||||
@@ -501,11 +475,11 @@ void HookContext::sanitize_fds() {
|
|||||||
if (exempted_fds.empty())
|
if (exempted_fds.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
jintArray array = env->NewIntArray(off + exempted_fds.size());
|
jintArray array = env->NewIntArray(static_cast<int>(off + exempted_fds.size()));
|
||||||
if (array == nullptr)
|
if (array == nullptr)
|
||||||
return 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) {
|
for (int fd : exempted_fds) {
|
||||||
if (fd >= 0 && fd < MAX_FD_SIZE) {
|
if (fd >= 0 && fd < MAX_FD_SIZE) {
|
||||||
allowed_fds[fd] = true;
|
allowed_fds[fd] = true;
|
||||||
@@ -592,15 +566,13 @@ void HookContext::run_modules_post() {
|
|||||||
/* Zygisksu changed: Load module fds */
|
/* Zygisksu changed: Load module fds */
|
||||||
void HookContext::app_specialize_pre() {
|
void HookContext::app_specialize_pre() {
|
||||||
flags[APP_SPECIALIZE] = true;
|
flags[APP_SPECIALIZE] = true;
|
||||||
|
info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid);
|
||||||
run_modules_pre();
|
run_modules_pre();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HookContext::app_specialize_post() {
|
void HookContext::app_specialize_post() {
|
||||||
run_modules_post();
|
run_modules_post();
|
||||||
if (info_flags & PROCESS_IS_MAGISK_APP) {
|
|
||||||
setenv("ZYGISK_ENABLED", "1", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanups
|
// Cleanups
|
||||||
env->ReleaseStringUTFChars(args.app->nice_name, process);
|
env->ReleaseStringUTFChars(args.app->nice_name, process);
|
||||||
@@ -739,35 +711,16 @@ void hook_functions() {
|
|||||||
|
|
||||||
PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, fork);
|
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, 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, 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);
|
PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close);
|
||||||
hook_commit();
|
hook_commit();
|
||||||
|
|
||||||
// Remove unhooked methods
|
// Remove unhooked methods
|
||||||
plt_hook_list->erase(
|
plt_hook_list->erase(
|
||||||
std::remove_if(plt_hook_list->begin(), plt_hook_list->end(),
|
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());
|
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() {
|
static bool unhook_functions() {
|
||||||
@@ -776,17 +729,14 @@ static bool unhook_functions() {
|
|||||||
// Restore JNIEnv
|
// Restore JNIEnv
|
||||||
if (g_ctx->env->functions == new_functions) {
|
if (g_ctx->env->functions == new_functions) {
|
||||||
g_ctx->env->functions = old_functions;
|
g_ctx->env->functions = old_functions;
|
||||||
if (gClassRef) {
|
delete new_functions;
|
||||||
g_ctx->env->DeleteGlobalRef(gClassRef);
|
|
||||||
gClassRef = nullptr;
|
|
||||||
class_getName = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unhook JNI methods
|
// Unhook JNI methods
|
||||||
for (const auto &[clz, methods] : *jni_hook_list) {
|
for (const auto &[clz, methods] : *jni_hook_list) {
|
||||||
if (!methods.empty() && old_jniRegisterNativeMethods(
|
if (!methods.empty() && g_ctx->env->RegisterNatives(
|
||||||
g_ctx->env, clz.data(), methods.data(), methods.size()) != 0) {
|
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());
|
LOGE("Failed to restore JNI hook of class [%s]\n", clz.data());
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,31 @@ void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<std::string> split_str(std::string_view s, std::string_view delimiter) {
|
||||||
|
std::list<std::string> ret;
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < s.size()) {
|
||||||
|
auto next = s.find(delimiter, pos);
|
||||||
|
if (next == std::string_view::npos) {
|
||||||
|
ret.emplace_back(s.substr(pos));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.emplace_back(s.substr(pos, next - pos));
|
||||||
|
pos = next + delimiter.size();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string join_str(const std::list<std::string>& list, std::string_view delimiter) {
|
||||||
|
std::string ret;
|
||||||
|
for (auto& s : list) {
|
||||||
|
if (!ret.empty())
|
||||||
|
ret += delimiter;
|
||||||
|
ret += s;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
sDIR make_dir(DIR *dp) {
|
sDIR make_dir(DIR *dp) {
|
||||||
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mntent.h>
|
#include <mntent.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
@@ -34,6 +36,16 @@ private:
|
|||||||
using thread_entry = void *(*)(void *);
|
using thread_entry = void *(*)(void *);
|
||||||
int new_daemon_thread(thread_entry entry, void *arg);
|
int new_daemon_thread(thread_entry entry, void *arg);
|
||||||
|
|
||||||
|
static inline bool str_contains(std::string_view s, std::string_view ss) {
|
||||||
|
return s.find(ss) != std::string_view::npos;
|
||||||
|
}
|
||||||
|
static inline bool str_starts(std::string_view s, std::string_view ss) {
|
||||||
|
return s.size() >= ss.size() && s.compare(0, ss.size(), ss) == 0;
|
||||||
|
}
|
||||||
|
static inline bool str_ends(std::string_view s, std::string_view ss) {
|
||||||
|
return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string_view::npos, ss) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, typename Impl>
|
template<typename T, typename Impl>
|
||||||
class stateless_allocator {
|
class stateless_allocator {
|
||||||
public:
|
public:
|
||||||
@@ -78,6 +90,25 @@ static inline sFILE xopen_file(int fd, const char *mode) {
|
|||||||
return make_file(fdopen(fd, mode));
|
return make_file(fdopen(fd, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class reversed_container {
|
||||||
|
public:
|
||||||
|
reversed_container(T &base) : base(base) {}
|
||||||
|
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
|
||||||
|
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
|
||||||
|
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
|
||||||
|
decltype(std::declval<T>().rend()) end() { return base.rend(); }
|
||||||
|
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
|
||||||
|
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
|
||||||
|
private:
|
||||||
|
T &base;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
reversed_container<T> reversed(T &base) {
|
||||||
|
return reversed_container<T>(base);
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
static inline void default_new(T *&p) { p = new T(); }
|
static inline void default_new(T *&p) { p = new T(); }
|
||||||
|
|
||||||
@@ -97,6 +128,10 @@ int parse_int(std::string_view s);
|
|||||||
|
|
||||||
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn);
|
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn);
|
||||||
|
|
||||||
|
std::list<std::string> split_str(std::string_view s, std::string_view delimiter);
|
||||||
|
|
||||||
|
std::string join_str(const std::list<std::string>& list, std::string_view delimiter);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T align_to(T v, int a) {
|
static inline T align_to(T v, int a) {
|
||||||
static_assert(std::is_integral<T>::value);
|
static_assert(std::is_integral<T>::value);
|
||||||
|
|||||||
@@ -111,12 +111,9 @@ namespace {
|
|||||||
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
|
||||||
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
|
||||||
|
|
||||||
PROCESS_IS_SYS_UI = (1u << 29),
|
PROCESS_IS_SYS_UI = (1u << 31),
|
||||||
DENYLIST_ENFORCING = (1u << 30),
|
|
||||||
PROCESS_IS_MAGISK_APP = (1u << 31),
|
|
||||||
|
|
||||||
UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING),
|
PRIVATE_MASK = PROCESS_IS_SYS_UI
|
||||||
PRIVATE_MASK = (PROCESS_IS_SYS_UI | DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct api_abi_base {
|
struct api_abi_base {
|
||||||
|
|||||||
71
loader/src/injector/unmount.cpp
Normal file
71
loader/src/injector/unmount.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "misc.hpp"
|
||||||
|
#include "zygisk.hpp"
|
||||||
|
|
||||||
|
using namespace std::string_view_literals;
|
||||||
|
|
||||||
|
static void lazy_unmount(const char* mountpoint) {
|
||||||
|
if (umount2(mountpoint, MNT_DETACH) != -1) {
|
||||||
|
LOGD("Unmounted (%s)", mountpoint);
|
||||||
|
} else {
|
||||||
|
LOGW("Failed to unmount: %s (%s)", strerror(errno), mountpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PARSE_OPT(name, flag) \
|
||||||
|
if (opt == name) { \
|
||||||
|
flags |= (flag); \
|
||||||
|
return true; \
|
||||||
|
}
|
||||||
|
|
||||||
|
void revert_unmount() {
|
||||||
|
std::vector<std::string> targets;
|
||||||
|
std::list<std::pair<std::string, std::string>> backups;
|
||||||
|
|
||||||
|
targets.emplace_back("/data/adb/ksu/modules");
|
||||||
|
parse_mnt("/proc/self/mounts", [&](mntent* mentry) {
|
||||||
|
if (str_starts(mentry->mnt_fsname, "/data/adb/")) {
|
||||||
|
targets.emplace_back(mentry->mnt_dir);
|
||||||
|
}
|
||||||
|
if (mentry->mnt_type == "overlay"sv) {
|
||||||
|
if (str_contains(mentry->mnt_opts, "/data/adb/ksu/modules")) {
|
||||||
|
targets.emplace_back(mentry->mnt_dir);
|
||||||
|
} else {
|
||||||
|
backups.emplace_back(mentry->mnt_dir, mentry->mnt_opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& s: reversed(targets)) {
|
||||||
|
lazy_unmount(s.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_mnt("/proc/self/mounts", [&](mntent* mentry) {
|
||||||
|
if (mentry->mnt_type == "overlay"sv) {
|
||||||
|
backups.remove_if([&](auto& mnt) {
|
||||||
|
return mnt.first == mentry->mnt_dir && mnt.second == mentry->mnt_opts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& mnt: backups) {
|
||||||
|
auto opts = split_str(mnt.second, ",");
|
||||||
|
unsigned long flags = 0;
|
||||||
|
opts.remove_if([&](auto& opt) {
|
||||||
|
PARSE_OPT(MNTOPT_RO, MS_RDONLY)
|
||||||
|
PARSE_OPT(MNTOPT_NOSUID, MS_NOSUID)
|
||||||
|
PARSE_OPT("relatime", MS_RELATIME)
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
auto mnt_data = join_str(opts, ",");
|
||||||
|
if (mount("overlay", mnt.first.data(), "overlay", flags, mnt_data.data()) != -1) {
|
||||||
|
LOGD("Remounted (%s)", mnt.first.data());
|
||||||
|
} else {
|
||||||
|
LOGW("Failed to remount: %s (%s, %s)", strerror(errno), mnt.first.data(), mnt_data.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,9 +43,9 @@ void Constructor() {
|
|||||||
std::string native_bridge;
|
std::string native_bridge;
|
||||||
do {
|
do {
|
||||||
if (!zygiskd::PingHeartbeat()) break;
|
if (!zygiskd::PingHeartbeat()) break;
|
||||||
|
#ifdef NDEBUG
|
||||||
logging::setfd(zygiskd::RequestLogcatFd());
|
logging::setfd(zygiskd::RequestLogcatFd());
|
||||||
|
#endif
|
||||||
LOGI("Read native bridge");
|
LOGI("Read native bridge");
|
||||||
native_bridge = zygiskd::ReadNativeBridge();
|
native_bridge = zygiskd::ReadNativeBridge();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ val moduleId: String by rootProject.extra
|
|||||||
val moduleName: String by rootProject.extra
|
val moduleName: String by rootProject.extra
|
||||||
val verCode: Int by rootProject.extra
|
val verCode: Int by rootProject.extra
|
||||||
val verName: String by rootProject.extra
|
val verName: String by rootProject.extra
|
||||||
|
val minKsuVersion: Int by rootProject.extra
|
||||||
|
val minKsudVersion: Int by rootProject.extra
|
||||||
|
val maxKsuVersion: Int by rootProject.extra
|
||||||
|
val minMagiskVersion: Int by rootProject.extra
|
||||||
|
|
||||||
android.buildFeatures {
|
android.buildFeatures {
|
||||||
androidResources = false
|
androidResources = false
|
||||||
@@ -49,7 +53,11 @@ androidComponents.onVariants { variant ->
|
|||||||
from("$projectDir/src") {
|
from("$projectDir/src") {
|
||||||
include("customize.sh", "daemon.sh")
|
include("customize.sh", "daemon.sh")
|
||||||
val tokens = mapOf(
|
val tokens = mapOf(
|
||||||
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false"
|
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
|
||||||
|
"MIN_KSU_VERSION" to "$minKsuVersion",
|
||||||
|
"MIN_KSUD_VERSION" to "$minKsudVersion",
|
||||||
|
"MAX_KSU_VERSION" to "$maxKsuVersion",
|
||||||
|
"MIN_MAGISK_VERSION" to "$minMagiskVersion",
|
||||||
)
|
)
|
||||||
filter<ReplaceTokens>("tokens" to tokens)
|
filter<ReplaceTokens>("tokens" to tokens)
|
||||||
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
|
||||||
@@ -106,7 +114,7 @@ androidComponents.onVariants { variant ->
|
|||||||
val installMagiskTask = task<Exec>("installMagisk$variantCapped") {
|
val installMagiskTask = task<Exec>("installMagisk$variantCapped") {
|
||||||
group = "module"
|
group = "module"
|
||||||
dependsOn(pushTask)
|
dependsOn(pushTask)
|
||||||
commandLine("adb", "shell", "su", "-c", "KSU=true magisk --install-module /data/local/tmp/$zipFileName")
|
commandLine("adb", "shell", "su", "-c", "magisk --install-module /data/local/tmp/$zipFileName")
|
||||||
}
|
}
|
||||||
|
|
||||||
task<Exec>("installKsuAndReboot$variantCapped") {
|
task<Exec>("installKsuAndReboot$variantCapped") {
|
||||||
|
|||||||
@@ -1,31 +1,61 @@
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
SKIPUNZIP=1
|
SKIPUNZIP=1
|
||||||
|
|
||||||
if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then
|
DEBUG=@DEBUG@
|
||||||
|
MIN_KSU_VERSION=@MIN_KSU_VERSION@
|
||||||
|
MIN_KSUD_VERSION=@MIN_KSUD_VERSION@
|
||||||
|
MAX_KSU_VERSION=@MAX_KSU_VERSION@
|
||||||
|
MIN_MAGISK_VERSION=@MIN_MAGISK_VERSION@
|
||||||
|
|
||||||
|
if [ "$BOOTMODE" ] && [ "$KSU" ]; then
|
||||||
ui_print "- Installing from KernelSU app"
|
ui_print "- Installing from KernelSU app"
|
||||||
|
ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
|
||||||
|
if ! [ "$KSU_KERNEL_VER_CODE" ] || [ "$KSU_KERNEL_VER_CODE" -lt "$MIN_KSU_VERSION" ]; then
|
||||||
|
ui_print "*********************************************************"
|
||||||
|
ui_print "! KernelSU version is too old!"
|
||||||
|
ui_print "! Please update KernelSU to latest version"
|
||||||
|
abort "*********************************************************"
|
||||||
|
elif [ "$KSU_KERNEL_VER_CODE" -ge "$MAX_KSU_VERSION" ]; then
|
||||||
|
ui_print "*********************************************************"
|
||||||
|
ui_print "! KernelSU version abnormal!"
|
||||||
|
ui_print "! Please integrate KernelSU into your kernel"
|
||||||
|
ui_print " as submodule instead of copying the source code"
|
||||||
|
abort "*********************************************************"
|
||||||
|
fi
|
||||||
|
if ! [ "$KSU_VER_CODE" ] || [ "$KSU_VER_CODE" -lt "$MIN_KSUD_VERSION" ]; then
|
||||||
|
ui_print "*********************************************************"
|
||||||
|
ui_print "! ksud version is too old!"
|
||||||
|
ui_print "! Please update KernelSU Manager to latest version"
|
||||||
|
abort "*********************************************************"
|
||||||
|
fi
|
||||||
|
if [ "$(which magisk)" ]; then
|
||||||
|
ui_print "*********************************************************"
|
||||||
|
ui_print "! Multiple root implementation is NOT supported!"
|
||||||
|
ui_print "! Please uninstall Magisk before installing Zygisksu"
|
||||||
|
abort "*********************************************************"
|
||||||
|
fi
|
||||||
|
elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then
|
||||||
|
ui_print "- Installing from Magisk app"
|
||||||
|
if [ "$MAGISK_VER_CODE" -lt "$MIN_MAGISK_VERSION" ]; then
|
||||||
|
ui_print "*********************************************************"
|
||||||
|
ui_print "! Magisk version is too old!"
|
||||||
|
ui_print "! Please update Magisk to latest version"
|
||||||
|
abort "*********************************************************"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
ui_print "*********************************************************"
|
ui_print "*********************************************************"
|
||||||
ui_print "! Install from recovery or Magisk is NOT supported"
|
ui_print "! Install from recovery is not supported"
|
||||||
ui_print "! Please install from KernelSU app"
|
ui_print "! Please install from KernelSU or Magisk app"
|
||||||
abort "*********************************************************"
|
abort "*********************************************************"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION=$(grep_prop version "${TMPDIR}/module.prop")
|
VERSION=$(grep_prop version "${TMPDIR}/module.prop")
|
||||||
ui_print "- Installing Zygisksu $VERSION"
|
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 "*********************************************************"
|
|
||||||
ui_print "! KernelSU version is too old!"
|
|
||||||
ui_print "! Please update KernelSU to latest version"
|
|
||||||
abort "*********************************************************"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check android
|
# check android
|
||||||
if [ "$API" -lt 29 ]; then
|
if [ "$API" -lt 29 ]; then
|
||||||
ui_print "! Unsupported sdk: $API"
|
ui_print "! Unsupported sdk: $API"
|
||||||
abort "! Minimal supported sdk is 29 (Android 10.0)"
|
abort "! Minimal supported sdk is 29 (Android 10)"
|
||||||
else
|
else
|
||||||
ui_print "- Device sdk: $API"
|
ui_print "- Device sdk: $API"
|
||||||
fi
|
fi
|
||||||
@@ -50,12 +80,14 @@ extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip"
|
|||||||
extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
|
extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
|
||||||
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
||||||
|
|
||||||
ui_print "- Checking SELinux patches"
|
if [ "$KSU" ]; then
|
||||||
if ! /data/adb/ksud sepolicy check "$TMPDIR/sepolicy.rule"; then
|
ui_print "- Checking SELinux patches"
|
||||||
ui_print "*********************************************************"
|
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
|
||||||
ui_print "! Unable to apply SELinux patches!"
|
ui_print "*********************************************************"
|
||||||
ui_print "! Your kernel may not support SELinux patch fully"
|
ui_print "! Unable to apply SELinux patches!"
|
||||||
abort "*********************************************************"
|
ui_print "! Your kernel may not support SELinux patch fully"
|
||||||
|
abort "*********************************************************"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ui_print "- Extracting module files"
|
ui_print "- Extracting module files"
|
||||||
@@ -111,17 +143,19 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ui_print "- Hex patching"
|
if [ $DEBUG = false ]; then
|
||||||
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
ui_print "- Hex patching"
|
||||||
if [ "$HAS32BIT" = true ]; then
|
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
|
if [ "$HAS32BIT" = true ]; then
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
|
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
|
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
|
||||||
fi
|
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
|
||||||
if [ "$HAS64BIT" = true ]; then
|
fi
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
|
if [ "$HAS64BIT" = true ]; then
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libinjector.so"
|
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
|
||||||
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
|
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
|
fi
|
||||||
|
|
||||||
ui_print "- Setting permissions"
|
ui_print "- Setting permissions"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
|
|
||||||
DEBUG=@DEBUG@
|
DEBUG=@DEBUG@
|
||||||
MODDIR=${0%/*}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2155
|
# shellcheck disable=SC2155
|
||||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
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"
|
log -p i -t "zygisksu" "Start watchdog"
|
||||||
resetprop ro.dalvik.vm.native.bridge libzygiskloader.so
|
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,24 @@
|
|||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
|
|
||||||
MODDIR=${0%/*}
|
MODDIR=${0%/*}
|
||||||
|
if [ "$ZYGISK_ENABLED" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$MODDIR"
|
||||||
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
|
||||||
unshare -m sh -c "$MODDIR/daemon.sh $@&"
|
|
||||||
|
if [ "$(which magisk)" ] && [ ".." -ef "/data/adb/modules" ]; then
|
||||||
|
for file in ../*; do
|
||||||
|
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
|
||||||
|
if [ -f "$file/post-fs-data.sh" ]; then
|
||||||
|
cd "$file"
|
||||||
|
log -p i -t "zygisksu" "Manually trigger post-fs-data.sh for $file"
|
||||||
|
sh "$(realpath ./post-fs-data.sh)"
|
||||||
|
cd "$MODDIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
sh -c "./daemon.sh $@&"
|
||||||
|
|||||||
@@ -10,3 +10,6 @@ allow * magisk_file lnk_file *
|
|||||||
allow * magisk_file sock_file *
|
allow * magisk_file sock_file *
|
||||||
|
|
||||||
allow system_server system_server process execmem
|
allow system_server system_server process execmem
|
||||||
|
allow zygote mnt_vendor_file dir search
|
||||||
|
allow zygote system_file dir mounton
|
||||||
|
allow zygote labeledfs filesystem mount
|
||||||
|
|||||||
@@ -1 +1,21 @@
|
|||||||
#!/system/bin/sh
|
#!/system/bin/sh
|
||||||
|
|
||||||
|
MODDIR=${0%/*}
|
||||||
|
if [ "$ZYGISK_ENABLED" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$MODDIR"
|
||||||
|
|
||||||
|
if [ "$(which magisk)" ] && [ ".." -ef "/data/adb/modules" ]; then
|
||||||
|
for file in ../*; do
|
||||||
|
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
|
||||||
|
if [ -f "$file/service.sh" ]; then
|
||||||
|
cd "$file"
|
||||||
|
log -p i -t "zygisksu" "Manually trigger service.sh for $file"
|
||||||
|
sh "$(realpath ./service.sh)"
|
||||||
|
cd "$MODDIR"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ pluginManagement {
|
|||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.library") version "7.4.0"
|
id("com.android.library") version "7.4.1"
|
||||||
id("com.android.application") version "7.4.0"
|
id("com.android.application") version "7.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,18 @@ android_logger = "0.12.0"
|
|||||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||||
clap = { version = "4.1.4", features = ["derive"] }
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
const_format = "0.2.5"
|
const_format = "0.2.5"
|
||||||
|
konst = "0.3.4"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
memfd = "0.6.2"
|
memfd = "0.6.2"
|
||||||
nix = "0.26.2"
|
nix = "0.26.2"
|
||||||
num_enum = "0.5.9"
|
num_enum = "0.5.9"
|
||||||
|
once_cell = "1.17.1"
|
||||||
passfd = "0.1.5"
|
passfd = "0.1.5"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|
||||||
|
binder = { path = "src/external/binder_rs/binder" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ plugins {
|
|||||||
|
|
||||||
val verName: String by rootProject.extra
|
val verName: String by rootProject.extra
|
||||||
val verCode: Int by rootProject.extra
|
val verCode: Int by rootProject.extra
|
||||||
|
val minKsuVersion: Int by rootProject.extra
|
||||||
|
val maxKsuVersion: Int by rootProject.extra
|
||||||
|
val minMagiskVersion: Int by rootProject.extra
|
||||||
|
|
||||||
android.buildFeatures {
|
android.buildFeatures {
|
||||||
androidResources = false
|
androidResources = false
|
||||||
@@ -20,7 +23,11 @@ cargo {
|
|||||||
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
|
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
|
||||||
profile = if (isDebug) "debug" else "release"
|
profile = if (isDebug) "debug" else "release"
|
||||||
exec = { spec, _ ->
|
exec = { spec, _ ->
|
||||||
|
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
|
||||||
spec.environment("VERSION_CODE", verCode)
|
spec.environment("VERSION_CODE", verCode)
|
||||||
spec.environment("VERSION_NAME", verName)
|
spec.environment("VERSION_NAME", verName)
|
||||||
|
spec.environment("MIN_KSU_VERSION", minKsuVersion)
|
||||||
|
spec.environment("MAX_KSU_VERSION", maxKsuVersion)
|
||||||
|
spec.environment("MIN_MAGISK_VERSION", minMagiskVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
|
use konst::primitive::parse_i32;
|
||||||
|
use konst::unwrap_ctx;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
pub const VERSION_NAME: &str = env!("VERSION_NAME");
|
pub const VERSION_NAME: &str = env!("VERSION_NAME");
|
||||||
pub const VERSION_CODE: &str = env!("VERSION_CODE");
|
pub const VERSION_CODE: &str = env!("VERSION_CODE");
|
||||||
pub const VERSION_FULL: &str = concatcp!(VERSION_NAME, " (", VERSION_CODE, ")");
|
pub const VERSION_FULL: &str = concatcp!(VERSION_NAME, " (", VERSION_CODE, ")");
|
||||||
|
pub const MIN_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_KSU_VERSION")));
|
||||||
|
pub const MAX_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MAX_KSU_VERSION")));
|
||||||
|
pub const MIN_MAGISK_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_MAGISK_VERSION")));
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
||||||
@@ -23,14 +28,14 @@ macro_rules! lp_select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
|
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 SOCKET_PLACEHOLDER: &str = "socket_placeholder";
|
||||||
|
|
||||||
pub const PATH_KSU_MODULE_DIR: &str = "/data/adb/ksu/modules";
|
pub const PATH_MODULE_DIR: &str = "..";
|
||||||
pub const PATH_ZYGISKSU_DIR: &str = concatcp!(PATH_KSU_MODULE_DIR, "/zygisksu");
|
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
||||||
pub const PATH_ZYGISKD32: &str = concatcp!(PATH_ZYGISKSU_DIR, "/bin/zygiskd32");
|
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
||||||
pub const PATH_ZYGISKD64: &str = concatcp!(PATH_ZYGISKSU_DIR, "/bin/zygiskd64");
|
|
||||||
pub const PATH_DAEMON_LOCK: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd.lock");
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
|
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@@ -38,7 +43,13 @@ pub enum DaemonSocketAction {
|
|||||||
PingHeartbeat,
|
PingHeartbeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
ReadNativeBridge,
|
ReadNativeBridge,
|
||||||
|
GetProcessFlags,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
GetModuleDir,
|
GetModuleDir,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zygisk process flags
|
||||||
|
pub const PROCESS_GRANTED_ROOT: u32 = 1 << 0;
|
||||||
|
pub const PROCESS_ON_DENYLIST: u32 = 1 << 1;
|
||||||
|
pub const PROCESS_IS_SYSUI: u32 = 1 << 31;
|
||||||
|
|||||||
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
@@ -3,10 +3,12 @@
|
|||||||
mod companion;
|
mod companion;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod dl;
|
mod dl;
|
||||||
|
mod root_impl;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod watchdog;
|
mod watchdog;
|
||||||
mod zygiskd;
|
mod zygiskd;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::{Subcommand, Parser};
|
use clap::{Subcommand, Parser};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@@ -35,19 +37,23 @@ fn init_android_logger(tag: &str) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn start() -> Result<()> {
|
||||||
|
root_impl::setup()?;
|
||||||
|
let cli = Args::parse();
|
||||||
|
match cli.command {
|
||||||
|
Commands::Watchdog => watchdog::entry()?,
|
||||||
|
Commands::Daemon => zygiskd::entry()?,
|
||||||
|
Commands::Companion { fd } => companion::entry(fd)?,
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let process = std::env::args().next().unwrap();
|
let process = std::env::args().next().unwrap();
|
||||||
let nice_name = process.split('/').last().unwrap();
|
let nice_name = process.split('/').last().unwrap();
|
||||||
init_android_logger(nice_name);
|
init_android_logger(nice_name);
|
||||||
|
|
||||||
let cli = Args::parse();
|
if let Err(e) = start() {
|
||||||
let result = match cli.command {
|
|
||||||
Commands::Watchdog => watchdog::entry(),
|
|
||||||
Commands::Daemon => zygiskd::entry(lp_select!(false, true)),
|
|
||||||
Commands::Companion { fd } => companion::entry(fd),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(e) = &result {
|
|
||||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
zygiskd/src/root_impl/kernelsu.rs
Normal file
36
zygiskd/src/root_impl/kernelsu.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use nix::libc::prctl;
|
||||||
|
use crate::constants::{MIN_KSU_VERSION, MAX_KSU_VERSION};
|
||||||
|
|
||||||
|
const KERNEL_SU_OPTION: i32 = 0xdeadbeefu32 as i32;
|
||||||
|
|
||||||
|
const CMD_GET_VERSION: usize = 2;
|
||||||
|
const CMD_GET_ALLOW_LIST: usize = 5;
|
||||||
|
const CMD_GET_DENY_LIST: usize = 6;
|
||||||
|
|
||||||
|
pub fn is_kernel_su() -> Result<bool> {
|
||||||
|
let mut version = 0;
|
||||||
|
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
|
||||||
|
return match version {
|
||||||
|
0 => Ok(false),
|
||||||
|
MIN_KSU_VERSION..=MAX_KSU_VERSION => Ok(true),
|
||||||
|
1..=MIN_KSU_VERSION => bail!("KernelSU version too old: {}", version),
|
||||||
|
_ => bail!("KernelSU version abnormal: {}", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||||
|
let mut size = 1024u32;
|
||||||
|
let mut uids = vec![0; size as usize];
|
||||||
|
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_ALLOW_LIST, uids.as_mut_ptr(), &mut size as *mut u32) };
|
||||||
|
uids.resize(size as usize, 0);
|
||||||
|
uids.contains(&uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||||
|
let mut size = 1024u32;
|
||||||
|
let mut uids = vec![0; size as usize];
|
||||||
|
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_DENY_LIST, uids.as_mut_ptr(), &mut size as *mut u32) };
|
||||||
|
uids.resize(size as usize, 0);
|
||||||
|
uids.contains(&uid)
|
||||||
|
}
|
||||||
30
zygiskd/src/root_impl/magisk.rs
Normal file
30
zygiskd/src/root_impl/magisk.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use anyhow::{bail, Result};
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
use crate::constants::MIN_MAGISK_VERSION;
|
||||||
|
|
||||||
|
pub fn is_magisk() -> Result<bool> {
|
||||||
|
let version: Option<i32> = Command::new("magisk")
|
||||||
|
.arg("-V")
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn().ok()
|
||||||
|
.and_then(|child| child.wait_with_output().ok())
|
||||||
|
.and_then(|output| String::from_utf8(output.stdout).ok())
|
||||||
|
.and_then(|output| output.trim().parse().ok());
|
||||||
|
if let Some(version) = version {
|
||||||
|
if version < MIN_MAGISK_VERSION {
|
||||||
|
bail!("Magisk version too old: {}", version);
|
||||||
|
}
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||||
|
// TODO: uid_on_allowlist
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||||
|
// TODO: uid_on_denylist
|
||||||
|
return false;
|
||||||
|
}
|
||||||
40
zygiskd/src/root_impl/mod.rs
Normal file
40
zygiskd/src/root_impl/mod.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
mod kernelsu;
|
||||||
|
mod magisk;
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
|
enum RootImpl {
|
||||||
|
KernelSU,
|
||||||
|
Magisk,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ROOT_IMPL: OnceCell<RootImpl> = OnceCell::new();
|
||||||
|
|
||||||
|
pub fn setup() -> Result<()> {
|
||||||
|
if kernelsu::is_kernel_su()? {
|
||||||
|
if let Ok(true) = magisk::is_magisk() {
|
||||||
|
bail!("Multiple root implementation");
|
||||||
|
}
|
||||||
|
let _ = ROOT_IMPL.set(RootImpl::KernelSU);
|
||||||
|
} else if magisk::is_magisk()? {
|
||||||
|
let _ = ROOT_IMPL.set(RootImpl::Magisk);
|
||||||
|
} else {
|
||||||
|
bail!("Unknown root implementation");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||||
|
match ROOT_IMPL.get().unwrap() {
|
||||||
|
RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid),
|
||||||
|
RootImpl::Magisk => magisk::uid_on_allowlist(uid),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||||
|
match ROOT_IMPL.get().unwrap() {
|
||||||
|
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
|
||||||
|
RootImpl::Magisk => magisk::uid_on_denylist(uid),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::constants;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nix::unistd::gettid;
|
use nix::unistd::gettid;
|
||||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
|
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()
|
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")
|
Command::new("resetprop")
|
||||||
.arg(constants::PROP_NATIVE_BRIDGE)
|
.arg(name)
|
||||||
.arg(get_native_bridge())
|
.arg(value)
|
||||||
.spawn()?.wait()?;
|
.spawn()?.wait()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -41,6 +40,7 @@ pub trait UnixStreamExt {
|
|||||||
fn read_usize(&mut self) -> Result<usize>;
|
fn read_usize(&mut self) -> Result<usize>;
|
||||||
fn read_string(&mut self) -> Result<String>;
|
fn read_string(&mut self) -> Result<String>;
|
||||||
fn write_u8(&mut self, value: u8) -> Result<()>;
|
fn write_u8(&mut self, value: u8) -> Result<()>;
|
||||||
|
fn write_u32(&mut self, value: u32) -> Result<()>;
|
||||||
fn write_usize(&mut self, value: usize) -> Result<()>;
|
fn write_usize(&mut self, value: usize) -> Result<()>;
|
||||||
fn write_string(&mut self, value: &str) -> Result<()>;
|
fn write_string(&mut self, value: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,11 @@ impl UnixStreamExt for UnixStream {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_u32(&mut self, value: u32) -> Result<()> {
|
||||||
|
self.write_all(&value.to_ne_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn write_usize(&mut self, value: usize) -> Result<()> {
|
fn write_usize(&mut self, value: usize) -> Result<()> {
|
||||||
self.write_all(&value.to_ne_bytes())?;
|
self.write_all(&value.to_ne_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
use crate::constants;
|
use crate::{constants, utils};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use nix::fcntl::{flock, FlockArg};
|
use nix::unistd::{getgid, getuid, Pid};
|
||||||
use nix::unistd::{getgid, getuid};
|
|
||||||
use std::os::unix::prelude::AsRawFd;
|
|
||||||
use std::process::{Child, Command};
|
use std::process::{Child, Command};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::{fs, thread};
|
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<()> {
|
pub fn entry() -> Result<()> {
|
||||||
log::info!("Start zygisksu watchdog");
|
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 = fs::read_to_string("/proc/self/attr/current")?;
|
||||||
let context = context.trim_end_matches('\0');
|
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" {
|
if context != "u:r:su:s0" && context != "u:r:magisk:s0" {
|
||||||
bail!("SELinux context incorrect: {context}");
|
bail!("SELinux context incorrect: {context}");
|
||||||
}
|
}
|
||||||
@@ -40,38 +41,65 @@ fn check_permission() -> Result<()> {
|
|||||||
|
|
||||||
fn ensure_single_instance() -> Result<()> {
|
fn ensure_single_instance() -> Result<()> {
|
||||||
log::info!("Ensure single instance");
|
log::info!("Ensure single instance");
|
||||||
let metadata = fs::metadata(constants::PATH_ZYGISKSU_DIR);
|
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
|
||||||
if metadata.is_err() || !metadata.unwrap().is_dir() {
|
match utils::abstract_namespace_socket(&name) {
|
||||||
bail!("Zygisksu is not installed");
|
Ok(socket) => unsafe { LOCK = Some(socket) },
|
||||||
}
|
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
|
||||||
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?");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_daemon() -> Result<()> {
|
fn spawn_daemon() -> Result<()> {
|
||||||
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
let mut lives = 5;
|
||||||
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
loop {
|
||||||
let (sender, receiver) = mpsc::channel();
|
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
||||||
let spawn = |mut daemon: Child| {
|
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
||||||
let sender = sender.clone();
|
let mut child_ids = vec![];
|
||||||
thread::spawn(move || {
|
let (sender, receiver) = mpsc::channel();
|
||||||
let result = daemon.wait().unwrap();
|
let mut spawn = |mut daemon: Child| {
|
||||||
log::error!("Daemon process {} died: {}", daemon.id(), result);
|
child_ids.push(daemon.id());
|
||||||
drop(daemon);
|
let sender = sender.clone();
|
||||||
sender.send(()).unwrap();
|
thread::spawn(move || {
|
||||||
});
|
let result = daemon.wait().unwrap();
|
||||||
};
|
log::error!("Daemon process {} died: {}", daemon.id(), result);
|
||||||
if let Ok(it) = daemon32 { spawn(it) }
|
drop(daemon);
|
||||||
if let Ok(it) = daemon64 { spawn(it) }
|
let _ = sender.send(());
|
||||||
let _ = receiver.recv();
|
});
|
||||||
bail!("Daemon process died");
|
};
|
||||||
|
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::constants::DaemonSocketAction;
|
||||||
use crate::utils::{restore_native_bridge, UnixStreamExt};
|
use crate::utils::UnixStreamExt;
|
||||||
use crate::{constants, utils};
|
use crate::{constants, lp_select, root_impl, utils};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use memfd::Memfd;
|
use memfd::Memfd;
|
||||||
use nix::{
|
use nix::{
|
||||||
@@ -31,10 +31,10 @@ struct Context {
|
|||||||
modules: Vec<Module>,
|
modules: Vec<Module>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry(is64: bool) -> Result<()> {
|
pub fn entry() -> Result<()> {
|
||||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
||||||
|
|
||||||
let arch = get_arch(is64)?;
|
let arch = get_arch()?;
|
||||||
log::debug!("Daemon architecture: {arch}");
|
log::debug!("Daemon architecture: {arch}");
|
||||||
|
|
||||||
log::info!("Load modules");
|
log::info!("Load modules");
|
||||||
@@ -47,7 +47,7 @@ pub fn entry(is64: bool) -> Result<()> {
|
|||||||
let context = Arc::new(context);
|
let context = Arc::new(context);
|
||||||
|
|
||||||
log::info!("Create socket");
|
log::info!("Create socket");
|
||||||
let listener = create_daemon_socket(is64)?;
|
let listener = create_daemon_socket()?;
|
||||||
|
|
||||||
log::info!("Handle zygote connections");
|
log::info!("Handle zygote connections");
|
||||||
for stream in listener.incoming() {
|
for stream in listener.incoming() {
|
||||||
@@ -63,23 +63,21 @@ pub fn entry(is64: bool) -> Result<()> {
|
|||||||
Ok(())
|
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 output = Command::new("getprop").arg("ro.product.cpu.abi").output()?;
|
||||||
let system_arch = String::from_utf8(output.stdout)?;
|
let system_arch = String::from_utf8(output.stdout)?;
|
||||||
let is_arm = system_arch.contains("arm");
|
if system_arch.contains("arm") {
|
||||||
let is_x86 = system_arch.contains("x86");
|
return Ok(lp_select!("armeabi-v7a", "arm64-v8a"));
|
||||||
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("x86") {
|
||||||
|
return Ok(lp_select!("x86", "x86_64"));
|
||||||
|
}
|
||||||
|
bail!("Unsupported system architecture: {}", system_arch);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||||
let mut modules = Vec::new();
|
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,
|
Ok(dir) => dir,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Failed reading modules directory: {}", e);
|
log::warn!("Failed reading modules directory: {}", e);
|
||||||
@@ -95,7 +93,7 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log::info!(" Loading module `{name}`...");
|
log::info!(" Loading module `{name}`...");
|
||||||
let memfd = match create_memfd(&name, &so_path) {
|
let memfd = match create_memfd(&so_path) {
|
||||||
Ok(memfd) => memfd,
|
Ok(memfd) => memfd,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!(" Failed to create memfd for `{name}`: {e}");
|
log::warn!(" Failed to create memfd for `{name}`: {e}");
|
||||||
@@ -118,10 +116,9 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
|||||||
Ok(modules)
|
Ok(modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
|
fn create_memfd(so_path: &PathBuf) -> Result<Memfd> {
|
||||||
let opts = memfd::MemfdOptions::default().allow_sealing(true);
|
let opts = memfd::MemfdOptions::default().allow_sealing(true);
|
||||||
let memfd = opts.create(name)?;
|
let memfd = opts.create("jit-cache")?;
|
||||||
|
|
||||||
let file = fs::File::open(so_path)?;
|
let file = fs::File::open(so_path)?;
|
||||||
let mut reader = std::io::BufReader::new(file);
|
let mut reader = std::io::BufReader::new(file);
|
||||||
let mut writer = memfd.as_file();
|
let mut writer = memfd.as_file();
|
||||||
@@ -137,10 +134,10 @@ fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
|
|||||||
Ok(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")?;
|
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||||
let suffix = if is64 { "zygiskd64" } else { "zygiskd32" };
|
let prefix = lp_select!("zygiskd32", "zygiskd64");
|
||||||
let name = String::from(suffix) + constants::SOCKET_PLACEHOLDER;
|
let name = String::from(prefix) + constants::SOCKET_PLACEHOLDER;
|
||||||
let listener = utils::abstract_namespace_socket(&name)?;
|
let listener = utils::abstract_namespace_socket(&name)?;
|
||||||
log::debug!("Daemon socket: {name}");
|
log::debug!("Daemon socket: {name}");
|
||||||
Ok(listener)
|
Ok(listener)
|
||||||
@@ -175,7 +172,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
|||||||
log::trace!("New daemon action {:?}", action);
|
log::trace!("New daemon action {:?}", action);
|
||||||
match action {
|
match action {
|
||||||
DaemonSocketAction::PingHeartbeat => {
|
DaemonSocketAction::PingHeartbeat => {
|
||||||
restore_native_bridge()?;
|
// Do nothing
|
||||||
}
|
}
|
||||||
DaemonSocketAction::RequestLogcatFd => {
|
DaemonSocketAction::RequestLogcatFd => {
|
||||||
loop {
|
loop {
|
||||||
@@ -195,6 +192,18 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
|||||||
DaemonSocketAction::ReadNativeBridge => {
|
DaemonSocketAction::ReadNativeBridge => {
|
||||||
stream.write_string(&context.native_bridge)?;
|
stream.write_string(&context.native_bridge)?;
|
||||||
}
|
}
|
||||||
|
DaemonSocketAction::GetProcessFlags => {
|
||||||
|
let uid = stream.read_u32()? as i32;
|
||||||
|
let mut flags = 0u32;
|
||||||
|
if root_impl::uid_on_allowlist(uid) {
|
||||||
|
flags |= constants::PROCESS_GRANTED_ROOT;
|
||||||
|
}
|
||||||
|
if root_impl::uid_on_denylist(uid) {
|
||||||
|
flags |= constants::PROCESS_ON_DENYLIST;
|
||||||
|
}
|
||||||
|
// TODO: PROCESS_IS_SYSUI?
|
||||||
|
stream.write_u32(flags)?;
|
||||||
|
}
|
||||||
DaemonSocketAction::ReadModules => {
|
DaemonSocketAction::ReadModules => {
|
||||||
stream.write_usize(context.modules.len())?;
|
stream.write_usize(context.modules.len())?;
|
||||||
for module in context.modules.iter() {
|
for module in context.modules.iter() {
|
||||||
@@ -223,7 +232,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
|||||||
DaemonSocketAction::GetModuleDir => {
|
DaemonSocketAction::GetModuleDir => {
|
||||||
let index = stream.read_usize()?;
|
let index = stream.read_usize()?;
|
||||||
let module = &context.modules[index];
|
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)?;
|
let dir = fs::File::open(dir)?;
|
||||||
stream.send_fd(dir.as_raw_fd())?;
|
stream.send_fd(dir.as_raw_fd())?;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user