diff --git a/loader/src/common/daemon.cpp b/loader/src/common/daemon.cpp index 25d14491..5649f006 100644 --- a/loader/src/common/daemon.cpp +++ b/loader/src/common/daemon.cpp @@ -94,8 +94,8 @@ namespace zygiskd { return res; } - std::vector ReadModules() { - std::vector modules; + std::vector ReadModules() { + std::vector modules; int fd = Connect(1); if (fd == -1) { PLOGE("ReadModules"); @@ -261,16 +261,17 @@ namespace zygiskd { } else info->running = false; } - std::string GetCleanNamespace() { + std::string UpdateMountNamespace(enum mount_namespace_state nms_state) { int fd = Connect(1); if (fd == -1) { - PLOGE("GetCleanNamespace"); + PLOGE("UpdateMountNamespace"); return ""; } - socket_utils::write_u8(fd, (uint8_t) SocketAction::GetCleanNamespace); + socket_utils::write_u8(fd, (uint8_t) SocketAction::UpdateMountNamespace); socket_utils::write_u32(fd, getpid()); + socket_utils::write_u8(fd, (uint8_t)nms_state); uint32_t target_pid = socket_utils::read_u32(fd); int target_fd = 0; @@ -279,6 +280,8 @@ namespace zygiskd { target_fd = (int)socket_utils::read_u32(fd); if (target_fd == 0) goto error; + + close(fd); return "/proc/" + std::to_string(target_pid) + "/fd/" + std::to_string(target_fd); diff --git a/loader/src/include/daemon.h b/loader/src/include/daemon.h index 90f8d2a7..7016ca97 100644 --- a/loader/src/include/daemon.h +++ b/loader/src/include/daemon.h @@ -61,13 +61,19 @@ struct zygote_info { bool running; }; +enum mount_namespace_state { + Clean, + Rooted, + Module +}; + namespace zygiskd { - struct Module { + struct ModuleInfo { std::string name; UniqueFd memfd; - inline explicit Module(std::string name, int memfd) : name(name), memfd(memfd) {} + inline explicit ModuleInfo(std::string name, int memfd) : name(name), memfd(memfd) {} }; enum class SocketAction { @@ -80,7 +86,7 @@ namespace zygiskd { GetModuleDir, ZygoteRestart, SystemServerStarted, - GetCleanNamespace + UpdateMountNamespace }; void Init(const char *path); @@ -91,7 +97,7 @@ namespace zygiskd { int RequestLogcatFd(); - std::vector ReadModules(); + std::vector ReadModules(); uint32_t GetProcessFlags(uid_t uid); @@ -105,5 +111,5 @@ namespace zygiskd { void GetInfo(struct zygote_info *info); - std::string GetCleanNamespace(); + std::string UpdateMountNamespace(enum mount_namespace_state mns_state); } diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 2d3cedb0..40af1d98 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -123,7 +123,6 @@ vector> *plt_hook_list; map, StringCmp> *jni_hook_list; bool should_unmap_zygisk = false; std::vector cached_map_infos = {}; -std::vector cached_mountinfo = {}; } // namespace @@ -138,36 +137,29 @@ DCL_HOOK_FUNC(int, fork) { return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork(); } -void clean_mnt_ns() { - std::string path = zygiskd::GetCleanNamespace(); - LOGI("Switching to clean namespace: %s", path.data()); +bool update_mnt_ns(enum mount_namespace_state mns_state, bool dry_run) { + std::string ns_path = zygiskd::UpdateMountNamespace(mns_state); + if (ns_path.empty()) { + PLOGE("Failed to update mount namespace"); - if (path.empty()) { - LOGE("Failed to get clean namespace path"); - - return; - } - - int nsfd = open(path.data(), O_RDONLY | O_CLOEXEC); - if (nsfd == -1) { - LOGE("Failed to open clean namespace: %s", strerror(errno)); - - return; + return false; } - if (setns(nsfd, CLONE_NEWNS) == -1) { - LOGE("Failed to setns clean namespace: %s", strerror(errno)); + if (dry_run) return true; - close(nsfd); + int updated_ns = open(ns_path.data(), O_RDONLY); + if (updated_ns == -1) { + PLOGE("Failed to open mount namespace [%s]", ns_path.data()); - return; + return false; } - close(nsfd); + LOGD("set mount namespace to [%s] fd=[%d]\n", ns_path.data(), updated_ns); + setns(updated_ns, CLONE_NEWNS); - LOGD("Switched to clean namespace"); + close(updated_ns); - return; + return true; } // Unmount stuffs in the process's private mount namespace @@ -177,17 +169,19 @@ DCL_HOOK_FUNC(int, unshare, int flags) { // For some unknown reason, unmounting app_process in SysUI can break. // This is reproducible on the official AVD running API 26 and 27. // Simply avoid doing any unmounts for SysUI to avoid potential issues. - (g_ctx->info_flags & PROCESS_IS_FIRST_STARTED) == 0) { - if (g_ctx->flags[DO_REVERT_UNMOUNT]) clean_mnt_ns(); - else if (!(g_ctx->info_flags & (PROCESS_IS_MANAGER | PROCESS_GRANTED_ROOT))) - do_umount(cached_mountinfo); + !g_ctx->flags[SERVER_FORK_AND_SPECIALIZE] && !(g_ctx->info_flags & PROCESS_IS_FIRST_STARTED)) { + if (g_ctx->info_flags & (PROCESS_IS_MANAGER | PROCESS_GRANTED_ROOT)) { + update_mnt_ns(Rooted, false); + } else if (!g_ctx->flags[DO_REVERT_UNMOUNT]) { + update_mnt_ns(Module, false); + } - /* Zygisksu changed: No umount app_process */ old_unshare(CLONE_NEWNS); - - // Restore errno back to 0 - errno = 0; } + + /* INFO: To spoof the errno value */ + errno = 0; + return res; } @@ -218,7 +212,6 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) { unhook_functions(); cached_map_infos.clear(); - cached_mountinfo.clear(); if (should_unmap_zygisk) { // Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature, @@ -631,15 +624,9 @@ void ZygiskContext::run_modules_post() { void ZygiskContext::app_specialize_pre() { flags[APP_SPECIALIZE] = true; - info_flags = zygiskd::GetProcessFlags(getpid()); - if (info_flags & PROCESS_ON_DENYLIST) { - if (info_flags & PROCESS_ROOT_IS_KSU) { - cached_mountinfo = fill_ksu_umount_paths(); - } else if (info_flags & PROCESS_ROOT_IS_APATCH){ - cached_mountinfo = fill_apatch_umount_paths(); - } else if (info_flags & PROCESS_ROOT_IS_MAGISK) { - cached_mountinfo = fill_magisk_umount_paths(); - } + info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid); + if (info_flags & PROCESS_IS_FIRST_STARTED) { + update_mnt_ns(Clean, true); } if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) { @@ -715,8 +702,10 @@ void ZygiskContext::nativeForkSystemServer_post() { void ZygiskContext::nativeForkAndSpecialize_pre() { process = env->GetStringUTFChars(args.app->nice_name, nullptr); LOGV("pre forkAndSpecialize [%s]", process); - flags[APP_FORK_AND_SPECIALIZE] = true; + + update_mnt_ns(Clean, false); + /* Zygisksu changed: No args.app->fds_to_ignore check since we are Android 10+ */ if (logging::getfd() != -1) { exempted_fds.push_back(logging::getfd()); diff --git a/loader/src/injector/unmount.cpp b/loader/src/injector/unmount.cpp deleted file mode 100644 index bdb64523..00000000 --- a/loader/src/injector/unmount.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include - -#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"; - constexpr auto KSU_OVERLAY_SOURCE = "KSU"; - constexpr auto AP_OVERLAY_SOURCE = "APatch"; - const std::vector DEVICE_PARTITIONS{"/system", "/vendor", "/product", "/system_ext", "/odm", "/oem"}; - - void lazy_unmount(const char* mountpoint) { - if (umount2(mountpoint, MNT_DETACH) != -1) { - LOGD("Unmounted (%s)", mountpoint); - } else { -#ifndef NDEBUG - PLOGE("Unmount (%s)", mountpoint); -#endif - } - } -} - -void do_umount(std::vector targets) { - for (auto& target: targets) { - lazy_unmount(target.c_str()); - } -} - -std::vector fill_ksu_umount_paths() { - std::string ksu_loop; - std::vector targets; - - // Unmount ksu module dir last - targets.emplace_back(MODULE_DIR); - - std::vector mounts = parse_mount_info("self"); - for (auto &info : mounts) { - if (info.target == MODULE_DIR) { - ksu_loop = info.source; - continue; - } - - // Unmount everything mounted to /data/adb - if (info.target.starts_with("/data/adb")) { - targets.emplace_back(info.target); - } - - // Unmount everything mounted to /data/adb - if (info.root.starts_with("/adb/modules")) { - targets.emplace_back(info.target); - } - - // Unmount ksu overlays - if (info.type == "overlay" - && info.source == KSU_OVERLAY_SOURCE - && std::find(DEVICE_PARTITIONS.begin(), DEVICE_PARTITIONS.end(), info.target) != DEVICE_PARTITIONS.end()) { - targets.emplace_back(info.target); - } - - // Unmount temp dir - if (info.type == "tmpfs" && info.source == KSU_OVERLAY_SOURCE) { - targets.emplace_back(info.target); - } - } - - for (auto &info : mounts) { - // Unmount everything from ksu loop except ksu module dir - if (info.source == ksu_loop && info.target != MODULE_DIR) { - targets.emplace_back(info.target); - } - } - - return targets; -} - -std::vector fill_magisk_umount_paths() { - std::vector 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); - } - // Unmount everything mounted to /data/adb - if (info.target.starts_with("/data/adb")) { - targets.emplace_back(info.target); - } - } - - return targets; -} - -std::vector fill_apatch_umount_paths() { - std::string ap_loop; - std::vector targets; - - // Unmount ksu module dir last - targets.emplace_back(MODULE_DIR); - - std::vector mounts = parse_mount_info("self"); - for (auto &info : mounts) { - if (info.target == MODULE_DIR) { - ap_loop = info.source; - continue; - } - - // Unmount everything mounted to /data/adb - if (info.target.starts_with("/data/adb")) { - targets.emplace_back(info.target); - } - - // Unmount everything mounted to /data/adb - if (info.root.starts_with("/adb/modules")) { - targets.emplace_back(info.target); - } - - // Unmount ksu overlays - if (info.type == "overlay" - && info.source == AP_OVERLAY_SOURCE - && std::find(DEVICE_PARTITIONS.begin(), DEVICE_PARTITIONS.end(), info.target) != DEVICE_PARTITIONS.end()) { - targets.emplace_back(info.target); - } - - // Unmount temp dir - if (info.type == "tmpfs" && info.source == AP_OVERLAY_SOURCE) { - targets.emplace_back(info.target); - } - } - - for (auto &info : mounts) { - // Unmount everything from ksu loop except ksu module dir - if (info.source == ap_loop && info.target != MODULE_DIR) { - targets.emplace_back(info.target); - } - } - - return targets; -} diff --git a/loader/src/injector/zygisk.hpp b/loader/src/injector/zygisk.hpp index 331a6760..d171890d 100644 --- a/loader/src/injector/zygisk.hpp +++ b/loader/src/injector/zygisk.hpp @@ -8,13 +8,3 @@ extern size_t block_size; void hook_functions(); void clean_trace(const char* path, size_t load = 1, size_t unload = 0, bool spoof_maps = false); - - - -void do_umount(std::vector targets); - -std::vector fill_ksu_umount_paths(); - -std::vector fill_magisk_umount_paths(); - -std::vector fill_apatch_umount_paths(); diff --git a/zygiskd/src/constants.h b/zygiskd/src/constants.h index 49da0086..55d482f2 100644 --- a/zygiskd/src/constants.h +++ b/zygiskd/src/constants.h @@ -54,4 +54,10 @@ enum RootImplState { Abnormal }; +enum MountNamespaceState { + Clean, + Rooted, + Module +}; + #endif /* CONSTANTS_H */ diff --git a/zygiskd/src/root_impl/common.c b/zygiskd/src/root_impl/common.c index aaa6a93e..2198bdc3 100644 --- a/zygiskd/src/root_impl/common.c +++ b/zygiskd/src/root_impl/common.c @@ -127,17 +127,3 @@ bool uid_is_manager(uid_t uid) { } } } - -bool uid_is_systemui(uid_t uid) { - struct stat s; - if (stat("/data/user_de/0/com.android.systemui", &s) == -1) { - if (errno != ENOENT) { - LOGE("Failed to stat SystemUI data directory: %s\n", strerror(errno)); - } - errno = 0; - - return false; - } - - return s.st_uid == uid; -} diff --git a/zygiskd/src/root_impl/common.h b/zygiskd/src/root_impl/common.h index 5a5bb5af..dedb796c 100644 --- a/zygiskd/src/root_impl/common.h +++ b/zygiskd/src/root_impl/common.h @@ -35,6 +35,4 @@ bool uid_should_umount(uid_t uid); bool uid_is_manager(uid_t uid); -bool uid_is_systemui(uid_t uid); - #endif /* COMMON_H */ diff --git a/zygiskd/src/utils.c b/zygiskd/src/utils.c index c72af944..dce5c71d 100644 --- a/zygiskd/src/utils.c +++ b/zygiskd/src/utils.c @@ -20,8 +20,11 @@ #include "utils.h" #include "root_impl/common.h" +#include "root_impl/magisk.h" int clean_namespace_fd = 0; +int rooted_namespace_fd = 0; +int module_namespace_fd = 0; bool switch_mount_namespace(pid_t pid) { char path[PATH_MAX]; @@ -590,7 +593,7 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount return true; } -void unmount_root(struct root_impl impl) { +void unmount_root(bool modules_only, struct root_impl impl) { /* INFO: We are already in the target pid mount namespace, so actually, when we use self here, we meant its pid. */ @@ -601,9 +604,6 @@ void unmount_root(struct root_impl impl) { return; } - char **mounts_to_unmount = NULL; - size_t mounts_to_unmount_len = 0; - switch (impl.impl) { case None: { break; } case Multiple: { break; } @@ -618,30 +618,24 @@ void unmount_root(struct root_impl impl) { struct mountinfo mount = mounts.mounts[i]; if ( - strcmp(mount.source, source_name) == 0 || - strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0 || - strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0 + ( + modules_only && + ( + strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0 + ) + ) || + ( + strcmp(mount.source, source_name) == 0 || + strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0 || + strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) == 0 + ) ) { - mounts_to_unmount = (char **)realloc(mounts_to_unmount, (mounts_to_unmount_len + 1) * sizeof(char *)); - if (!mounts_to_unmount) { - LOGE("Failed to allocate memory for mounts_to_unmount\n"); - - free_mounts(&mounts); - - return; - } + if (umount2(mount.target, MNT_DETACH) == -1) { + LOGE("[Magisk] Failed to unmount %s: %s\n", mount.target, strerror(errno)); - mounts_to_unmount[mounts_to_unmount_len] = strdup(mount.target); - if (!mounts_to_unmount[mounts_to_unmount_len]) { - LOGE("Failed to allocate memory for mounts_to_unmount[%zu]\n", mounts_to_unmount_len); - - free_mounts(&mounts); - - return; + continue; } - mounts_to_unmount_len++; - LOGI("[%s] Unmounted %s (%s | %s)\n", source_name, mount.target, mount.root, mount.source); } } @@ -649,61 +643,57 @@ void unmount_root(struct root_impl impl) { break; } case Magisk: { + LOGI("[Magisk] Unmounting root\n"); + for (size_t i = mounts.length - 1; i > 0; i--) { struct mountinfo mount = mounts.mounts[i]; if ( - strcmp(mount.source, "Magisk") == 0 || - strcmp(mount.source, "worker") == 0 || - strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) || - strncmp(mount.target, "/data/adb/modules", strlen("/data/adb/modules")) + ( + modules_only && + ( + strncmp(mount.target, "/debug_ramdisk", strlen("/debug_ramdisk")) == 0 || + strcmp(mount.source, "magisk") == 0 || + strncmp(mount.target, "/system/bin", strlen("/system/bin")) == 0 + ) + ) || + ( + !modules_only && + ( + strcmp(mount.source, "magisk") == 0 || + strncmp(mount.root, "/adb/modules", strlen("/adb/modules")) == 0 + ) + ) ) { - mounts_to_unmount = (char **)realloc(mounts_to_unmount, (mounts_to_unmount_len + 1) * sizeof(char *)); - if (!mounts_to_unmount) { - LOGE("Failed to allocate memory for mounts_to_unmount\n"); + if (umount2(mount.target, MNT_DETACH) == -1) { + LOGE("[Magisk] Failed to unmount %s: %s\n", mount.target, strerror(errno)); - free_mounts(&mounts); - - return; + continue; } - mounts_to_unmount[mounts_to_unmount_len] = strdup(mount.target); - if (!mounts_to_unmount[mounts_to_unmount_len]) { - LOGE("Failed to allocate memory for mounts_to_unmount[%zu]\n", mounts_to_unmount_len); - - free_mounts(&mounts); - - return; - } - - mounts_to_unmount_len++; - LOGI("[Magisk] Unmounted %s\n", mount.target); } } } } - for (size_t i = 0; i < mounts_to_unmount_len; i++) { - if (umount2(mounts_to_unmount[i], MNT_DETACH) == -1) { - LOGE("Failed to unmount %s: %s\n", mounts_to_unmount[i], strerror(errno)); - - continue; - } - } - - for (size_t i = 0; i < mounts_to_unmount_len; i++) { - free((void *)mounts_to_unmount[i]); - } - free((void *)mounts_to_unmount); - free_mounts(&mounts); return; } -int get_clean_mns_fd(int pid, struct root_impl impl) { - if (clean_namespace_fd != 0) return clean_namespace_fd; +int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl impl) { + LOGI(" - Saving mount namespace fd for pid %d. State: %d\n", pid, mns_state); + + LOGI(" - Clean namespace fd: %d\n", clean_namespace_fd); + LOGI(" - Rooted namespace fd: %d\n", rooted_namespace_fd); + LOGI(" - Module namespace fd: %d\n", module_namespace_fd); + + if (mns_state == Clean && clean_namespace_fd != 0) return clean_namespace_fd; + if (mns_state == Rooted && rooted_namespace_fd != 0) return rooted_namespace_fd; + if (mns_state == Module && module_namespace_fd != 0) return module_namespace_fd; + + LOGI(" - Creating socketpair\n"); int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) { @@ -718,11 +708,18 @@ int get_clean_mns_fd(int pid, struct root_impl impl) { pid_t fork_pid = fork(); if (fork_pid == 0) { switch_mount_namespace(pid); - unmount_root(impl); - write_uint32_t(writer, 0); - uint32_t _ = 0; - read_uint32_t(reader, &_); + if (mns_state != Rooted) { + unshare(CLONE_NEWNS); + unmount_root(mns_state == Module, impl); + } + + uint32_t mypid = 0; + while (mypid != (uint32_t)getpid()) { + write_uint32_t(writer, 0); + usleep(50); + read_uint32_t(reader, &mypid); + } _exit(0); } else if (fork_pid > 0) { @@ -739,7 +736,7 @@ int get_clean_mns_fd(int pid, struct root_impl impl) { return -1; } - write_uint32_t(writer, 0); + write_uint32_t(writer, (uint32_t)fork_pid); if (close(reader) == -1) { LOGE("Failed to close reader: %s\n", strerror(errno)); @@ -759,7 +756,11 @@ int get_clean_mns_fd(int pid, struct root_impl impl) { return -1; } - return (clean_namespace_fd = ns_fd); + LOGI(" - Forked child exited\n"); + + if (mns_state == Rooted) return (rooted_namespace_fd = ns_fd); + else if (mns_state == Clean) return (clean_namespace_fd = ns_fd); + else if (mns_state == Module) return (module_namespace_fd = ns_fd); } else { LOGE("fork: %s\n", strerror(errno)); diff --git a/zygiskd/src/utils.h b/zygiskd/src/utils.h index 81097bd2..1d8bebb9 100644 --- a/zygiskd/src/utils.h +++ b/zygiskd/src/utils.h @@ -107,6 +107,6 @@ int non_blocking_execv(const char *restrict file, char *const argv[]); void stringify_root_impl_name(struct root_impl impl, char *restrict output); -int get_clean_mns_fd(int pid, struct root_impl impl); +int save_mns_fd(int pid, enum MountNamespaceState mns_state, struct root_impl impl); #endif /* UTILS_H */ diff --git a/zygiskd/src/zygiskd.c b/zygiskd/src/zygiskd.c index 257e50a9..2b813f2f 100644 --- a/zygiskd/src/zygiskd.c +++ b/zygiskd/src/zygiskd.c @@ -507,11 +507,7 @@ void zygiskd_start(char *restrict argv[]) { if (first_process) { flags |= PROCESS_IS_FIRST_STARTED; - if (!uid_is_systemui(uid)) { - LOGI("First process started\n"); - - first_process = false; - } + first_process = false; } else { if (uid_is_manager(uid)) { flags |= PROCESS_IS_MANAGER; @@ -718,11 +714,15 @@ void zygiskd_start(char *restrict argv[]) { ssize_t ret = read_uint32_t(client_fd, (uint32_t *)&pid); ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "pid", ret, sizeof(pid)); + uint8_t mns_state = 0; + ret = read_uint8_t(client_fd, &mns_state); + ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "mns_state", ret, sizeof(mns_state)); + pid_t our_pid = getpid(); ret = write_uint32_t(client_fd, (uint32_t)our_pid); ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "our_pid", ret, sizeof(our_pid)); - uint32_t clean_namespace_fd = (uint32_t)get_clean_mns_fd(pid, impl); + uint32_t clean_namespace_fd = (uint32_t)save_mns_fd(pid, (enum MountNamespaceState)mns_state, impl); ret = write_uint32_t(client_fd, clean_namespace_fd); ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "clean_namespace_fd", ret, sizeof(clean_namespace_fd));