Compare commits

...

24 Commits

Author SHA1 Message Date
Nullptr
9d648d9aa4 Bump to 0.6.2 2023-03-03 18:20:50 +08:00
Nullptr
843086f6f3 Add more sepolicy 2023-03-03 18:20:50 +08:00
Nullptr
49e3ac9d7a Fix dependency bug: OnceCell crashes on 32 bit 2023-03-03 18:20:36 +08:00
Nullptr
446ed92f26 Change module dir and bump to 0.6.1 2023-03-01 12:42:12 +08:00
Nullptr
2e9cbf79a7 Bump to 0.6.0 2023-02-28 21:32:13 +08:00
Nullptr
cce8e6686f Implement uid_on_allowlist for Magisk 2023-02-28 20:48:32 +08:00
Nullptr
ff2658f2de Rename libs 2023-02-28 20:48:20 +08:00
Nullptr
f465cbf810 Refactor to better support Magisk 2023-02-28 19:50:41 +08:00
Nullptr
09b6673ab0 Implement revert_unmount_magisk 2023-02-28 16:03:41 +08:00
Nullptr
5f8eb4af09 No submodule 2023-02-28 12:37:58 +08:00
Nullptr
8affc8f991 Fix zygote restart & Show zygisksu status on module.prop 2023-02-26 11:54:52 +08:00
Nullptr
ec8475bca5 Better unmount refine 2023-02-25 13:59:37 +08:00
Nullptr
9ff1e27a7d Unmount everything under ksu loop 2023-02-25 11:00:35 +08:00
Nullptr
6d9cc560cc Bump to 0.5.1 2023-02-24 22:24:45 +08:00
Nullptr
f395cfb490 Fix stupid remount bug 2023-02-24 16:43:53 +08:00
Nullptr
03575edd96 Bump to 0.5.0 2023-02-24 09:34:48 +08:00
Nullptr
915749e59b Never allow multiple root implementation 2023-02-24 09:31:44 +08:00
Nullptr
d08b415577 Require ksud version 2023-02-24 08:57:24 +08:00
Nullptr
f27aed5068 Change memfd name to jit-cache 2023-02-20 16:35:02 +08:00
Nullptr
5365ab1f12 Check correct KernelSU version 2023-02-20 16:28:15 +08:00
Nullptr
b99d042002 Implement GetProcessFlags for KernelSU 2023-02-19 13:29:36 +08:00
Nullptr
57d3d8a0ba Refine unmount 2023-02-18 19:14:15 +08:00
Nullptr
e69aa5c527 Bump to 0.4.1 2023-02-17 21:38:55 +08:00
Nullptr
9b5eb1bac7 Support Magisk out of box 2023-02-17 21:08:19 +08:00
39 changed files with 908 additions and 221 deletions

9
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "loader/src/external/liblsplt"] [submodule "loader/src/external/lsplt"]
path = loader/src/external/liblsplt path = loader/src/external/lsplt
url = https://github.com/LSPosed/LSPlt url = https://github.com/LSPosed/lsplt
[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

View File

@@ -2,25 +2,25 @@
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment. Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD. (Coming soon) Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD.
## Requirements ## Requirements
+ Minimal KernelSU version: 10575 ### General
+ Minimal ksud version: 10616
+ Full SELinux patch support (If non-gki kernel) + No multiple root implementation installed
### KernelSU
+ Minimal KernelSU version: 10654
+ Minimal ksud version: 10670
+ Kernel has full SELinux patch support
### Magisk
+ Minimal version: 25208
+ Original Zygisk turned off
## 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
- [x] [Daemon] Handle zygote death
- [ ] [ Misc ] Support Magisk out of box

View File

@@ -31,8 +31,12 @@ 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.4.0") val verName by extra("v4-0.6.2")
val verCode by extra(gitCommitCount) val verCode by extra(gitCommitCount)
val minKsuVersion by extra(10654)
val minKsudVersion by extra(10670)
val maxKsuVersion by extra(20000)
val minMagiskVersion by extra(25208)
val androidMinSdkVersion by extra(29) val androidMinSdkVersion by extra(29)
val androidTargetSdkVersion by extra(33) val androidTargetSdkVersion by extra(33)

View File

@@ -13,7 +13,7 @@ LOCAL_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := zygiskloader LOCAL_MODULE := zygisk_loader
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/loader)) FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/loader))
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%) LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
@@ -22,7 +22,7 @@ LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY) include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := injector LOCAL_MODULE := zygisk_injector
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/injector)) FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/injector))
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%) LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)

View File

@@ -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);

View File

@@ -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());
} }

View File

@@ -3,14 +3,14 @@ LOCAL_PATH := $(call my-dir)
# liblsplt.a # liblsplt.a
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE:= liblsplt LOCAL_MODULE:= liblsplt
LOCAL_C_INCLUDES := $(LOCAL_PATH)/liblsplt/lsplt/src/main/jni/include LOCAL_C_INCLUDES := $(LOCAL_PATH)/lsplt/lsplt/src/main/jni/include
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden -DLOG_DISABLED
LOCAL_CPPFLAGS := -std=c++20 LOCAL_CPPFLAGS := -std=c++20
LOCAL_STATIC_LIBRARIES := libcxx LOCAL_STATIC_LIBRARIES := libcxx
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
liblsplt/lsplt/src/main/jni/elf_util.cc \ lsplt/lsplt/src/main/jni/elf_util.cc \
liblsplt/lsplt/src/main/jni/lsplt.cc lsplt/lsplt/src/main/jni/lsplt.cc
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
# Header only library # Header only library

View File

@@ -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);

View File

@@ -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);

View File

@@ -26,10 +26,3 @@ void entry(void *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;
}

View File

@@ -0,0 +1,122 @@
#include <sys/sysmacros.h>
#include "files.hpp"
#include "misc.hpp"
using namespace std::string_view_literals;
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn) {
size_t len = 1024;
char *buf = (char *) malloc(len);
char *start;
ssize_t read;
while ((read = getline(&buf, &len, fp)) >= 0) {
start = buf;
if (trim) {
while (read && "\n\r "sv.find(buf[read - 1]) != std::string::npos)
--read;
buf[read] = '\0';
while (*start == ' ')
++start;
}
if (!fn(start))
break;
}
free(buf);
}
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn) {
if (auto fp = open_file(file, "re"))
file_readline(trim, fp.get(), fn);
}
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn) {
file_readline(false, file, fn);
}
std::vector<mount_info> parse_mount_info(const char *pid) {
char buf[PATH_MAX] = {};
snprintf(buf, sizeof(buf), "/proc/%s/mountinfo", pid);
std::vector<mount_info> result;
file_readline(buf, [&result](std::string_view line) -> bool {
int root_start = 0, root_end = 0;
int target_start = 0, target_end = 0;
int vfs_option_start = 0, vfs_option_end = 0;
int type_start = 0, type_end = 0;
int source_start = 0, source_end = 0;
int fs_option_start = 0, fs_option_end = 0;
int optional_start = 0, optional_end = 0;
unsigned int id, parent, maj, min;
sscanf(line.data(),
"%u " // (1) id
"%u " // (2) parent
"%u:%u " // (3) maj:min
"%n%*s%n " // (4) mountroot
"%n%*s%n " // (5) target
"%n%*s%n" // (6) vfs options (fs-independent)
"%n%*[^-]%n - " // (7) optional fields
"%n%*s%n " // (8) FS type
"%n%*s%n " // (9) source
"%n%*s%n", // (10) fs options (fs specific)
&id, &parent, &maj, &min, &root_start, &root_end, &target_start,
&target_end, &vfs_option_start, &vfs_option_end,
&optional_start, &optional_end, &type_start, &type_end,
&source_start, &source_end, &fs_option_start, &fs_option_end);
auto root = line.substr(root_start, root_end - root_start);
auto target = line.substr(target_start, target_end - target_start);
auto vfs_option =
line.substr(vfs_option_start, vfs_option_end - vfs_option_start);
++optional_start;
--optional_end;
auto optional = line.substr(
optional_start,
optional_end - optional_start > 0 ? optional_end - optional_start : 0);
auto type = line.substr(type_start, type_end - type_start);
auto source = line.substr(source_start, source_end - source_start);
auto fs_option =
line.substr(fs_option_start, fs_option_end - fs_option_start);
unsigned int shared = 0;
unsigned int master = 0;
unsigned int propagate_from = 0;
if (auto pos = optional.find("shared:"); pos != std::string_view::npos) {
shared = parse_int(optional.substr(pos + 7));
}
if (auto pos = optional.find("master:"); pos != std::string_view::npos) {
master = parse_int(optional.substr(pos + 7));
}
if (auto pos = optional.find("propagate_from:");
pos != std::string_view::npos) {
propagate_from = parse_int(optional.substr(pos + 15));
}
result.emplace_back(mount_info {
.id = id,
.parent = parent,
.device = static_cast<dev_t>(makedev(maj, min)),
.root {root},
.target {target},
.vfs_option {vfs_option},
.optional {
.shared = shared,
.master = master,
.propagate_from = propagate_from,
},
.type {type},
.source {source},
.fs_option {fs_option},
});
return true;
});
return result;
}
sDIR make_dir(DIR *dp) {
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
}
sFILE make_file(FILE *fp) {
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; });
}

View File

@@ -0,0 +1,56 @@
#include <dirent.h>
#include <functional>
#include <string>
#include <vector>
struct mount_info {
unsigned int id;
unsigned int parent;
dev_t device;
std::string root;
std::string target;
std::string vfs_option;
struct {
unsigned int shared;
unsigned int master;
unsigned int propagate_from;
} optional;
std::string type;
std::string source;
std::string fs_option;
};
void file_readline(bool trim, FILE *fp, const std::function<bool(std::string_view)> &fn);
void file_readline(bool trim, const char *file, const std::function<bool(std::string_view)> &fn);
void file_readline(const char *file, const std::function<bool(std::string_view)> &fn);
std::vector<mount_info> parse_mount_info(const char *pid);
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>;
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>;
sDIR make_dir(DIR *dp);
sFILE make_file(FILE *fp);
static inline sDIR open_dir(const char *path) {
return make_dir(opendir(path));
}
static inline sDIR xopen_dir(const char *path) {
return make_dir(opendir(path));
}
static inline sDIR xopen_dir(int dirfd) {
return make_dir(fdopendir(dirfd));
}
static inline sFILE open_file(const char *path, const char *mode) {
return make_file(fopen(path, mode));
}
static inline sFILE xopen_file(const char *path, const char *mode) {
return make_file(fopen(path, mode));
}
static inline sFILE xopen_file(int fd, const char *mode) {
return make_file(fdopen(fd, mode));
}

View File

@@ -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;
});
}

View File

@@ -17,7 +17,7 @@
#include "zygisk.hpp" #include "zygisk.hpp"
#include "memory.hpp" #include "memory.hpp"
#include "module.hpp" #include "module.hpp"
#include "misc.hpp" #include "files.hpp"
using namespace std; using namespace std;
using jni_hook::hash_map; using jni_hook::hash_map;
@@ -219,7 +219,11 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
// Simply avoid doing any unmounts for SysUI to avoid potential issues. // Simply avoid doing any unmounts for SysUI to avoid potential issues.
(g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) { (g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) {
if (g_ctx->flags[DO_REVERT_UNMOUNT]) { if (g_ctx->flags[DO_REVERT_UNMOUNT]) {
revert_unmount(); if (g_ctx->info_flags & PROCESS_ROOT_IS_KSU) {
revert_unmount_ksu();
} else if (g_ctx->info_flags & PROCESS_ROOT_IS_MAGISK) {
revert_unmount_magisk();
}
} }
/* Zygisksu changed: No umount app_process */ /* Zygisksu changed: No umount app_process */
@@ -566,15 +570,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);

View File

@@ -23,22 +23,27 @@ int parse_int(std::string_view s) {
return val; return val;
} }
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) {
auto fp = sFILE(setmntent(file, "re"), endmntent); std::list<std::string> ret;
if (fp) { size_t pos = 0;
mntent mentry{}; while (pos < s.size()) {
char buf[PATH_MAX]; auto next = s.find(delimiter, pos);
while (getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) { if (next == std::string_view::npos) {
if (!fn(&mentry)) ret.emplace_back(s.substr(pos));
break; break;
} }
ret.emplace_back(s.substr(pos, next - pos));
pos = next + delimiter.size();
} }
return ret;
} }
sDIR make_dir(DIR *dp) { std::string join_str(const std::list<std::string>& list, std::string_view delimiter) {
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; }); std::string ret;
} for (auto& s : list) {
if (!ret.empty())
sFILE make_file(FILE *fp) { ret += delimiter;
return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; }); ret += s;
}
return ret;
} }

View File

@@ -1,11 +1,9 @@
#pragma once #pragma once
#include <dirent.h> #include <list>
#include <functional>
#include <memory> #include <memory>
#include <mntent.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <string>
#include <string_view> #include <string_view>
#include "logging.h" #include "logging.h"
@@ -34,6 +32,10 @@ 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;
}
template<typename T, typename Impl> template<typename T, typename Impl>
class stateless_allocator { class stateless_allocator {
public: public:
@@ -49,33 +51,23 @@ public:
bool operator!=(const stateless_allocator&) { return false; } bool operator!=(const stateless_allocator&) { return false; }
}; };
using sFILE = std::unique_ptr<FILE, decltype(&fclose)>; template <typename T>
using sDIR = std::unique_ptr<DIR, decltype(&closedir)>; class reversed_container {
sDIR make_dir(DIR *dp); public:
sFILE make_file(FILE *fp); reversed_container(T &base) : base(base) {}
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
decltype(std::declval<T>().rend()) end() { return base.rend(); }
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
private:
T &base;
};
static inline sDIR open_dir(const char *path) { template <typename T>
return make_dir(opendir(path)); reversed_container<T> reversed(T &base) {
} return reversed_container<T>(base);
static inline sDIR xopen_dir(const char *path) {
return make_dir(opendir(path));
}
static inline sDIR xopen_dir(int dirfd) {
return make_dir(fdopendir(dirfd));
}
static inline sFILE open_file(const char *path, const char *mode) {
return make_file(fopen(path, mode));
}
static inline sFILE xopen_file(const char *path, const char *mode) {
return make_file(fopen(path, mode));
}
static inline sFILE xopen_file(int fd, const char *mode) {
return make_file(fdopen(fd, mode));
} }
template<class T> template<class T>
@@ -95,7 +87,9 @@ struct StringCmp {
*/ */
int parse_int(std::string_view s); int parse_int(std::string_view s);
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn); std::list<std::string> split_str(std::string_view s, std::string_view delimiter);
std::string join_str(const std::list<std::string>& list, std::string_view delimiter);
template <typename T> template <typename T>
static inline T align_to(T v, int a) { static inline T align_to(T v, int a) {

View File

@@ -111,12 +111,11 @@ 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_ROOT_IS_KSU = (1u << 29),
DENYLIST_ENFORCING = (1u << 30), PROCESS_ROOT_IS_MAGISK = (1u << 30),
PROCESS_IS_MAGISK_APP = (1u << 31), PROCESS_IS_SYS_UI = (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 {

View File

@@ -0,0 +1,122 @@
#include <mntent.h>
#include <sys/mount.h>
#include "files.hpp"
#include "logging.h"
#include "misc.hpp"
#include "zygisk.hpp"
using namespace std::string_view_literals;
namespace {
constexpr auto MODULE_DIR = "/data/adb/modules";
struct overlay_backup {
std::string target;
std::string vfs_option;
std::string fs_option;
};
void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1) {
LOGD("Unmounted (%s)", mountpoint);
} else {
PLOGE("Unmount (%s)", mountpoint);
}
}
}
#define PARSE_OPT(name, flag) \
if (opt == (name)) { \
flags |= (flag); \
return true; \
}
void revert_unmount_ksu() {
std::string ksu_loop;
std::vector<std::string> targets;
std::list<overlay_backup> backups;
// Unmount ksu module dir last
targets.emplace_back(MODULE_DIR);
for (auto& info: parse_mount_info("self")) {
if (info.target == MODULE_DIR) {
ksu_loop = info.source;
continue;
}
// Unmount everything on /data/adb except ksu module dir
if (info.target.starts_with("/data/adb")) {
targets.emplace_back(info.target);
}
// Unmount ksu overlays
if (info.type == "overlay") {
if (str_contains(info.fs_option, MODULE_DIR)) {
targets.emplace_back(info.target);
} else {
auto backup = overlay_backup{
.target = info.target,
.vfs_option = info.vfs_option,
.fs_option = info.fs_option,
};
backups.emplace_back(backup);
}
}
}
for (auto& info: parse_mount_info("self")) {
// Unmount everything from ksu loop except ksu module dir
if (info.source == ksu_loop && info.target != MODULE_DIR) {
targets.emplace_back(info.target);
}
}
// Do unmount
for (auto& s: reversed(targets)) {
lazy_unmount(s.data());
}
// Affirm unmounted system overlays
for (auto& info: parse_mount_info("self")) {
if (info.type == "overlay") {
backups.remove_if([&](overlay_backup& mnt) {
return mnt.target == info.target && mnt.fs_option == info.fs_option;
});
}
}
// Restore system overlays
for (auto& mnt: backups) {
auto opts = split_str(mnt.vfs_option, ",");
opts.splice(opts.end(), split_str(mnt.fs_option, ","));
unsigned long flags = 0;
opts.remove_if([&](auto& opt) {
PARSE_OPT(MNTOPT_RO, MS_RDONLY)
PARSE_OPT(MNTOPT_NOSUID, MS_NOSUID)
PARSE_OPT("relatime", MS_RELATIME)
return false;
});
auto mnt_data = join_str(opts, ",");
if (mount("overlay", mnt.target.data(), "overlay", flags, mnt_data.data()) != -1) {
LOGD("Remounted (%s)", mnt.target.data());
} else {
PLOGE("Remount (%s, %s)", mnt.target.data(), mnt.fs_option.data());
}
}
}
void revert_unmount_magisk() {
std::vector<std::string> targets;
// Unmount dummy skeletons and MAGISKTMP
// since mirror nodes are always mounted under skeleton, we don't have to specifically unmount
for (auto& info: parse_mount_info("self")) {
if (info.source == "magisk" || info.source == "worker" || // magisktmp tmpfs
info.root.starts_with("/adb/modules")) { // bind mount from data partition
targets.push_back(info.target);
}
}
for (auto& s: reversed(targets)) {
lazy_unmount(s.data());
}
}

View File

@@ -8,4 +8,7 @@ extern void *self_handle;
void hook_functions(); void hook_functions();
void revert_unmount(); void revert_unmount_ksu();
void revert_unmount_magisk();

View File

@@ -13,7 +13,7 @@ uint8_t NativeBridgeItf[sizeof(NativeBridgeCallbacks<__ANDROID_API_R__>) * 2]{0}
namespace { namespace {
constexpr auto kZygoteProcesses = {"zygote", "zygote32", "zygote64", "usap32", "usap64"}; constexpr auto kZygoteProcesses = {"zygote", "zygote32", "zygote64", "usap32", "usap64"};
constexpr auto kInjector = "/system/" LP_SELECT("lib", "lib64") "/libinjector.so"; constexpr auto kInjector = "/system/" LP_SELECT("lib", "lib64") "/libzygisk_injector.so";
void* sOriginalBridge = nullptr; void* sOriginalBridge = nullptr;
} }
@@ -70,13 +70,13 @@ void Constructor() {
LOGI("Load original native bridge: %s", native_bridge.data()); LOGI("Load original native bridge: %s", native_bridge.data());
sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW); sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW);
if (sOriginalBridge == nullptr) { if (sOriginalBridge == nullptr) {
LOGE("dlopen failed: %s", dlerror()); LOGE("%s", dlerror());
break; break;
} }
auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf"); auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf");
if (original_native_bridge_itf == nullptr) { if (original_native_bridge_itf == nullptr) {
LOGE("dlsym failed: %s", dlerror()); LOGE("%s", dlerror());
break; break;
} }

View File

@@ -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
@@ -34,7 +38,7 @@ androidComponents.onVariants { variant ->
into(moduleDir) into(moduleDir)
from("${rootProject.projectDir}/README.md") from("${rootProject.projectDir}/README.md")
from("$projectDir/src") { from("$projectDir/src") {
exclude("module.prop", "customize.sh", "daemon.sh") exclude("module.prop", "customize.sh", "service.sh")
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf")) filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
} }
from("$projectDir/src") { from("$projectDir/src") {
@@ -47,9 +51,13 @@ androidComponents.onVariants { variant ->
) )
} }
from("$projectDir/src") { from("$projectDir/src") {
include("customize.sh", "daemon.sh") include("customize.sh", "service.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") {

View File

@@ -2,28 +2,56 @@
SKIPUNZIP=1 SKIPUNZIP=1
DEBUG=@DEBUG@ DEBUG=@DEBUG@
MIN_KSU_VERSION=@MIN_KSU_VERSION@
MIN_KSUD_VERSION=@MIN_KSUD_VERSION@
MAX_KSU_VERSION=@MAX_KSU_VERSION@
MIN_MAGISK_VERSION=@MIN_MAGISK_VERSION@
if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then if [ "$BOOTMODE" ] && [ "$KSU" ]; then
ui_print "- Installing from KernelSU app" ui_print "- 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_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
if [ "$KSU_KERNEL_VER_CODE" -lt 10575 ]; then
ui_print "*********************************************************"
ui_print "! KernelSU version is too old!"
ui_print "! Please update KernelSU to latest version"
abort "*********************************************************"
fi
# check android # check android
if [ "$API" -lt 29 ]; then if [ "$API" -lt 29 ]; then
ui_print "! Unsupported sdk: $API" ui_print "! Unsupported sdk: $API"
@@ -52,19 +80,25 @@ 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 ! check_sepolicy "$TMPDIR/sepolicy.rule"; then ui_print "- Checking SELinux patches"
ui_print "*********************************************************" if [ "$(getprop ro.product.first_api_level)" -lt 31 ]; then
ui_print "! Unable to apply SELinux patches!" echo "allow zygote appdomain_tmpfs file *" >> "$TMPDIR/sepolicy.rule"
ui_print "! Your kernel may not support SELinux patch fully" echo "allow zygote appdomain_tmpfs dir *" >> "$TMPDIR/sepolicy.rule"
abort "*********************************************************" fi
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
ui_print "*********************************************************"
ui_print "! Unable to apply SELinux patches!"
ui_print "! Your kernel may not support SELinux patch fully"
abort "*********************************************************"
fi
fi fi
ui_print "- Extracting module files" ui_print "- Extracting module files"
extract "$ZIPFILE" 'daemon.sh' "$MODPATH"
extract "$ZIPFILE" 'module.prop' "$MODPATH" extract "$ZIPFILE" 'module.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH" extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH" extract "$ZIPFILE" 'service.sh' "$MODPATH"
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true
@@ -79,8 +113,8 @@ if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
ui_print "- Extracting x86 libraries" ui_print "- Extracting x86 libraries"
extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
extract "$ZIPFILE" 'lib/x86/libinjector.so' "$MODPATH/system/lib" true extract "$ZIPFILE" 'lib/x86/libzygisk_injector.so' "$MODPATH/system/lib" true
extract "$ZIPFILE" 'lib/x86/libzygiskloader.so' "$MODPATH/system/lib" true extract "$ZIPFILE" 'lib/x86/libzygisk_loader.so' "$MODPATH/system/lib" true
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd" ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
fi fi
@@ -88,8 +122,8 @@ if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
ui_print "- Extracting x64 libraries" ui_print "- Extracting x64 libraries"
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64" mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
extract "$ZIPFILE" 'lib/x86_64/libinjector.so' "$MODPATH/system/lib64" true extract "$ZIPFILE" 'lib/x86_64/libzygisk_injector.so' "$MODPATH/system/lib64" true
extract "$ZIPFILE" 'lib/x86_64/libzygiskloader.so' "$MODPATH/system/lib64" true extract "$ZIPFILE" 'lib/x86_64/libzygisk_loader.so' "$MODPATH/system/lib64" true
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd" ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
fi fi
else else
@@ -97,8 +131,8 @@ else
ui_print "- Extracting arm libraries" ui_print "- Extracting arm libraries"
extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32" mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
extract "$ZIPFILE" 'lib/armeabi-v7a/libinjector.so' "$MODPATH/system/lib" true extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_injector.so' "$MODPATH/system/lib" true
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygiskloader.so' "$MODPATH/system/lib" true extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_loader.so' "$MODPATH/system/lib" true
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd" ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
fi fi
@@ -106,8 +140,8 @@ else
ui_print "- Extracting arm64 libraries" ui_print "- Extracting arm64 libraries"
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64" mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
extract "$ZIPFILE" 'lib/arm64-v8a/libinjector.so' "$MODPATH/system/lib64" true extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_injector.so' "$MODPATH/system/lib64" true
extract "$ZIPFILE" 'lib/arm64-v8a/libzygiskloader.so' "$MODPATH/system/lib64" true extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_loader.so' "$MODPATH/system/lib64" true
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd" ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
fi fi
fi fi
@@ -117,13 +151,13 @@ if [ $DEBUG = false ]; then
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18) SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
if [ "$HAS32BIT" = true ]; then if [ "$HAS32BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32" 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/libzygisk_injector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so" sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygisk_loader.so"
fi fi
if [ "$HAS64BIT" = true ]; then if [ "$HAS64BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64" 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/libzygisk_injector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so" sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygisk_loader.so"
fi fi
fi fi

View File

@@ -1,11 +0,0 @@
#!/system/bin/sh
DEBUG=@DEBUG@
# shellcheck disable=SC2155
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
log -p i -t "zygisksu" "Start watchdog"
resetprop ro.dalvik.vm.native.bridge libzygiskloader.so
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1

View File

@@ -1,8 +1,23 @@
#!/system/bin/sh #!/system/bin/sh
MODDIR=${0%/*} MODDIR=${0%/*}
if [ "$ZYGISK_ENABLED" ]; then
exit 0
fi
cd $MODDIR cd "$MODDIR"
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge) getprop ro.dalvik.vm.native.bridge > /dev/.native_bridge
resetprop ro.dalvik.vm.native.bridge libzygisk_loader.so
unshare -m sh -c "./daemon.sh $@&" if [ "$(which magisk)" ]; then
for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/post-fs-data.sh" ]; then
cd "$file"
log -p i -t "zygisksu" "Manually trigger post-fs-data.sh for $file"
sh "$(realpath ./post-fs-data.sh)"
cd "$MODDIR"
fi
fi
done
fi

View File

@@ -10,3 +10,7 @@ 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 adb_data_file dir search
allow zygote mnt_vendor_file dir search
allow zygote system_file dir mounton
allow zygote labeledfs filesystem mount

29
module/src/service.sh Normal file
View File

@@ -0,0 +1,29 @@
#!/system/bin/sh
DEBUG=@DEBUG@
MODDIR=${0%/*}
if [ "$ZYGISK_ENABLED" ]; then
exit 0
fi
cd "$MODDIR"
export NATIVE_BRIDGE=$(cat /dev/.native_bridge)
rm /dev/.native_bridge
if [ "$(which magisk)" ]; then
for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/service.sh" ]; then
cd "$file"
log -p i -t "zygisksu" "Manually trigger service.sh for $file"
sh "$(realpath ./service.sh)"
cd "$MODDIR"
fi
fi
done
fi
log -p i -t "zygisksu" "Start watchdog"
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1

View File

@@ -6,19 +6,21 @@ edition = "2021"
rust-version = "1.67" rust-version = "1.67"
[dependencies] [dependencies]
android_logger = "0.12.0" android_logger = "0.13.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" 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" } binder = { git = "https://github.com/Kernel-SU/binder_rs" }
[profile.release] [profile.release]
strip = true strip = true

View File

@@ -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
@@ -23,5 +26,8 @@ cargo {
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path) 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)
} }
} }

View File

@@ -1,36 +1,40 @@
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;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info; pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
#[cfg(target_pointer_width = "64")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp64 };
}
#[cfg(target_pointer_width = "32")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp32 };
}
pub const PROP_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 PROP_CTL_RESTART: &str = "ctl.restart";
pub const ZYGISK_LOADER: &str = "libzygiskloader.so"; pub const ZYGISK_LOADER: &str = "libzygisk_loader.so";
pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder"; pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder";
pub const PATH_MODULE_DIR: &str = ".."; pub const PATH_MODULES_DIR: &str = "..";
pub const PATH_MODULE_PROP: &str = "module.prop";
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32"; pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64"; pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
pub const PATH_TMP_DIR: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER);
pub const PATH_TMP_PROP: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER, "/module.prop");
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
pub const STATUS_ROOT_IMPL_NONE: &str = "❌ Unknown root implementation";
pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too old";
pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version";
pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed";
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
@@ -38,7 +42,15 @@ 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_ROOT_IS_KSU: u32 = 1 << 29;
pub const PROCESS_ROOT_IS_MAGISK: u32 = 1 << 30;
pub const PROCESS_IS_SYSUI: u32 = 1 << 31;

View File

@@ -76,7 +76,7 @@ pub unsafe fn dlopen(path: &str, flags: i32) -> Result<*mut c_void> {
let result = android_dlopen_ext(filename, flags, &info); let result = android_dlopen_ext(filename, flags, &info);
if result.is_null() { if result.is_null() {
let e = std::ffi::CStr::from_ptr(libc::dlerror()).to_string_lossy(); let e = std::ffi::CStr::from_ptr(libc::dlerror()).to_string_lossy();
bail!("dlopen failed: {}", e); bail!(e);
} }
Ok(result) Ok(result)
} }

View File

@@ -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(),
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());
} }
} }

View File

@@ -0,0 +1,41 @@
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 enum Version {
Supported,
TooOld,
Abnormal,
}
pub fn get_kernel_su() -> Option<Version> {
let mut version = 0;
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
match version {
0 => None,
MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
1..=MIN_KSU_VERSION => Some(Version::TooOld),
_ => Some(Version::Abnormal)
}
}
pub fn uid_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)
}

View File

@@ -0,0 +1,46 @@
use std::process::{Command, Stdio};
use crate::constants::MIN_MAGISK_VERSION;
pub enum Version {
Supported,
TooOld,
}
pub fn get_magisk() -> Option<Version> {
let version: Option<i32> = Command::new("magisk")
.arg("-V")
.stdout(Stdio::piped())
.spawn().ok()
.and_then(|child| child.wait_with_output().ok())
.and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|output| output.trim().parse().ok());
version.map(|version| {
if version >= MIN_MAGISK_VERSION {
Version::Supported
} else {
Version::TooOld
}
})
}
pub fn uid_on_allowlist(uid: i32) -> bool {
let output: Option<String> = Command::new("magisk")
.arg("--sqlite")
.arg("select uid from policies where policy=2")
.stdout(Stdio::piped())
.spawn().ok()
.and_then(|child| child.wait_with_output().ok())
.and_then(|output| String::from_utf8(output.stdout).ok());
let lines = match &output {
Some(output) => output.lines(),
None => return false,
};
lines.into_iter().any(|line| {
line.trim().strip_prefix("uid=").and_then(|uid| uid.parse().ok()) == Some(uid)
})
}
pub fn uid_on_denylist(uid: i32) -> bool {
// TODO: uid_on_denylist
return false;
}

View File

@@ -0,0 +1,61 @@
mod kernelsu;
mod magisk;
pub enum RootImpl {
None,
TooOld,
Abnormal,
Multiple,
KernelSU,
Magisk,
}
// FIXME: OnceCell bugs on 32 bit
static mut ROOT_IMPL: RootImpl = RootImpl::None;
pub fn setup() {
let ksu_version = kernelsu::get_kernel_su();
let magisk_version = magisk::get_magisk();
let impl_ = match (ksu_version, magisk_version) {
(None, None) => RootImpl::None,
(Some(_), Some(_)) => RootImpl::Multiple,
(Some(ksu_version), None) => {
match ksu_version {
kernelsu::Version::Supported => RootImpl::KernelSU,
kernelsu::Version::TooOld => RootImpl::TooOld,
kernelsu::Version::Abnormal => RootImpl::Abnormal,
}
}
(None, Some(magisk_version)) => {
match magisk_version {
magisk::Version::Supported => RootImpl::Magisk,
magisk::Version::TooOld => RootImpl::TooOld,
}
}
};
unsafe { ROOT_IMPL = impl_; }
}
pub fn get_impl() -> &'static RootImpl {
unsafe { &ROOT_IMPL }
}
// FIXME: Without #[inline(never)], this function will lag forever
#[inline(never)]
pub fn uid_on_allowlist(uid: i32) -> bool {
match get_impl() {
RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid),
RootImpl::Magisk => magisk::uid_on_allowlist(uid),
_ => unreachable!(),
}
}
#[inline(never)]
pub fn uid_on_denylist(uid: i32) -> bool {
match get_impl() {
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
RootImpl::Magisk => magisk::uid_on_denylist(uid),
_ => unreachable!(),
}
}

View File

@@ -1,11 +1,23 @@
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};
use std::ffi::c_char;
use std::os::fd::FromRawFd; use std::os::fd::FromRawFd;
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr}; use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
#[cfg(target_pointer_width = "64")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp64 };
}
#[cfg(target_pointer_width = "32")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp32 };
}
pub fn random_string() -> String { pub fn random_string() -> String {
Alphanumeric.sample_string(&mut rand::thread_rng(), 8) Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
} }
@@ -26,6 +38,24 @@ pub fn get_native_bridge() -> String {
std::env::var("NATIVE_BRIDGE").unwrap_or_default() std::env::var("NATIVE_BRIDGE").unwrap_or_default()
} }
pub fn log_raw(level: i32, tag: &str, message: &str) -> Result<()> {
let tag = std::ffi::CString::new(tag)?;
let message = std::ffi::CString::new(message)?;
unsafe {
__android_log_print(level as i32, tag.as_ptr(), message.as_ptr());
}
Ok(())
}
pub fn get_property(name: &str) -> Result<String> {
let name = std::ffi::CString::new(name)?;
let mut buf = vec![0u8; 92];
unsafe {
__system_property_get(name.as_ptr(), buf.as_mut_ptr() as *mut c_char);
}
Ok(String::from_utf8(buf)?)
}
pub fn set_property(name: &str, value: &str) -> Result<()> { pub fn set_property(name: &str, value: &str) -> Result<()> {
Command::new("resetprop") Command::new("resetprop")
.arg(name) .arg(name)
@@ -40,6 +70,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<()>;
} }
@@ -75,6 +106,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(())
@@ -96,3 +132,8 @@ pub fn abstract_namespace_socket(name: &str) -> Result<UnixListener> {
let listener = unsafe { UnixListener::from_raw_fd(socket) }; let listener = unsafe { UnixListener::from_raw_fd(socket) };
Ok(listener) Ok(listener)
} }
extern "C" {
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
}

View File

@@ -1,21 +1,35 @@
use crate::{constants, utils}; use crate::{constants, root_impl, utils};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use nix::unistd::{getgid, getuid, Pid}; use nix::unistd::{getgid, getuid, Pid};
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, io, thread};
use std::ffi::CString;
use std::io::{BufRead, Write};
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
use std::time::Duration; use std::time::Duration;
use binder::IBinder; use binder::IBinder;
use nix::errno::Errno;
use nix::libc;
use nix::sys::signal::{kill, Signal}; use nix::sys::signal::{kill, Signal};
use once_cell::sync::OnceCell;
static mut LOCK: Option<UnixListener> = None; static LOCK: OnceCell<UnixListener> = OnceCell::new();
static PROP_SECTIONS: OnceCell<[String; 2]> = OnceCell::new();
pub fn entry() -> Result<()> { pub fn entry() -> Result<()> {
log::info!("Start zygisksu watchdog"); log::info!("Start zygisksu watchdog");
check_permission()?; check_permission()?;
ensure_single_instance()?; ensure_single_instance()?;
spawn_daemon() mount_prop()?;
if check_and_set_hint()? == false {
log::warn!("Requirements not met, exiting");
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
return Ok(());
}
let end = spawn_daemon();
set_prop_hint(constants::STATUS_CRASHED)?;
end
} }
fn check_permission() -> Result<()> { fn check_permission() -> Result<()> {
@@ -43,12 +57,86 @@ fn ensure_single_instance() -> Result<()> {
log::info!("Ensure single instance"); log::info!("Ensure single instance");
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER; let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
match utils::abstract_namespace_socket(&name) { match utils::abstract_namespace_socket(&name) {
Ok(socket) => unsafe { LOCK = Some(socket) }, Ok(socket) => { let _ = LOCK.set(socket); }
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?") Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
} }
Ok(()) Ok(())
} }
fn mount_prop() -> Result<()> {
let module_prop = if let root_impl::RootImpl::Magisk = root_impl::get_impl() {
let magisk_path = Command::new("magisk").arg("--path").output()?;
let mut magisk_path = String::from_utf8(magisk_path.stdout)?;
magisk_path.pop(); // Removing '\n'
let cwd = std::env::current_dir()?;
let dir = cwd.file_name().unwrap().to_string_lossy();
format!("{magisk_path}/.magisk/modules/{dir}/{}", constants::PATH_MODULE_PROP)
} else {
constants::PATH_MODULE_PROP.to_string()
};
log::info!("Mount {module_prop}");
let module_prop_file = fs::File::open(&module_prop)?;
let mut section = 0;
let mut sections: [String; 2] = [String::new(), String::new()];
let lines = io::BufReader::new(module_prop_file).lines();
for line in lines {
let line = line?;
if line.starts_with("description=") {
sections[0].push_str("description=");
sections[1].push_str(line.trim_start_matches("description="));
sections[1].push('\n');
section = 1;
} else {
sections[section].push_str(&line);
sections[section].push('\n');
}
}
let _ = PROP_SECTIONS.set(sections);
fs::create_dir(constants::PATH_TMP_DIR)?;
fs::File::create(constants::PATH_TMP_PROP)?;
// FIXME: sys_mount cannot be compiled on 32 bit
unsafe {
let r = libc::mount(
CString::new(constants::PATH_TMP_PROP)?.as_ptr(),
CString::new(module_prop)?.as_ptr(),
std::ptr::null(),
libc::MS_BIND,
std::ptr::null(),
);
Errno::result(r)?;
}
Ok(())
}
fn set_prop_hint(hint: &str) -> Result<()> {
let mut file = fs::File::create(constants::PATH_TMP_PROP)?;
let sections = PROP_SECTIONS.get().unwrap();
file.write_all(sections[0].as_bytes())?;
file.write_all(b"[")?;
file.write_all(hint.as_bytes())?;
file.write_all(b"] ")?;
file.write_all(sections[1].as_bytes())?;
Ok(())
}
fn check_and_set_hint() -> Result<bool> {
let root_impl = root_impl::get_impl();
match root_impl {
root_impl::RootImpl::None => set_prop_hint(constants::STATUS_ROOT_IMPL_NONE)?,
root_impl::RootImpl::TooOld => set_prop_hint(constants::STATUS_ROOT_IMPL_TOO_OLD)?,
root_impl::RootImpl::Abnormal => set_prop_hint(constants::STATUS_ROOT_IMPL_ABNORMAL)?,
root_impl::RootImpl::Multiple => set_prop_hint(constants::STATUS_ROOT_IMPL_MULTIPLE)?,
_ => {
set_prop_hint(constants::STATUS_LOADED)?;
return Ok(true);
}
}
Ok(false)
}
fn spawn_daemon() -> Result<()> { fn spawn_daemon() -> Result<()> {
let mut lives = 5; let mut lives = 5;
loop { loop {
@@ -99,7 +187,6 @@ fn spawn_daemon() -> Result<()> {
log::error!("Restarting zygote..."); log::error!("Restarting zygote...");
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?; utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
utils::set_property(constants::PROP_SVC_ZYGOTE, "restart")?; utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
thread::sleep(Duration::from_secs(2));
} }
} }

View File

@@ -1,6 +1,6 @@
use crate::constants::DaemonSocketAction; use crate::constants::DaemonSocketAction;
use crate::utils::UnixStreamExt; use crate::utils::UnixStreamExt;
use crate::{constants, lp_select, 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::{
@@ -10,7 +10,6 @@ use nix::{
use passfd::FdPassingExt; use passfd::FdPassingExt;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::ffi::c_char;
use std::fs; use std::fs;
use std::os::unix::{ use std::os::unix::{
net::{UnixListener, UnixStream}, net::{UnixListener, UnixStream},
@@ -64,20 +63,19 @@ pub fn entry() -> Result<()> {
} }
fn get_arch() -> Result<&'static str> { fn get_arch() -> Result<&'static str> {
let output = Command::new("getprop").arg("ro.product.cpu.abi").output()?; let system_arch = utils::get_property("ro.product.cpu.abi")?;
let system_arch = String::from_utf8(output.stdout)?;
if system_arch.contains("arm") { if system_arch.contains("arm") {
return Ok(lp_select!("armeabi-v7a", "arm64-v8a")) return Ok(lp_select!("armeabi-v7a", "arm64-v8a"));
} }
if system_arch.contains("x86") { if system_arch.contains("x86") {
return Ok(lp_select!("x86", "x86_64")) return Ok(lp_select!("x86", "x86_64"));
} }
bail!("Unsupported system architecture: {}", system_arch); 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_MODULE_DIR) { let dir = match fs::read_dir(constants::PATH_MODULES_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);
@@ -93,7 +91,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}");
@@ -116,10 +114,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();
@@ -182,17 +179,30 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
Err(_) => break, Err(_) => break,
}; };
let tag = stream.read_string()?; let tag = stream.read_string()?;
let tag = std::ffi::CString::new(tag)?;
let message = stream.read_string()?; let message = stream.read_string()?;
let message = std::ffi::CString::new(message)?; utils::log_raw(level as i32, &tag, &message)?;
unsafe {
__android_log_print(level as i32, tag.as_ptr(), message.as_ptr());
}
} }
} }
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;
}
match root_impl::get_impl() {
root_impl::RootImpl::KernelSU => flags |= constants::PROCESS_ROOT_IS_KSU,
root_impl::RootImpl::Magisk => flags |= constants::PROCESS_ROOT_IS_MAGISK,
_ => ()
}
// 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() {
@@ -221,14 +231,10 @@ 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_MODULE_DIR, module.name); let dir = format!("{}/{}", constants::PATH_MODULES_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())?;
} }
} }
Ok(()) Ok(())
} }
extern "C" {
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
}