From 09b6673ab08737a05bf40dd8039b82dfd1762211 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Tue, 28 Feb 2023 16:00:34 +0800 Subject: [PATCH] Implement revert_unmount_magisk --- loader/src/injector/files.cpp | 122 ++++++++++++++++++++++++++++++++ loader/src/injector/files.hpp | 56 +++++++++++++++ loader/src/injector/hook.cpp | 8 ++- loader/src/injector/misc.cpp | 19 ----- loader/src/injector/misc.hpp | 41 ----------- loader/src/injector/module.hpp | 2 + loader/src/injector/unmount.cpp | 90 +++++++++++++++-------- loader/src/injector/zygisk.hpp | 5 +- zygiskd/src/constants.rs | 2 + zygiskd/src/zygiskd.rs | 5 ++ 10 files changed, 258 insertions(+), 92 deletions(-) create mode 100644 loader/src/injector/files.cpp create mode 100644 loader/src/injector/files.hpp diff --git a/loader/src/injector/files.cpp b/loader/src/injector/files.cpp new file mode 100644 index 0000000..e597295 --- /dev/null +++ b/loader/src/injector/files.cpp @@ -0,0 +1,122 @@ +#include + +#include "files.hpp" +#include "misc.hpp" + +using namespace std::string_view_literals; + +void file_readline(bool trim, FILE *fp, const std::function &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 &fn) { + if (auto fp = open_file(file, "re")) + file_readline(trim, fp.get(), fn); +} +void file_readline(const char *file, const std::function &fn) { + file_readline(false, file, fn); +} + +std::vector parse_mount_info(const char *pid) { + char buf[PATH_MAX] = {}; + snprintf(buf, sizeof(buf), "/proc/%s/mountinfo", pid); + std::vector 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(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; }); +} diff --git a/loader/src/injector/files.hpp b/loader/src/injector/files.hpp new file mode 100644 index 0000000..73bc9e2 --- /dev/null +++ b/loader/src/injector/files.hpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +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 &fn); +void file_readline(bool trim, const char *file, const std::function &fn); +void file_readline(const char *file, const std::function &fn); + +std::vector parse_mount_info(const char *pid); + +using sFILE = std::unique_ptr; +using sDIR = std::unique_ptr; +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)); +} diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 516a782..fff72d4 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -17,7 +17,7 @@ #include "zygisk.hpp" #include "memory.hpp" #include "module.hpp" -#include "misc.hpp" +#include "files.hpp" using namespace std; 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. (g_ctx->info_flags & PROCESS_IS_SYS_UI) == 0) { 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 */ diff --git a/loader/src/injector/misc.cpp b/loader/src/injector/misc.cpp index 0731a65..0881664 100644 --- a/loader/src/injector/misc.cpp +++ b/loader/src/injector/misc.cpp @@ -23,17 +23,6 @@ int parse_int(std::string_view s) { return val; } -void parse_mnt(const char* file, const std::function& fn) { - auto fp = sFILE(setmntent(file, "re"), endmntent); - if (fp) { - mntent mentry{}; - char buf[PATH_MAX]; - while (getmntent_r(fp.get(), &mentry, buf, sizeof(buf))) { - fn(&mentry); - } - } -} - std::list split_str(std::string_view s, std::string_view delimiter) { std::list ret; size_t pos = 0; @@ -58,11 +47,3 @@ std::string join_str(const std::list& list, std::string_view delimi } return ret; } - -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; }); -} diff --git a/loader/src/injector/misc.hpp b/loader/src/injector/misc.hpp index fda3db3..b526145 100644 --- a/loader/src/injector/misc.hpp +++ b/loader/src/injector/misc.hpp @@ -1,12 +1,8 @@ #pragma once -#include -#include #include #include -#include #include -#include #include #include @@ -39,12 +35,6 @@ int new_daemon_thread(thread_entry entry, void *arg); static inline bool str_contains(std::string_view s, std::string_view ss) { return s.find(ss) != std::string_view::npos; } -static inline bool str_starts(std::string_view s, std::string_view ss) { - return s.size() >= ss.size() && s.compare(0, ss.size(), ss) == 0; -} -static inline bool str_ends(std::string_view s, std::string_view ss) { - return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string_view::npos, ss) == 0; -} template class stateless_allocator { @@ -61,35 +51,6 @@ public: bool operator!=(const stateless_allocator&) { return false; } }; -using sFILE = std::unique_ptr; -using sDIR = std::unique_ptr; -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)); -} - template class reversed_container { public: @@ -126,8 +87,6 @@ struct StringCmp { */ int parse_int(std::string_view s); -void parse_mnt(const char* file, const std::function& fn); - std::list split_str(std::string_view s, std::string_view delimiter); std::string join_str(const std::list& list, std::string_view delimiter); diff --git a/loader/src/injector/module.hpp b/loader/src/injector/module.hpp index 77f2987..60b2fa0 100644 --- a/loader/src/injector/module.hpp +++ b/loader/src/injector/module.hpp @@ -111,6 +111,8 @@ namespace { PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT, PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST, + PROCESS_ROOT_IS_KSU = (1u << 29), + PROCESS_ROOT_IS_MAGISK = (1u << 30), PROCESS_IS_SYS_UI = (1u << 31), PRIVATE_MASK = PROCESS_IS_SYS_UI diff --git a/loader/src/injector/unmount.cpp b/loader/src/injector/unmount.cpp index 594aca7..7b893a2 100644 --- a/loader/src/injector/unmount.cpp +++ b/loader/src/injector/unmount.cpp @@ -1,5 +1,7 @@ +#include #include +#include "files.hpp" #include "logging.h" #include "misc.hpp" #include "zygisk.hpp" @@ -7,7 +9,13 @@ using namespace std::string_view_literals; namespace { - constexpr auto KSU_MODULE_DIR = "/data/adb/ksu/modules"sv; + constexpr auto KSU_MODULE_DIR = "/data/adb/ksu/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) { @@ -24,37 +32,44 @@ namespace { return true; \ } -void revert_unmount() { +void revert_unmount_ksu() { std::string ksu_loop; std::vector targets; - std::list> backups; + std::list backups; // Unmount ksu module dir last targets.emplace_back(KSU_MODULE_DIR); - parse_mnt("/proc/self/mounts", [&](mntent* mentry) { - if (mentry->mnt_dir == KSU_MODULE_DIR) { - ksu_loop = mentry->mnt_fsname; - return; + + for (auto& info: parse_mount_info("self")) { + if (info.target == KSU_MODULE_DIR) { + ksu_loop = info.source; + continue; } // Unmount everything on /data/adb except ksu module dir - if (str_starts(mentry->mnt_dir, "/data/adb")) { - targets.emplace_back(mentry->mnt_dir); + if (info.target.starts_with("/data/adb")) { + targets.emplace_back(info.target); } // Unmount ksu overlays - if (mentry->mnt_type == "overlay"sv) { - if (str_contains(mentry->mnt_opts, KSU_MODULE_DIR)) { - targets.emplace_back(mentry->mnt_dir); + if (info.type == "overlay") { + LOGV("Overlay: %s (%s)", info.target.data(), info.fs_option.data()); + if (str_contains(info.fs_option, KSU_MODULE_DIR)) { + targets.emplace_back(info.target); } else { - backups.emplace_back(mentry->mnt_dir, mentry->mnt_opts); + auto backup = overlay_backup{ + .target = info.target, + .vfs_option = info.vfs_option, + .fs_option = info.fs_option, + }; + backups.emplace_back(backup); } } - }); - // Unmount everything from ksu loop except ksu module dir - parse_mnt("/proc/self/mounts", [&](mntent* mentry) { - if (mentry->mnt_fsname == ksu_loop && mentry->mnt_dir != KSU_MODULE_DIR) { - targets.emplace_back(mentry->mnt_dir); + } + for (auto& info: parse_mount_info("self")) { + // Unmount everything from ksu loop except ksu module dir + if (info.source == ksu_loop && info.target != KSU_MODULE_DIR) { + targets.emplace_back(info.target); } - }); + } // Do unmount for (auto& s: reversed(targets)) { @@ -62,18 +77,18 @@ void revert_unmount() { } // Affirm unmounted system overlays - parse_mnt("/proc/self/mounts", [&](mntent* mentry) { - if (mentry->mnt_type == "overlay"sv) { - backups.remove_if([&](auto& mnt) { - return mnt.first == mentry->mnt_dir && mnt.second == mentry->mnt_opts; + 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; }); } - return true; - }); + } // Restore system overlays for (auto& mnt: backups) { - auto opts = split_str(mnt.second, ","); + 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) @@ -82,10 +97,27 @@ void revert_unmount() { return false; }); auto mnt_data = join_str(opts, ","); - if (mount("overlay", mnt.first.data(), "overlay", flags, mnt_data.data()) != -1) { - LOGD("Remounted (%s)", mnt.first.data()); + if (mount("overlay", mnt.target.data(), "overlay", flags, mnt_data.data()) != -1) { + LOGD("Remounted (%s)", mnt.target.data()); } else { - PLOGE("Remount (%s, %s)", mnt.first.data(), mnt_data.data()); + PLOGE("Remount (%s, %s)", mnt.target.data(), mnt.fs_option.data()); } } } + +void revert_unmount_magisk() { + 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); + } + } + + for (auto& s: targets) { + lazy_unmount(s.data()); + } +} diff --git a/loader/src/injector/zygisk.hpp b/loader/src/injector/zygisk.hpp index addfdea..995a89f 100644 --- a/loader/src/injector/zygisk.hpp +++ b/loader/src/injector/zygisk.hpp @@ -8,4 +8,7 @@ extern void *self_handle; void hook_functions(); -void revert_unmount(); +void revert_unmount_ksu(); + +void revert_unmount_magisk(); + diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index e274ba6..f32e68a 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -51,4 +51,6 @@ pub enum DaemonSocketAction { // 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; diff --git a/zygiskd/src/zygiskd.rs b/zygiskd/src/zygiskd.rs index 29dfbe4..9d4271b 100644 --- a/zygiskd/src/zygiskd.rs +++ b/zygiskd/src/zygiskd.rs @@ -201,6 +201,11 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()> 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)?; }