diff --git a/zygiskd/.cargo/config.toml b/zygiskd-old/.cargo/config.toml similarity index 100% rename from zygiskd/.cargo/config.toml rename to zygiskd-old/.cargo/config.toml diff --git a/zygiskd/Cargo.toml b/zygiskd-old/Cargo.toml similarity index 100% rename from zygiskd/Cargo.toml rename to zygiskd-old/Cargo.toml diff --git a/zygiskd/build.gradle.kts b/zygiskd-old/build.gradle.kts similarity index 100% rename from zygiskd/build.gradle.kts rename to zygiskd-old/build.gradle.kts diff --git a/zygiskd/src/companion.rs b/zygiskd-old/src/companion.rs similarity index 100% rename from zygiskd/src/companion.rs rename to zygiskd-old/src/companion.rs diff --git a/zygiskd/src/constants.rs b/zygiskd-old/src/constants.rs similarity index 100% rename from zygiskd/src/constants.rs rename to zygiskd-old/src/constants.rs diff --git a/zygiskd/src/dl.rs b/zygiskd-old/src/dl.rs similarity index 100% rename from zygiskd/src/dl.rs rename to zygiskd-old/src/dl.rs diff --git a/zygiskd/src/main.rs b/zygiskd-old/src/main.rs similarity index 100% rename from zygiskd/src/main.rs rename to zygiskd-old/src/main.rs diff --git a/zygiskd/src/root_impl/apatch.rs b/zygiskd-old/src/root_impl/apatch.rs similarity index 100% rename from zygiskd/src/root_impl/apatch.rs rename to zygiskd-old/src/root_impl/apatch.rs diff --git a/zygiskd/src/root_impl/kernelsu.rs b/zygiskd-old/src/root_impl/kernelsu.rs similarity index 100% rename from zygiskd/src/root_impl/kernelsu.rs rename to zygiskd-old/src/root_impl/kernelsu.rs diff --git a/zygiskd/src/root_impl/magisk.rs b/zygiskd-old/src/root_impl/magisk.rs similarity index 100% rename from zygiskd/src/root_impl/magisk.rs rename to zygiskd-old/src/root_impl/magisk.rs diff --git a/zygiskd/src/root_impl/mod.rs b/zygiskd-old/src/root_impl/mod.rs similarity index 100% rename from zygiskd/src/root_impl/mod.rs rename to zygiskd-old/src/root_impl/mod.rs diff --git a/zygiskd/src/utils.rs b/zygiskd-old/src/utils.rs similarity index 100% rename from zygiskd/src/utils.rs rename to zygiskd-old/src/utils.rs diff --git a/zygiskd/src/zygiskd.rs b/zygiskd-old/src/zygiskd.rs similarity index 100% rename from zygiskd/src/zygiskd.rs rename to zygiskd-old/src/zygiskd.rs diff --git a/zygiskd/LICENSE b/zygiskd/LICENSE new file mode 100644 index 0000000..bf86410 --- /dev/null +++ b/zygiskd/LICENSE @@ -0,0 +1,24 @@ +BSD 2-Clause License + +Copyright (c) 2024, The PerformanC Organization + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/zygiskd/Makefile b/zygiskd/Makefile new file mode 100644 index 0000000..da3bc8a --- /dev/null +++ b/zygiskd/Makefile @@ -0,0 +1,19 @@ +CC := ~/Android/Sdk/ndk/27.0.11902837/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android34-clang + +FILES = root_impl/common.c \ + root_impl/kernelsu.c \ + main.c \ + utils.c \ + +CFLAGS = -D_GNU_SOURCE -Wall -Wextra -Werror -O3 -Iroot_impl -llog + +all: CFLAGS += -DDEBUG=0 +all: + $(CC) $(CFLAGS) $(FILES) -o zygiskd + +debug: CFLAGS += -DDEBUG=1 +debug: + $(CC) $(CFLAGS) $(FILES) -o zygiskd + +clean: + rm -f zygiskd \ No newline at end of file diff --git a/zygiskd/companion.c b/zygiskd/companion.c new file mode 100644 index 0000000..df8387e --- /dev/null +++ b/zygiskd/companion.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dl.h" +#include "utils.h" + +typedef void (*ZygiskCompanionEntryFn)(u_int32_t); + +ZygiskCompanionEntryFn load_module(u_int32_t fd) { + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/proc/self/fd/%d", fd); + + void *handle = android_dlopen(path, RTLD_NOW); + void *entry = dlsym(handle, "zygisk_companion_entry"); + if (entry == NULL) return NULL; + + return (ZygiskCompanionEntryFn)entry; +} + +void *ExecuteNew(void *arg) { + u_int32_t fd = (u_int32_t)arg; + + struct stat st0; + if (fstat(fd) == -1) { + printf("Failed to stat client fd\n"); + + exit(0); + } + entry(fd); + + // Only close client if it is the same file so we don't + // accidentally close a re-used file descriptor. + // This check is required because the module companion + // handler could've closed the file descriptor already. + struct stat st1; + if (fstat(fd) == -1) { + printf("Failed to stat client fd\n"); + + exit(0); + } + + if (st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino) { + close(fd); + } + + return NULL; +} + +void entry(u_int32_t fd) { + printf("companion entry fd=%d\n", fd); + + char name[256]; + read(fd, name, 256); + + int library_fd; + read(fd, &library_fd, sizeof(library_fd)); + + ZygiskCompanionEntryFn entry = load_module(library_fd); + + close(library_fd); + + if (entry == NULL) { + printf("No companion entry for `%s`\n", name); + + write(fd, (void *)0, 1); + + exit(0); + } + + printf("Companion process created for `%s`\n", name); + + write(fd, (void *)1, 1); + + while (1) { + int client_fd; + read(fd, &client_fd, sizeof(client_fd)); + + printf("New companion request from module `%s` fd=`%d`\n", name, client_fd); + + write(fd, (void *)1, 1); + + pthread_t thread; + pthread_create(&thread, NULL, ExecuteNew, (void *)client_fd); + pthread_detach(thread); + + + } +} diff --git a/zygiskd/constants.h b/zygiskd/constants.h new file mode 100644 index 0000000..a3c7cf4 --- /dev/null +++ b/zygiskd/constants.h @@ -0,0 +1,70 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#include + +#define bool _Bool +#define true 1 +#define false 0 + +// #define MIN_APATCH_VERSION (atoi(getenv("MIN_APATCH_VERSION"))) +// #define MIN_KSU_VERSION (atoi(getenv("MIN_KSU_VERSION"))) +// #define MAX_KSU_VERSION (atoi(getenv("MAX_KSU_VERSION"))) +// #define MIN_MAGISK_VERSION (atoi(getenv("MIN_MAGISK_VERSION"))) +// #define ZKSU_VERSION (getenv("ZKSU_VERSION")) + +#define MIN_APATCH_VERSION 0 +// val minKsudVersion by extra(11425) +// val maxKsuVersion by extra(20000) +#define MIN_KSU_VERSION 11425 +#define MAX_KSU_VERSION 20000 +#define MIN_MAGISK_VERSION 0 +#define ZKSU_VERSION "1.0.0" + +#if DEBUG == false + #define MAX_LOG_LEVEL ANDROID_LOG_VERBOSE +#else + #define MAX_LOG_LEVEL ANDROID_LOG_INFO +#endif + +#if (defined(__LP64__) || defined(_LP64)) + #define lp_select(a, b) a +#else + #define lp_select(a, b) b +#endif + +#define PATH_MODULES_DIR ".." +#define ZYGOTE_INJECTED (lp_select(5, 4)) +#define DAEMON_SET_INFO (lp_select(7, 6)) +#define DAEMON_SET_ERROR_INFO (lp_select(9, 8)) +#define SYSTEM_SERVER_STARTED 10 + +enum DaemonSocketAction { + PingHeartbeat, + RequestLogcatFd, + GetProcessFlags, + GetInfo, + ReadModules, + RequestCompanionSocket, + GetModuleDir, + ZygoteRestart, + SystemServerStarted +}; + +enum ProcessFlags { + PROCESS_GRANTED_ROOT = 1 << 0, + PROCESS_ON_DENYLIST = 1 << 1, + PROCESS_IS_MANAGER = 1 << 28, + PROCESS_ROOT_IS_APATCH = 1 << 27, + PROCESS_ROOT_IS_KSU = 1 << 29, + PROCESS_ROOT_IS_MAGISK = 1 << 30, + PROCESS_IS_SYSUI = 1 << 31 +}; + +enum RootImplState { + Supported, + TooOld, + Abnormal +}; + +#endif /* CONSTANTS_H */ diff --git a/zygiskd/dl.c b/zygiskd/dl.c new file mode 100644 index 0000000..36582b6 --- /dev/null +++ b/zygiskd/dl.c @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +#define ANDROID_NAMESPACE_TYPE_SHARED 0x2 +#define ANDROID_DLEXT_USE_NAMESPACE 0x200 + +struct AndroidNamespace { + u_int8_t _unused[0]; +}; + +struct AndroidDlextinfo { + u_int64_t flags; + void *reserved_addr; + size_t reserved_size; + int relro_fd; + int library_fd; + __off64_t library_fd_offset; + struct AndroidNamespace *library_namespace; +}; + +void *android_dlopen_ext(const char *filename, int flags, const struct AndroidDlextinfo *extinfo); + +void *android_dlopen(char *path, u_int32_t flags) { + char *dir = dirname(path); + struct AndroidDlextinfo info = { + .flags = 0, + .reserved_addr = NULL, + .reserved_size = 0, + .relro_fd = 0, + .library_fd = 0, + .library_fd_offset = 0, + .library_namespace = NULL, + }; + + void *android_create_namespace_fn = dlsym(RTLD_DEFAULT, "__loader_android_create_namespace"); + + if (android_create_namespace_fn != NULL) { + void *ns = ((void *(*)(const char *, const char *, const char *, u_int32_t, void *, void *, void *))android_create_namespace_fn)( + path, + dir, + NULL, + ANDROID_NAMESPACE_TYPE_SHARED, + NULL, + NULL, + &android_dlopen + ); + + if (ns != NULL) { + info.flags = ANDROID_DLEXT_USE_NAMESPACE; + info.library_namespace = ns; + } + } + + return android_dlopen_ext(path, flags, &info); +} diff --git a/zygiskd/dl.h b/zygiskd/dl.h new file mode 100644 index 0000000..acdf231 --- /dev/null +++ b/zygiskd/dl.h @@ -0,0 +1,6 @@ +#ifndef DL_H +#define DL_H + +void *android_dlopen(char *path, u_int32_t flags); + +#endif /* DL_H */ \ No newline at end of file diff --git a/zygiskd/main.c b/zygiskd/main.c new file mode 100644 index 0000000..e29411a --- /dev/null +++ b/zygiskd/main.c @@ -0,0 +1,83 @@ +#include +#include + +#include + +#include "root_impl/common.h" + +#include "utils.h" + +// extern "C" { +// fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32; +// fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32; +// fn __system_property_set(name: *const c_char, value: *const c_char) -> u32; +// fn __system_property_find(name: *const c_char) -> *const c_void; +// fn __system_property_wait( +// info: *const c_void, +// old_serial: u32, +// new_serial: *mut u32, +// timeout: *const libc::timespec, +// ) -> bool; +// fn __system_property_serial(info: *const c_void) -> u32; +// } + +int __android_log_print(int prio, const char *tag, const char *fmt, ...); + +int main(int argc, char *argv[]) { + /* Initialize android logger */ + __android_log_print(ANDROID_LOG_INFO, "zygiskd", "Hello, world! :3"); + + if (argc > 1) { + if (strcmp(argv[0], "companion") == 0) { + /* WIP */ + + return 0; + } + + else if (strcmp(argv[1], "version") == 0) { + printf("ReZygisk Daemon %s\n", ZKSU_VERSION); + + return 0; + } + + else if (strcmp(argv[1], "root") == 0) { + root_impls_setup(); + enum RootImpl impl = get_impl(); + + switch (impl) { + case None: { + printf("No root implementation found.\n"); + + return 1; + } + + case Multiple: { + printf("Multiple root implementations found.\n"); + + return 1; + } + + case KernelSU: { + printf("KernelSU root implementation found.\n"); + + return 0; + } + } + + + return 0; + } + + else { + printf("Usage: zygiskd [companion|version|root]\n"); + + return 0; + } + } + + switch_mount_namespace((pid_t)1); + root_impls_setup(); + __android_log_print(ANDROID_LOG_INFO, "zygiskd", "Root implementation: %d", get_impl()); + + return 0; +} \ No newline at end of file diff --git a/zygiskd/root_impl/common.c b/zygiskd/root_impl/common.c new file mode 100644 index 0000000..684ca89 --- /dev/null +++ b/zygiskd/root_impl/common.c @@ -0,0 +1,52 @@ +#include "kernelsu.h" + +#include "common.h" + +static enum RootImpl ROOT_IMPL = None; + +void root_impls_setup() { + enum RootImplState ksu_version = ksu_get_kernel_su(); + + enum RootImpl impl = None; + + if (ksu_version == Supported) impl = KernelSU; + + ROOT_IMPL = impl; +} + +enum RootImpl get_impl() { + return ROOT_IMPL; +} + +bool uid_granted_root(int uid) { + switch (get_impl()) { + case KernelSU: { + return ksu_uid_granted_root(uid); + } + default: { + return false; + } + } +} + +bool uid_should_umount(int uid) { + switch (get_impl()) { + case KernelSU: { + return ksu_uid_should_umount(uid); + } + default: { + return false; + } + } +} + +bool uid_is_manager(int uid) { + switch (get_impl()) { + case KernelSU: { + return ksu_uid_is_manager(uid); + } + default: { + return false; + } + } +} diff --git a/zygiskd/root_impl/common.h b/zygiskd/root_impl/common.h new file mode 100644 index 0000000..d1a088a --- /dev/null +++ b/zygiskd/root_impl/common.h @@ -0,0 +1,22 @@ +#ifndef COMMON_H +#define COMMON_H + +#include "../constants.h" + +enum RootImpl { + None, + Multiple, /* INFO: I know. */ + KernelSU +}; + +void root_impls_setup(); + +enum RootImpl get_impl(); + +bool uid_granted_root(int uid); + +bool uid_should_umount(int uid); + +bool uid_is_manager(int uid); + +#endif /* COMMON_H */ diff --git a/zygiskd/root_impl/kernelsu.c b/zygiskd/root_impl/kernelsu.c new file mode 100644 index 0000000..cfa28e3 --- /dev/null +++ b/zygiskd/root_impl/kernelsu.c @@ -0,0 +1,54 @@ +#include + +#include +#include + +#include "../constants.h" + +#include "kernelsu.h" + +#define KERNEL_SU_OPTION 0xdeadbeef + +#define CMD_GET_VERSION 2 +#define CMD_UID_GRANTED_ROOT 12 +#define CMD_UID_SHOULD_UMOUNT 13 + +enum RootImplState ksu_get_kernel_su() { + int version = 0; + prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &version, 0, 0); + + if (version == 0) return -1; + + if (version >= MIN_KSU_VERSION && version <= MAX_KSU_VERSION) return Supported; + + if (version >= 1 && version <= MIN_KSU_VERSION - 1) return TooOld; + + return Abnormal; +} + +bool ksu_uid_granted_root(int uid) { + uint32_t result = 0; + bool granted = false; + prctl(KERNEL_SU_OPTION, CMD_UID_GRANTED_ROOT, uid, &granted, &result); + + if (result != KERNEL_SU_OPTION) return false; + + return granted; +} + +bool ksu_uid_should_umount(int uid) { + uint32_t result = 0; + bool umount = false; + prctl(KERNEL_SU_OPTION, CMD_UID_SHOULD_UMOUNT, uid, &umount, &result); + + if (result != KERNEL_SU_OPTION) return false; + + return umount; +} + +bool ksu_uid_is_manager(int uid) { + struct stat s; + if (stat("/data/user_de/0/me.weishu.kernelsu", &s) == 0) return s.st_uid == (uid_t)uid; + + return false; +} diff --git a/zygiskd/root_impl/kernelsu.h b/zygiskd/root_impl/kernelsu.h new file mode 100644 index 0000000..a4cd565 --- /dev/null +++ b/zygiskd/root_impl/kernelsu.h @@ -0,0 +1,14 @@ +#ifndef KERNELSU_H +#define KERNELSU_H + +#include "../constants.h" + +enum RootImplState ksu_get_kernel_su(); + +bool ksu_uid_granted_root(int uid); + +bool ksu_uid_should_umount(int uid); + +bool ksu_uid_is_manager(int uid); + +#endif \ No newline at end of file diff --git a/zygiskd/utils.c b/zygiskd/utils.c new file mode 100644 index 0000000..7c98777 --- /dev/null +++ b/zygiskd/utils.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +void switch_mount_namespace(pid_t pid) { + char current_path[PATH_MAX]; + if (getcwd(current_path, PATH_MAX) == NULL) { + /* TODO: Improve error messages */ + perror("getcwd"); + + return; + } + + /* INFO: We will NEVER achieve PATH_MAX value, but this is for ensurance. */ + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/%d/ns/mnt", pid); + + FILE *mnt_ns = fopen(path, "r"); + if (mnt_ns == NULL) { + /* TODO: Improve error messages */ + perror("fopen"); + + return; + } + + if (setns(fileno(mnt_ns), 0) == -1) { + /* TODO: Improve error messages */ + perror("setns"); + + return; + } + + fclose(mnt_ns); + + if (chdir(current_path) == -1) { + /* TODO: Improve error messages */ + perror("chdir"); + + return; + } +} + +int __system_property_get(const char *, char *); + +void get_property(const char *name, char *output) { + __system_property_get(name, output); +} + +void set_socket_create_context(const char *context) { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/thread-self/attr/sockcreate"); + + FILE *sockcreate = fopen(path, "w"); + if (sockcreate == NULL) { + perror("fopen"); + + return; + } + + if (fwrite(context, 1, strlen(context), sockcreate) != strlen(context)) { + perror("fwrite"); + + return; + } + + fclose(sockcreate); +} + +void get_current_attr(char *output) { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/self/attr/current"); + + FILE *current = fopen(path, "r"); + if (current == NULL) { + perror("fopen"); + + return; + } + + if (fgets(output, PATH_MAX, current) == NULL) { + perror("fgets"); + + return; + } + + fclose(current); +} + +void unix_datagram_sendto(const char *path, const char *buf) { + char current_attr[PATH_MAX]; + get_current_attr(current_attr); + + set_socket_create_context(current_attr); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + int socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (socket_fd == -1) { + perror("socket"); + + return; + } + + if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("connect"); + + return; + } + + if (sendto(socket_fd, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("sendto"); + + return; + } + + set_socket_create_context("u:r:zygote:s0"); + + close(socket_fd); +} + +int chcon(const char *path, const char *context) { + char command[PATH_MAX]; + snprintf(command, PATH_MAX, "chcon %s %s", context, path); + + return system(command); +} + +int unix_listener_from_path(char *path) { + remove(path); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + int socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (socket_fd == -1) { + perror("socket"); + + return -1; + } + + if (bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind"); + + return -1; + } + + if (listen(socket_fd, 2) == -1) { + perror("listen"); + + return -1; + } + + if (chcon(path, "u:object_r:magisk_file:s0") == -1) { + perror("chcon"); + + return -1; + } + + return socket_fd; +} \ No newline at end of file diff --git a/zygiskd/utils.h b/zygiskd/utils.h new file mode 100644 index 0000000..b880d4d --- /dev/null +++ b/zygiskd/utils.h @@ -0,0 +1,16 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +void switch_mount_namespace(pid_t pid); + +void get_property(const char *name, char *output); + +void set_socket_create_context(const char *context); + +void unix_datagram_sendto(const char *path, const char *buf); + +int unix_listener_from_path(char *path); + +#endif /* UTILS_H */ \ No newline at end of file diff --git a/zygiskd/zygiskd b/zygiskd/zygiskd new file mode 100755 index 0000000..5baaf30 Binary files /dev/null and b/zygiskd/zygiskd differ diff --git a/zygiskd/zygiskd.c b/zygiskd/zygiskd.c new file mode 100644 index 0000000..20ae753 --- /dev/null +++ b/zygiskd/zygiskd.c @@ -0,0 +1,353 @@ +#include +#include +#include +#include + +#include "root_impl/common.h" +#include "constants.h" +#include "utils.h" + +struct Module { + char *name; + int lib_fd; +} + +struct Context { + struct Module *modules; +} + +enum Architecture { + ARM32, + ARM64, + X86, + X86_64, +} + +static char TMP_PATH[] = "/data/adb/rezygisk"; +static char CONTROLLER_SOCKET[PATH_MAX]; +static char PATH_CP_NAME[PATH_MAX]; + +enum Architecture get_arch() { + char system_arch[PROP_VALUE_MAX]; + get_property("ro.product.cpu.abi", system_arch); + + if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64); + if (strstr(system_arch, "x86") != NULL) return lp_select(ARM64, X86_64); + + printf("Unsupported system architecture: %s\n", system_arch); + exit(1); +} + +int create_library_fd(char *so_path) { + int memfd = memfd_create("jit-cache-zygisk", MFD_ALLOW_SEALING); + if (memfd == -1) { + printf("Failed creating memfd: %s\n", strerror(errno)); + return -1; + } + + int file = open(so_path, O_RDONLY); + if (file == -1) { + printf("Failed opening file: %s\n", strerror(errno)); + return -1; + } + + struct stat st; + fstat(file, &st); + ftruncate(memfd, st.st_size); + + void *addr = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); + if (addr == MAP_FAILED) { + printf("Failed mapping memory: %s\n", strerror(errno)); + return -1; + } + + read(file, addr, st.st_size); + munmap(addr, st.st_size); + close(file); + + unsigned int seals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL; + if (fcntl(memfd, F_ADD_SEALS, seals) == -1) { + printf("Failed adding seals: %s\n", strerror(errno)); + return -1; + } + + return memfd; +} + +/* WARNING: Dynamic memory based */ +struct Module **load_modules(enum Architecture arch) { + struct Module **modules = malloc(sizeof(struct Module *)); + DIR *dir = opendir(PATH_MODULES_DIR); + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + char *name = entry->d_name; + char so_path[PATH_MAX]; + snprintf(so_path, PATH_MAX, "%s/zygisk/%s.so", PATH_MODULES_DIR, name); + + struct stat st; + if (stat(so_path, &st) == -1) continue; + + char disabled[PATH_MAX]; + snprintf(disabled, PATH_MAX, "%s/disable", PATH_MODULES_DIR); + if (stat(disabled, &st) != -1) continue; + + printf("Loading module `%s`...\n", name); + int lib_fd = create_library_fd(so_path); + if (lib_fd == -1) continue; + + struct Module *module = malloc(sizeof(struct Module)); + module->name = name; + module->lib_fd = lib_fd; + modules = realloc(modules, sizeof(modules) + sizeof(module)); + modules[sizeof(modules)] = module; + } + + return modules; +} + +void free_modules(struct Module **modules) { + for (int i = 0; i < sizeof(modules); i++) { + free(modules[i]); + } + + free(modules); +} + +int create_daemon_socket() { + set_socket_create_context("u:r:zygote:s0"); + int socket_fd = unix_listener_from_path(PATH_CP_NAME); + + return socket_fd; +} + +void zygiskd_start() { + printf("Welcome to ReZygisk %s!\n", ZKSU_VERSION); + + snprintf(CONTROLLER_SOCKET, PATH_MAX, "%s/init_monitor", TMP_PATH); + snprintf(PATH_CP_NAME, PATH_MAX, "%s/%s", TMP_PATH, lp_select("/cp32.sock", "/cp64.sock")); + + Architecture arch = get_arch(); + printf("Daemon architecture: %s\n", arch); + + struct Module **modules = load_modules(arch); + + char *msg = malloc(1); + size_t msg_len = 1; + + switch (get_impl()) { + case KernelSU: { + msg[0] = DAEMON_SET_INFO; + + msg = realloc(msg, strlen("Root: KernelSU, Modules: ") + 1); + memcpy(msg + 1, "Root: KernelSU, Modules: ", strlen("Root: KernelSU, Modules: ")); + msg_len += strlen("Root: KernelSU, Modules: "); + + for (int i = 0; i < sizeof(modules); i++) { + msg = realloc(msg, strlen(modules[i]->name) + strlen(", ") + 1); + memcpy(msg + msg_len, modules[i]->name, strlen(modules[i]->name)); + msg_len += strlen(modules[i]->name); + memcpy(msg + msg_len, ", ", strlen(", ")); + msg_len += strlen(", "); + + free(modules[i]); + } + } + default: { + msg[0] = DAEMON_SET_ERROR_INFO; + + msg = realloc(msg, strlen("Invalid root implementation") + 1); + memcpy(msg + 1, "Invalid root implementation", strlen("Invalid root implementation")); + } + } + + msg = realloc(msg, msg_len + 1); + msg[msg_len] = '\0'; + + unix_datagram_sendto(CONTROLLER_SOCKET, msg); + + int socket_fd = create_daemon_socket(); + if (socket_fd == -1) { + printf("Failed creating daemon socket: %s\n", strerror(errno)); + + return; + } + + while (1) { + struct sockaddr_un addr; + socklen_t addr_len = sizeof(addr); + int client_fd = accept(socket_fd, (struct sockaddr *) &addr, &addr_len); + if (client_fd == -1) { + printf("Failed accepting client: %s\n", strerror(errno)); + + return; + } + + char action; + read(client_fd, &action, 1); + + switch (action) { + case DAEMON_PING_HEARTBEAT: { + char value = ZYGOTE_INJECTED; + unix_datagram_sendto(CONTROLLER_SOCKET, &value); + } + case DAEMON_ZYGOTE_RESTART: { + printf("Zygote restarted, clean up companions\n"); + + free_modules(modules); + + /* companion code */ + } + case DAEMON_SYSTEM_SERVER_STARTED: { + char value = SYSTEM_SERVER_STARTED; + unix_datagram_sendto(CONTROLLER_SOCKET, &value); + } + default: { + // WIP + } + } + } +} + +void spawn_companion(char *name, int lib_fd) { + /* Creates 2 connected unix streams */ + int sockets[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); + + int daemon_fd = sockets[0]; + int companion_fd = sockets[1]; + + pid_t pid = fork(); + if (pid == -1) { + printf("Failed forking: %s\n", strerror(errno)); + + return; + } else if (pid > 0) { + close(companion_fd); + + int status; + waitpid(pid, &status, 0); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + write(daemon_fd, name, strlen(name)); + write(daemon_fd, &lib_fd, sizeof(lib_fd)); + + int response; + read(daemon_fd, &response, 1); + + if (response == 0) { + printf("No companion spawned for %s because it has no entry\n", name); + } else if (response == 1) { + printf("Spawned companion for %s\n", name); + } else { + printf("Invalid companion response\n"); + } + } else { + printf("Exited with status %d\n", status); + } + } else { + fcntl(companion_fd, F_SETFD, 0); + } + + char *args[] = { "zygiskd", "companion", companion_fd, NULL }; + execv("/system/bin/zygiskd", args); + + exit(0); +} + +void handle_daemon_action(enum DaemonSocketAction action, int stream_fd, struct Context *context) { + switch (action) { + case RequestLogcatFd: { + while (1) { + char level; + read(stream_fd, &level, 1); + + char tag[PATH_MAX]; + read(stream_fd, tag, PATH_MAX); + + char message[PATH_MAX]; + read(stream_fd, message, PATH_MAX); + + __android_log_print(level, tag, message); + } + } + case GetProcessFlags: { + int uid; + read(stream_fd, &uid, sizeof(uid)); + + int flags = 0; + if (uid_is_manager(uid)) { + flags |= PROCESS_IS_MANAGER; + } else { + if (uid_granted_root(uid)) { + flags |= PROCESS_GRANTED_ROOT; + } + if (uid_should_umount(uid)) { + flags |= PROCESS_ON_DENYLIST; + } + } + + switch (get_impl()) { + case KernelSU: { + flags |= PROCESS_ROOT_IS_KSU; + } + // case Magisk: { + // flags |= PROCESS_ROOT_IS_MAGISK; + // } + // case APatch: { + // flags |= PROCESS_ROOT_IS_APATCH; + // } + } + + write(stream_fd, &flags, sizeof(flags)); + } + case GetInfo: { + int flags = 0; + + switch (get_impl()) { + case KernelSU: { + flags |= PROCESS_ROOT_IS_KSU; + } + // case Magisk: { + // flags |= PROCESS_ROOT_IS_MAGISK; + // } + // case APatch: { + // flags |= PROCESS_ROOT_IS_APATCH; + // } + } + + write(stream_fd, &flags, sizeof(flags)); + + int pid = getpid(); + write(stream_fd, &pid, sizeof(pid)); + } + case ReadModules: { + int len = sizeof(context->modules); + write(stream_fd, &len, sizeof(len)); + + for (int i = 0; i < len; i++) { + write(stream_fd, context->modules[i]->name, strlen(context->modules[i]->name)); + send_fd(stream_fd, context->modules[i]->lib_fd); + } + } + case RequestCompanionSocket: { + /* WIP */ + + break; + } + case GetModuleDir: { + int index; + read(stream_fd, &index, sizeof(index)); + + char dir[PATH_MAX]; + snprintf(dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context->modules[index]->name); + int dir_fd = open(dir, O_RDONLY); + + send_fd(stream_fd, dir_fd); + } + default: { + // WIP + } + } +} +