From 55c3ee3a6fb6db2cb6d7a7fd427d898570ee3c92 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 7 Jul 2025 10:58:02 -0700 Subject: [PATCH] Move Zygisk code out of module.cpp --- native/src/core/daemon.cpp | 8 +- native/src/core/daemon.rs | 3 +- native/src/core/include/core.hpp | 6 +- native/src/core/lib.rs | 6 +- native/src/core/module.cpp | 19 +-- native/src/core/module.rs | 191 +++++++++++++++--------------- native/src/core/zygisk/entry.cpp | 25 +++- native/src/core/zygisk/zygisk.hpp | 3 + native/src/include/consts.hpp | 2 - 9 files changed, 135 insertions(+), 128 deletions(-) diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index 9023c6f53..f3c096536 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -218,13 +218,7 @@ static void handle_request_sync(int client, int code) { denylist_handler(-1, nullptr); // Restore native bridge property - auto nb = get_prop(NBPROP); - auto len = sizeof(ZYGISKLDR) - 1; - if (nb == ZYGISKLDR) { - set_prop(NBPROP, "0"); - } else if (nb.size() > len) { - set_prop(NBPROP, nb.data() + len); - } + restore_zygisk_prop(); write_int(client, 0); diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 1e80798a1..f121e9b12 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -145,8 +145,9 @@ impl MagiskD { ); initialize_denylist(); setup_module_mount(); - let modules = self.handle_modules(); + let modules = self.load_modules(); self.module_list.set(modules).ok(); + self.apply_modules(); clean_mounts(); false diff --git a/native/src/core/include/core.hpp b/native/src/core/include/core.hpp index a22526b58..c467abc24 100644 --- a/native/src/core/include/core.hpp +++ b/native/src/core/include/core.hpp @@ -28,14 +28,16 @@ enum class RespondCode : int { struct ModuleInfo; -extern std::string native_bridge; - // Daemon int connect_daemon(int req, bool create = false); const char *get_magisk_tmp(); void unlock_blocks(); bool setup_magisk_env(); bool check_key_combo(); + +// Zygisk daemon +rust::Str get_zygisk_lib_name(); +void set_zygisk_prop(); void restore_zygisk_prop(); // Sockets diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index e1a896c7b..1ca0ec307 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -12,7 +12,6 @@ use cxx::{ExternType, type_id}; use daemon::{MagiskD, daemon_entry}; use derive::Decodable; use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; -use module::load_modules; use mount::{find_preinit_device, revert_unmount}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; use selinux::{lgetfilecon, lsetfilecon, restorecon, setfilecon}; @@ -173,6 +172,7 @@ pub mod ffi { fn uninstall_pkg(apk: Utf8CStrRef); fn update_deny_flags(uid: i32, process: &str, flags: &mut u32); fn initialize_denylist(); + fn get_zygisk_lib_name() -> &'static str; fn restore_zygisk_prop(); fn switch_mnt_ns(pid: i32) -> i32; fn app_request(req: &SuAppRequest) -> i32; @@ -223,8 +223,6 @@ pub mod ffi { #[namespace = "rust"] fn daemon_entry(); - #[namespace = "rust"] - fn load_modules(module_list: &[ModuleInfo], zygisk_name: &str); } // Default constructors @@ -259,7 +257,7 @@ pub mod ffi { fn get() -> &'static MagiskD; } unsafe extern "C++" { - fn handle_modules(self: &MagiskD) -> Vec; + fn load_modules(self: &MagiskD) -> Vec; } } diff --git a/native/src/core/module.cpp b/native/src/core/module.cpp index 16ff711cf..b4bf10761 100644 --- a/native/src/core/module.cpp +++ b/native/src/core/module.cpp @@ -125,7 +125,7 @@ static rust::Vec collect_modules(bool zygisk_enabled, bool open_zygi } return fd; }; - std::for_each(modules.begin(),modules.end(), [&](ModuleInfo &info) { + ranges::for_each(modules, [&](ModuleInfo &info) { info.z32 = convert_to_memfd(info.z32); info.z64 = convert_to_memfd(info.z64); }); @@ -133,28 +133,15 @@ static rust::Vec collect_modules(bool zygisk_enabled, bool open_zygi return modules; } -rust::Vec MagiskD::handle_modules() const noexcept { +rust::Vec MagiskD::load_modules() const noexcept { bool zygisk = zygisk_enabled(); prepare_modules(); exec_module_scripts("post-fs-data", collect_modules(zygisk, false)); // Recollect modules (module scripts could remove itself) auto list = collect_modules(zygisk, true); - if (zygisk) { - string native_bridge_orig = get_prop(NBPROP); - if (native_bridge_orig.empty()) { - native_bridge_orig = "0"; - } - native_bridge = native_bridge_orig != "0" ? ZYGISKLDR + native_bridge_orig : ZYGISKLDR; - set_prop(NBPROP, native_bridge.data()); - // Whether Huawei's Maple compiler is enabled. - // If so, system server will be created by a special Zygote which ignores the native bridge - // and make system server out of our control. Avoid it by disabling. - if (get_prop("ro.maple.enable") == "1") { - set_prop("ro.maple.enable", "0"); - } + set_zygisk_prop(); } - rust::load_modules(rust::Slice(list), zygisk ? native_bridge : ""); return list; } diff --git a/native/src/core/module.rs b/native/src/core/module.rs index 8c4538e90..d930a7a61 100644 --- a/native/src/core/module.rs +++ b/native/src/core/module.rs @@ -1,5 +1,6 @@ use crate::consts::{MODULEMNT, MODULEROOT, WORKERDIR}; -use crate::ffi::{ModuleInfo, get_magisk_tmp}; +use crate::daemon::MagiskD; +use crate::ffi::{get_magisk_tmp, get_zygisk_lib_name}; use crate::load_prop_file; use base::{ Directory, FsPathBuilder, LoggedResult, OsResultStatic, ResultExt, Utf8CStr, Utf8CStrBuf, @@ -487,7 +488,9 @@ fn inject_magisk_bins(system: &mut FsNode) { } } -fn inject_zygisk_bins(system: &mut FsNode, name: &str) { +fn inject_zygisk_bins(system: &mut FsNode) { + let name = get_zygisk_lib_name(); + #[cfg(target_pointer_width = "64")] let has_32_bit = cstr!("/system/bin/linker").exists(); @@ -543,110 +546,112 @@ fn inject_zygisk_bins(system: &mut FsNode, name: &str) { } } -pub fn load_modules(module_list: &[ModuleInfo], zygisk_name: &str) { - let mut system = FsNode::new_dir(); +impl MagiskD { + pub fn apply_modules(&self) { + let mut system = FsNode::new_dir(); - // Build all the base "prefix" paths - let mut root = cstr::buf::default().join_path("/"); + // Build all the base "prefix" paths + let mut root = cstr::buf::default().join_path("/"); - let mut module_dir = cstr::buf::default().join_path(MODULEROOT); + let mut module_dir = cstr::buf::default().join_path(MODULEROOT); - let mut module_mnt = cstr::buf::default() - .join_path(get_magisk_tmp()) - .join_path(MODULEMNT); + let mut module_mnt = cstr::buf::default() + .join_path(get_magisk_tmp()) + .join_path(MODULEMNT); - let mut worker = cstr::buf::default() - .join_path(get_magisk_tmp()) - .join_path(WORKERDIR); + let mut worker = cstr::buf::default() + .join_path(get_magisk_tmp()) + .join_path(WORKERDIR); - // Create a collection of all relevant paths - let mut root_paths = FilePaths { - real: PathTracker::from(&mut root), - worker: PathTracker::from(&mut worker), - module_mnt: PathTracker::from(&mut module_mnt), - module_root: PathTracker::from(&mut module_dir), - }; + // Create a collection of all relevant paths + let mut root_paths = FilePaths { + real: PathTracker::from(&mut root), + worker: PathTracker::from(&mut worker), + module_mnt: PathTracker::from(&mut module_mnt), + module_root: PathTracker::from(&mut module_dir), + }; - // Step 1: Create virtual filesystem tree - // - // In this step, there is zero logic applied during tree construction; we simply collect - // and record the union of all module filesystem trees under each of their /system directory. + // Step 1: Create virtual filesystem tree + // + // In this step, there is zero logic applied during tree construction; we simply collect and + // record the union of all module filesystem trees under each of their /system directory. - for info in module_list { - let mut module_paths = root_paths.append(&info.name); - { - // Read props - let prop = module_paths.append("system.prop"); - if prop.module().exists() { - // Do NOT go through property service as it could cause boot lock - load_prop_file(prop.module(), true); - } - } - { - // Check whether skip mounting - let skip = module_paths.append("skip_mount"); - if skip.module().exists() { - continue; - } - } - { - // Double check whether the system folder exists - let sys = module_paths.append("system"); - if sys.module().exists() { - info!("{}: loading module files", &info.name); - system.collect(sys).log_ok(); - } - } - } - - // Step 2: Inject custom files - // - // Magisk provides some built-in functionality that requires augmenting the filesystem. - // We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library - // has to also be added into the default LD_LIBRARY_PATH for code injection. - // We directly inject file nodes into the virtual filesystem tree we built in the previous - // step, treating Magisk just like a special "module". - - if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") { - inject_magisk_bins(&mut system); - } - if !zygisk_name.is_empty() { - inject_zygisk_bins(&mut system, zygisk_name); - } - - // Step 3: Extract all supported read-only partition roots - // - // For simplicity and backwards compatibility on older Android versions, when constructing - // Magisk modules, we always assume that there is only a single read-only partition mounted - // at /system. However, on modern Android there are actually multiple read-only partitions - // mounted at their respective paths. We need to extract these subtrees out of the main - // tree and treat them as individual trees. - - let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */ - if let FsNode::Directory { children } = &mut system { - for dir in SECONDARY_READ_ONLY_PARTITIONS { - // Only treat these nodes as root iff it is actually a directory in rootdir - if let Ok(attr) = dir.get_attr() - && attr.is_dir() + for info in self.module_list.get().iter().flat_map(|v| v.iter()) { + let mut module_paths = root_paths.append(&info.name); { - let name = dir.trim_start_matches('/'); - if let Some(root) = children.remove(name) { - roots.insert(name, root); + // Read props + let prop = module_paths.append("system.prop"); + if prop.module().exists() { + // Do NOT go through property service as it could cause boot lock + load_prop_file(prop.module(), true); + } + } + { + // Check whether skip mounting + let skip = module_paths.append("skip_mount"); + if skip.module().exists() { + continue; + } + } + { + // Double check whether the system folder exists + let sys = module_paths.append("system"); + if sys.module().exists() { + info!("{}: loading module files", &info.name); + system.collect(sys).log_ok(); } } } - } - roots.insert("system", system); - for (dir, mut root) in roots { - // Step 4: Convert virtual filesystem tree into concrete operations + // Step 2: Inject custom files // - // Compare the virtual filesystem tree we constructed against the real filesystem - // structure on-device to generate a series of "operations". - // The "core" of the logic is to decide which directories need to be rebuilt in the - // tmpfs worker directory, and real sub-nodes need to be mirrored inside it. + // Magisk provides some built-in functionality that requires augmenting the filesystem. + // We expose several cmdline tools (e.g. su) into PATH, and the zygisk shared library + // has to also be added into the default LD_LIBRARY_PATH for code injection. + // We directly inject file nodes into the virtual filesystem tree we built in the previous + // step, treating Magisk just like a special "module". - let path = root_paths.append(dir); - root.commit(path, true).log_ok(); + if get_magisk_tmp() != "/sbin" || get_path_env().split(":").all(|s| s != "/sbin") { + inject_magisk_bins(&mut system); + } + if self.zygisk_enabled() { + inject_zygisk_bins(&mut system); + } + + // Step 3: Extract all supported read-only partition roots + // + // For simplicity and backwards compatibility on older Android versions, when constructing + // Magisk modules, we always assume that there is only a single read-only partition mounted + // at /system. However, on modern Android there are actually multiple read-only partitions + // mounted at their respective paths. We need to extract these subtrees out of the main + // tree and treat them as individual trees. + + let mut roots = BTreeMap::new(); /* mapOf(partition_name -> FsNode) */ + if let FsNode::Directory { children } = &mut system { + for dir in SECONDARY_READ_ONLY_PARTITIONS { + // Only treat these nodes as root iff it is actually a directory in rootdir + if let Ok(attr) = dir.get_attr() + && attr.is_dir() + { + let name = dir.trim_start_matches('/'); + if let Some(root) = children.remove(name) { + roots.insert(name, root); + } + } + } + } + roots.insert("system", system); + + for (dir, mut root) in roots { + // Step 4: Convert virtual filesystem tree into concrete operations + // + // Compare the virtual filesystem tree we constructed against the real filesystem + // structure on-device to generate a series of "operations". + // The "core" of the logic is to decide which directories need to be rebuilt in the + // tmpfs worker directory, and real sub-nodes need to be mirrored inside it. + + let path = root_paths.append(dir); + root.commit(path, true).log_ok(); + } } } diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index a72672b58..7e045e430 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -10,7 +10,7 @@ using namespace std; -string native_bridge = "0"; +static string zygisk_lib_name = "0"; static void zygiskd(int socket) { if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) @@ -110,10 +110,29 @@ extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf { }, }; +rust::Str get_zygisk_lib_name() { + return zygisk_lib_name; +} + +void set_zygisk_prop() { + string native_bridge_orig = get_prop(NBPROP); + if (native_bridge_orig.empty()) { + native_bridge_orig = "0"; + } + zygisk_lib_name = native_bridge_orig == "0" ? ZYGISKLDR : ZYGISKLDR + native_bridge_orig; + set_prop(NBPROP, zygisk_lib_name.data()); + // Whether Huawei's Maple compiler is enabled. + // If so, system server will be created by a special Zygote which ignores the native bridge + // and make system server out of our control. Avoid it by disabling. + if (get_prop("ro.maple.enable") == "1") { + set_prop("ro.maple.enable", "0"); + } +} + void restore_zygisk_prop() { string native_bridge_orig = "0"; - if (native_bridge.length() > strlen(ZYGISKLDR)) { - native_bridge_orig = native_bridge.substr(strlen(ZYGISKLDR)); + if (zygisk_lib_name.length() > strlen(ZYGISKLDR)) { + native_bridge_orig = zygisk_lib_name.substr(strlen(ZYGISKLDR)); } set_prop(NBPROP, native_bridge_orig.data()); } diff --git a/native/src/core/zygisk/zygisk.hpp b/native/src/core/zygisk/zygisk.hpp index edf5199c6..ca273bad4 100644 --- a/native/src/core/zygisk/zygisk.hpp +++ b/native/src/core/zygisk/zygisk.hpp @@ -3,6 +3,9 @@ #include #include +#define ZYGISKLDR "libzygisk.so" +#define NBPROP "ro.dalvik.vm.native.bridge" + #if defined(__LP64__) #define ZLOGD(...) LOGD("zygisk64: " __VA_ARGS__) #define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__) diff --git a/native/src/include/consts.hpp b/native/src/include/consts.hpp index ba836ac94..a94d70db9 100644 --- a/native/src/include/consts.hpp +++ b/native/src/include/consts.hpp @@ -1,8 +1,6 @@ #pragma once #define JAVA_PACKAGE_NAME "com.topjohnwu.magisk" -#define ZYGISKLDR "libzygisk.so" -#define NBPROP "ro.dalvik.vm.native.bridge" #define SECURE_DIR "/data/adb" #define MODULEROOT SECURE_DIR "/modules" #define MODULEUPGRADE SECURE_DIR "/modules_update"