diff --git a/native/src/base/files.rs b/native/src/base/files.rs index 41445cd2e..e50c6fcea 100644 --- a/native/src/base/files.rs +++ b/native/src/base/files.rs @@ -268,7 +268,7 @@ impl DirEntry<'_> { } unsafe fn open_fd(&self, flags: i32) -> io::Result { - self.dir.open_fd(self.d_name(), flags, 0) + self.dir.open_raw_fd(self.d_name(), flags, 0) } pub fn open_as_dir(&self) -> io::Result { @@ -353,10 +353,17 @@ impl Directory { unsafe { libc::rewinddir(self.dirp) } } - unsafe fn open_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result { + unsafe fn open_raw_fd(&self, name: &CStr, flags: i32, mode: i32) -> io::Result { libc::openat(self.as_raw_fd(), name.as_ptr(), flags | O_CLOEXEC, mode).check_os_err() } + pub fn open_fd(&self, name: &Utf8CStr, flags: i32, mode: i32) -> io::Result { + unsafe { + self.open_raw_fd(name.as_cstr(), flags, mode) + .map(|fd| OwnedFd::from_raw_fd(fd)) + } + } + pub fn contains_path(&self, path: &CStr) -> bool { // WARNING: Using faccessat is incorrect, because the raw linux kernel syscall // does not support the flag AT_SYMLINK_NOFOLLOW until 5.8 with faccessat2. @@ -417,7 +424,7 @@ impl Directory { } else if e.is_file() { let mut src = e.open_as_file(O_RDONLY)?; let mut dest = unsafe { - File::from_raw_fd(dir.open_fd( + File::from_raw_fd(dir.open_raw_fd( e.d_name(), O_WRONLY | O_CREAT | O_TRUNC, 0o777, diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index f66f598a3..e4e597fc9 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -163,7 +163,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { break; } case +RequestCode::ZYGISK: - zygisk_handler(client, &cred); + MagiskD().zygisk_handler(client); break; default: __builtin_unreachable(); diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 343536df7..4862feeab 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -6,13 +6,16 @@ use crate::logging::{magisk_logging, start_log_daemon}; use crate::package::ManagerInfo; use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{ - cstr, info, libc, open_fd, BufReadExt, Directory, FsPath, FsPathBuf, LoggedResult, ReadExt, - Utf8CStr, Utf8CStrBufArr, WriteExt, + cstr, info, libc, open_fd, warn, BufReadExt, Directory, FsPath, FsPathBuf, LoggedResult, + ReadExt, Utf8CStr, Utf8CStrBufArr, WriteExt, }; use bit_set::BitSet; +use bytemuck::{bytes_of, bytes_of_mut, Pod, Zeroable}; use std::fs::File; use std::io; -use std::io::{BufReader, Read, Write}; +use std::io::{BufReader, ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::os::fd::{FromRawFd, OwnedFd, RawFd}; +use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream}; use std::sync::{Mutex, OnceLock}; // Global magiskd singleton @@ -59,7 +62,7 @@ pub struct MagiskD { pub sql_connection: Mutex>, pub manager_info: Mutex, boot_stage_lock: Mutex, - module_list: OnceLock>, + pub module_list: OnceLock>, sdk_int: i32, pub is_emulator: bool, is_recovery: bool, @@ -254,6 +257,7 @@ pub fn get_magiskd() -> &'static MagiskD { pub trait IpcRead { fn ipc_read_int(&mut self) -> io::Result; fn ipc_read_string(&mut self) -> io::Result; + fn ipc_read_vec(&mut self) -> io::Result>; } impl IpcRead for T { @@ -269,6 +273,17 @@ impl IpcRead for T { self.take(len as u64).read_to_string(&mut val)?; Ok(val) } + + fn ipc_read_vec(&mut self) -> io::Result> { + let len = self.ipc_read_int()? as usize; + let mut vec = Vec::new(); + let mut val: E = Zeroable::zeroed(); + for _ in 0..len { + self.read_pod(&mut val)?; + vec.push(val.clone()); + } + Ok(vec) + } } pub trait IpcWrite { @@ -286,3 +301,88 @@ impl IpcWrite for T { self.write_all(val.as_bytes()) } } + +pub trait UnixSocketExt { + fn send_fds(&mut self, fd: &[RawFd]) -> io::Result<()>; + fn recv_fd(&mut self) -> io::Result>; + fn recv_fds(&mut self) -> io::Result>; +} + +impl UnixSocketExt for UnixStream { + fn send_fds(&mut self, fds: &[RawFd]) -> io::Result<()> { + match fds.len() { + 0 => self.ipc_write_int(-1)?, + len => { + // 4k buffer is reasonable enough + let mut buf = [0u8; 4096]; + let mut ancillary = SocketAncillary::new(&mut buf); + if !ancillary.add_fds(fds) { + return Err(ErrorKind::OutOfMemory.into()); + } + let fd_count = len as i32; + let iov = IoSlice::new(bytes_of(&fd_count)); + self.send_vectored_with_ancillary(&[iov], &mut ancillary)?; + } + }; + Ok(()) + } + + fn recv_fd(&mut self) -> io::Result> { + let mut fd_count = 0; + self.peek(bytes_of_mut(&mut fd_count))?; + if fd_count < 1 { + return Ok(None); + } + + // 4k buffer is reasonable enough + let mut buf = [0u8; 4096]; + let mut ancillary = SocketAncillary::new(&mut buf); + let iov = IoSliceMut::new(bytes_of_mut(&mut fd_count)); + self.recv_vectored_with_ancillary(&mut [iov], &mut ancillary)?; + for msg in ancillary.messages() { + if let Ok(msg) = msg { + if let AncillaryData::ScmRights(mut scm_rights) = msg { + // We only want the first one + let fd = if let Some(fd) = scm_rights.next() { + unsafe { OwnedFd::from_raw_fd(fd) } + } else { + return Ok(None); + }; + // Close all others + for fd in scm_rights { + unsafe { libc::close(fd) }; + } + return Ok(Some(fd)); + } + } + } + Ok(None) + } + + fn recv_fds(&mut self) -> io::Result> { + let mut fd_count = 0; + // 4k buffer is reasonable enough + let mut buf = [0u8; 4096]; + let mut ancillary = SocketAncillary::new(&mut buf); + let iov = IoSliceMut::new(bytes_of_mut(&mut fd_count)); + self.recv_vectored_with_ancillary(&mut [iov], &mut ancillary)?; + let mut fds: Vec = Vec::new(); + for msg in ancillary.messages() { + if let Ok(msg) = msg { + if let AncillaryData::ScmRights(scm_rights) = msg { + fds = scm_rights + .map(|fd| unsafe { OwnedFd::from_raw_fd(fd) }) + .collect(); + } + } + } + if fd_count as usize != fds.len() { + warn!( + "Received unexpected number of fds: expected={} actual={}", + fd_count, + fds.len() + ); + } + Ok(fds) + } +} diff --git a/native/src/core/deny/utils.cpp b/native/src/core/deny/utils.cpp index 3db579564..6c05408d0 100644 --- a/native/src/core/deny/utils.cpp +++ b/native/src/core/deny/utils.cpp @@ -429,3 +429,12 @@ bool is_deny_target(int uid, string_view process) { } return false; } + +void update_deny_flags(int uid, rust::Str process, uint32_t &flags) { + if (is_deny_target(uid, { process.begin(), process.end() })) { + flags |= +ZygiskStateFlags::ProcessOnDenyList; + } + if (denylist_enforced) { + flags |= +ZygiskStateFlags::DenyListEnforced; + } +} diff --git a/native/src/core/include/core.hpp b/native/src/core/include/core.hpp index 8554a7eae..d058df893 100644 --- a/native/src/core/include/core.hpp +++ b/native/src/core/include/core.hpp @@ -46,7 +46,6 @@ void exec_task(std::function &&task); // Daemon handlers void denylist_handler(int client, const sock_cred *cred); void su_daemon_handler(int client, const sock_cred *cred); -void zygisk_handler(int client, const sock_cred *cred); // Module stuffs void disable_modules(); diff --git a/native/src/core/include/daemon.hpp b/native/src/core/include/daemon.hpp index be73e4b00..7a2cf7c76 100644 --- a/native/src/core/include/daemon.hpp +++ b/native/src/core/include/daemon.hpp @@ -5,6 +5,7 @@ const char *get_magisk_tmp(); void install_apk(rust::Utf8CStr apk); void uninstall_pkg(rust::Utf8CStr pkg); +void update_deny_flags(int uid, rust::Str process, uint32_t &flags); // Rust bindings static inline rust::Utf8CStr get_magisk_tmp_rs() { return get_magisk_tmp(); } diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index de4b74984..6a065f191 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -2,6 +2,8 @@ #![feature(try_blocks)] #![feature(let_chains)] #![feature(fn_traits)] +#![feature(unix_socket_ancillary_data)] +#![feature(unix_socket_peek)] #![allow(clippy::missing_safety_doc)] use base::Utf8CStr; @@ -11,6 +13,7 @@ use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd use mount::{clean_mounts, find_preinit_device, revert_unmount, setup_mounts}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; use su::get_default_root_settings; +use zygisk::zygisk_should_load_module; #[path = "../include/consts.rs"] mod consts; @@ -21,6 +24,7 @@ mod mount; mod package; mod resetprop; mod su; +mod zygisk; #[cxx::bridge] pub mod ffi { @@ -76,7 +80,7 @@ pub mod ffi { fn resolve_preinit_dir(base_dir: Utf8CStrRef) -> String; fn install_apk(apk: Utf8CStrRef); fn uninstall_pkg(apk: Utf8CStrRef); - + fn update_deny_flags(uid: i32, process: &str, flags: &mut u32); fn switch_mnt_ns(pid: i32) -> i32; } @@ -141,6 +145,21 @@ pub mod ffi { z64: i32, } + #[repr(i32)] + enum ZygiskRequest { + GetInfo, + ConnectCompanion, + GetModDir, + } + + #[repr(u32)] + enum ZygiskStateFlags { + ProcessGrantedRoot = 0x00000001, + ProcessOnDenyList = 0x00000002, + DenyListEnforced = 0x40000000, + ProcessIsMagiskApp = 0x80000000, + } + unsafe extern "C++" { include!("include/sqlite.hpp"); @@ -171,6 +190,7 @@ pub mod ffi { fn clean_mounts(); fn find_preinit_device() -> String; fn revert_unmount(pid: i32); + fn zygisk_should_load_module(flags: u32) -> bool; unsafe fn persist_get_prop(name: Utf8CStrRef, prop_cb: Pin<&mut PropCb>); unsafe fn persist_get_props(prop_cb: Pin<&mut PropCb>); unsafe fn persist_delete_prop(name: Utf8CStrRef) -> bool; @@ -186,13 +206,14 @@ pub mod ffi { fn is_recovery(&self) -> bool; fn sdk_int(&self) -> i32; fn boot_stage_handler(&self, client: i32, code: i32); + fn zygisk_handler(&self, client: i32); fn preserve_stub_apk(&self); fn prune_su_access(&self); - fn uid_granted_root(&self, mut uid: i32) -> bool; #[cxx_name = "get_manager"] unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32; fn set_module_list(&self, module_list: Vec); fn module_list(&self) -> &Vec; + fn get_module_fds(&self, is_64_bit: bool) -> Vec; #[cxx_name = "get_db_settings"] fn get_db_settings_for_cxx(&self, cfg: &mut DbSettings) -> bool; @@ -219,6 +240,7 @@ pub mod ffi { fn boot_complete(self: &MagiskD); #[allow(dead_code)] fn handle_modules(self: &MagiskD); + fn connect_zygiskd(self: &MagiskD, client: i32); } } diff --git a/native/src/core/package.rs b/native/src/core/package.rs index 461d2a393..6eef08f9b 100644 --- a/native/src/core/package.rs +++ b/native/src/core/package.rs @@ -473,6 +473,12 @@ impl MagiskD { apk.remove().log().ok(); } + pub fn get_manager_uid(&self, user: i32) -> i32 { + let mut info = self.manager_info.lock().unwrap(); + let (uid, _) = info.get_manager(self, user, false); + uid + } + pub unsafe fn get_manager_for_cxx(&self, user: i32, ptr: *mut CxxString, install: bool) -> i32 { let mut info = self.manager_info.lock().unwrap(); let (uid, pkg) = info.get_manager(self, user, install); diff --git a/native/src/core/zygisk/daemon.rs b/native/src/core/zygisk/daemon.rs new file mode 100644 index 000000000..fdeb44290 --- /dev/null +++ b/native/src/core/zygisk/daemon.rs @@ -0,0 +1,101 @@ +use crate::consts::MODULEROOT; +use crate::daemon::{to_user_id, IpcRead, MagiskD, UnixSocketExt}; +use crate::ffi::{update_deny_flags, ZygiskRequest, ZygiskStateFlags}; +use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY}; +use base::{cstr, open_fd, Directory, FsPathBuf, LoggedResult, Utf8CStrBufArr, WriteExt}; +use std::os::fd::{AsRawFd, FromRawFd, RawFd}; +use std::os::unix::net::UnixStream; + +const UNMOUNT_MASK: u32 = + ZygiskStateFlags::ProcessOnDenyList.repr | ZygiskStateFlags::DenyListEnforced.repr; + +pub fn zygisk_should_load_module(flags: u32) -> bool { + flags & UNMOUNT_MASK != UNMOUNT_MASK && flags & ZygiskStateFlags::ProcessIsMagiskApp.repr == 0 +} + +impl MagiskD { + pub fn zygisk_handler(&self, client: i32) { + let mut client = unsafe { UnixStream::from_raw_fd(client) }; + let _: LoggedResult<()> = try { + let code = ZygiskRequest { + repr: client.ipc_read_int()?, + }; + match code { + ZygiskRequest::GetInfo => self.get_process_info(client)?, + ZygiskRequest::ConnectCompanion => self.connect_zygiskd(client.as_raw_fd()), + ZygiskRequest::GetModDir => self.get_mod_dir(client)?, + _ => {} + } + }; + } + + pub fn get_module_fds(&self, is_64_bit: bool) -> Vec { + if let Some(module_list) = self.module_list.get() { + module_list + .iter() + .map(|m| if is_64_bit { m.z64 } else { m.z32 }) + .collect() + } else { + Vec::new() + } + } + + fn get_process_info(&self, mut client: UnixStream) -> LoggedResult<()> { + let uid = client.ipc_read_int()?; + let process = client.ipc_read_string()?; + let is_64_bit = client.ipc_read_int()? != 0; + let mut flags: u32 = 0; + update_deny_flags(uid, &process, &mut flags); + if self.get_manager_uid(to_user_id(uid)) == uid { + flags |= ZygiskStateFlags::ProcessIsMagiskApp.repr + } + if self.uid_granted_root(uid) { + flags |= ZygiskStateFlags::ProcessGrantedRoot.repr + } + + // First send flags + client.write_pod(&flags)?; + + // Next send modules + if zygisk_should_load_module(flags) { + if let Some(module_list) = self.module_list.get() { + let module_fds: Vec = module_list + .iter() + .map(|m| if is_64_bit { m.z64 } else { m.z32 }) + .collect(); + client.send_fds(&module_fds)?; + } + } + + // If we're not in system_server, we are done + if uid != 1000 || process != "system_server" { + return Ok(()); + } + + // Read all failed modules + let failed_ids: Vec = client.ipc_read_vec()?; + if let Some(module_list) = self.module_list.get() { + for id in failed_ids { + let mut buf = Utf8CStrBufArr::default(); + let path = FsPathBuf::new(&mut buf) + .join(MODULEROOT) + .join(&module_list[id as usize].name) + .join("zygisk"); + // Create the unloaded marker file + Directory::open(&path)?.open_fd(cstr!("unloaded"), O_CREAT | O_RDONLY, 0o644)?; + } + } + + Ok(()) + } + + fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> { + let id = client.ipc_read_int()?; + let module = &self.module_list.get().unwrap()[id as usize]; + let mut buf = Utf8CStrBufArr::default(); + let dir = FsPathBuf::new(&mut buf).join(MODULEROOT).join(&module.name); + let fd = open_fd!(&dir, O_RDONLY | O_CLOEXEC)?; + client.send_fds(&[fd.as_raw_fd()])?; + Ok(()) + } +} diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index fbad3069e..b0fd044b9 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -15,8 +15,6 @@ using namespace std; string native_bridge = "0"; -using modules_t = const rust::Vec&; - static bool is_compatible_with(uint32_t) { zygisk_logging(); hook_entry(); @@ -30,56 +28,13 @@ extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf { .isCompatibleWith = &is_compatible_with, }; -// 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; -} - -int remote_get_info(int uid, const char *process, uint32_t *flags, vector &fds) { - if (int fd = zygisk_request(ZygiskRequest::GET_INFO); fd >= 0) { - write_int(fd, uid); - write_string(fd, process); -#ifdef __LP64__ - write_int(fd, 1); -#else - write_int(fd, 0); -#endif - xxread(fd, flags, sizeof(*flags)); - if (should_load_modules(*flags)) { - fds = recv_fds(fd); - } - return fd; - } - return -1; -} - // The following code runs in magiskd -static vector get_module_fds(bool is_64_bit, modules_t module_list) { - vector fds; - // All fds passed to send_fds have to be valid file descriptors. - // To workaround this issue, send over STDOUT_FILENO as an indicator of an - // invalid fd as it will always be /dev/null in magiskd -#if defined(__LP64__) - if (is_64_bit) { - std::transform(module_list.begin(), module_list.end(), std::back_inserter(fds), - [](const ModuleInfo &info) { return info.z64 < 0 ? STDOUT_FILENO : info.z64; }); - } else -#endif - { - std::transform(module_list.begin(), module_list.end(), std::back_inserter(fds), - [](const ModuleInfo &info) { return info.z32 < 0 ? STDOUT_FILENO : info.z32; }); - } - return fds; -} - static pthread_mutex_t zygiskd_lock = PTHREAD_MUTEX_INITIALIZER; static int zygiskd_sockets[] = { -1, -1 }; #define zygiskd_socket zygiskd_sockets[is_64_bit] -static void connect_companion(int client, modules_t module_list) { +void MagiskD::connect_zygiskd(int client) const noexcept { mutex_guard g(zygiskd_lock); bool is_64_bit = read_int(client); @@ -112,7 +67,7 @@ static void connect_companion(int client, modules_t module_list) { exit(-1); } close(fds[1]); - vector module_fds = get_module_fds(is_64_bit, module_list); + rust::Vec module_fds = get_module_fds(is_64_bit); send_fds(zygiskd_socket, module_fds.data(), module_fds.size()); // Wait for ack if (read_int(zygiskd_socket) != 0) { @@ -123,89 +78,6 @@ static void connect_companion(int client, modules_t module_list) { send_fd(zygiskd_socket, client); } -static void get_process_info(int client, const sock_cred *cred, modules_t module_list) { - int uid = read_int(client); - string process = read_string(client); - int arch = read_int(client); - auto &daemon = MagiskD(); - - uint32_t flags = 0; - - if (is_deny_target(uid, process)) { - flags |= PROCESS_ON_DENYLIST; - } - if (daemon.get_manager(to_user_id(uid), nullptr, false) == uid) { - flags |= PROCESS_IS_MAGISK_APP; - } - if (denylist_enforced) { - flags |= DENYLIST_ENFORCING; - } - if (daemon.uid_granted_root(uid)) { - flags |= PROCESS_GRANTED_ROOT; - } - - xwrite(client, &flags, sizeof(flags)); - - if (should_load_modules(flags)) { - vector fds = get_module_fds(arch, module_list); - send_fds(client, fds.data(), fds.size()); - } - - if (uid != 1000 || process != "system_server") - return; - - // Collect module status from system_server - int slots = read_int(client); - dynamic_bitset bits; - for (int i = 0; i < slots; ++i) { - dynamic_bitset::slot_type l = 0; - xxread(client, &l, sizeof(l)); - bits.emplace_back(l); - } - for (int id = 0; id < module_list.size(); ++id) { - if (!as_const(bits)[id]) { - // Either not a zygisk module, or incompatible - char buf[4096]; - ssprintf(buf, sizeof(buf), MODULEROOT "/%s/zygisk", - module_list[id].name.data()); - if (int dirfd = open(buf, O_RDONLY | O_CLOEXEC); dirfd >= 0) { - close(xopenat(dirfd, "unloaded", O_CREAT | O_RDONLY, 0644)); - close(dirfd); - } - } - } -} - -static void get_moddir(int client, modules_t module_list) { - int id = read_int(client); - char buf[4096]; - auto &m = module_list[id]; - ssprintf(buf, sizeof(buf), MODULEROOT "/%.*s", (int) m.name.size(), m.name.data()); - int dfd = xopen(buf, O_RDONLY | O_CLOEXEC); - send_fd(client, dfd); - close(dfd); -} - -void zygisk_handler(int client, const sock_cred *cred) { - auto &module_list = MagiskD().module_list(); - int code = read_int(client); - switch (code) { - case ZygiskRequest::GET_INFO: - get_process_info(client, cred, module_list); - break; - case ZygiskRequest::CONNECT_COMPANION: - connect_companion(client, module_list); - break; - case ZygiskRequest::GET_MODDIR: - get_moddir(client, module_list); - break; - default: - // Unknown code - break; - } - close(client); -} - void reset_zygisk(bool restore) { if (!zygisk_enabled) return; static atomic_uint zygote_start_count{1}; diff --git a/native/src/core/zygisk/mod.rs b/native/src/core/zygisk/mod.rs new file mode 100644 index 000000000..aca428a64 --- /dev/null +++ b/native/src/core/zygisk/mod.rs @@ -0,0 +1,3 @@ +mod daemon; + +pub use daemon::zygisk_should_load_module; diff --git a/native/src/core/zygisk/module.cpp b/native/src/core/zygisk/module.cpp index 5e26204f9..dc5ad1fa8 100644 --- a/native/src/core/zygisk/module.cpp +++ b/native/src/core/zygisk/module.cpp @@ -77,7 +77,7 @@ bool ZygiskModule::valid() const { } int ZygiskModule::connectCompanion() const { - if (int fd = zygisk_request(ZygiskRequest::CONNECT_COMPANION); fd >= 0) { + if (int fd = zygisk_request(+ZygiskRequest::ConnectCompanion); fd >= 0) { #ifdef __LP64__ write_int(fd, 1); #else @@ -90,11 +90,9 @@ int ZygiskModule::connectCompanion() const { } int ZygiskModule::getModuleDir() const { - if (int fd = zygisk_request(ZygiskRequest::GET_MODDIR); fd >= 0) { + if (owned_fd fd = zygisk_request(+ZygiskRequest::GetModDir); fd >= 0) { write_int(fd, id); - int dfd = recv_fd(fd); - close(fd); - return dfd; + return recv_fd(fd); } return -1; } @@ -210,6 +208,24 @@ bool ZygiskContext::plt_hook_commit() { // ----------------------------------------------------------------- +int ZygiskContext::get_module_info(int uid, std::vector &fds) { + if (int fd = zygisk_request(+ZygiskRequest::GetInfo); fd >= 0) { + write_int(fd, uid); + write_string(fd, process); +#ifdef __LP64__ + write_int(fd, 1); +#else + write_int(fd, 0); +#endif + xxread(fd, &info_flags, sizeof(info_flags)); + if (zygisk_should_load_module(info_flags)) { + fds = recv_fds(fd); + } + return fd; + } + return -1; +} + void ZygiskContext::sanitize_fds() { zygisk_close_logd(); @@ -315,16 +331,17 @@ void ZygiskContext::fork_post() { sigmask(SIG_UNBLOCK, SIGCHLD); } -void ZygiskContext::run_modules_pre(const vector &fds) { +void ZygiskContext::run_modules_pre(vector &fds) { for (int i = 0; i < fds.size(); ++i) { + owned_fd fd = fds[i]; struct stat s{}; - if (fstat(fds[i], &s) != 0 || !S_ISREG(s.st_mode)) { - close(fds[i]); + if (fstat(fd, &s) != 0 || !S_ISREG(s.st_mode)) { + fds[i] = -1; continue; } android_dlextinfo info { .flags = ANDROID_DLEXT_USE_LIBRARY_FD, - .library_fd = fds[i], + .library_fd = fd, }; if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) { if (void *e = dlsym(h, "zygisk_module_entry")) { @@ -332,8 +349,8 @@ void ZygiskContext::run_modules_pre(const vector &fds) { } } else if (flags & SERVER_FORK_AND_SPECIALIZE) { ZLOGW("Failed to dlopen zygisk module: %s\n", dlerror()); + fds[i] = -1; } - close(fds[i]); } for (auto it = modules.begin(); it != modules.end();) { @@ -370,19 +387,18 @@ void ZygiskContext::app_specialize_pre() { flags |= APP_SPECIALIZE; vector module_fds; - int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds); + owned_fd fd = get_module_info(args.app->uid, module_fds); if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) { ZLOGI("[%s] is on the denylist\n", process); flags |= DO_REVERT_UNMOUNT; } else if (fd >= 0) { run_modules_pre(module_fds); } - close(fd); } void ZygiskContext::app_specialize_post() { run_modules_post(); - if (info_flags & PROCESS_IS_MAGISK_APP) { + if (info_flags & +ZygiskStateFlags::ProcessIsMagiskApp) { setenv("ZYGISK_ENABLED", "1", 1); } @@ -392,24 +408,21 @@ void ZygiskContext::app_specialize_post() { void ZygiskContext::server_specialize_pre() { vector module_fds; - int fd = remote_get_info(1000, "system_server", &info_flags, module_fds); - if (fd >= 0) { + if (owned_fd fd = get_module_info(1000, module_fds); fd >= 0) { if (module_fds.empty()) { write_int(fd, 0); } else { run_modules_pre(module_fds); - // Send the bitset of module status back to magiskd from system_server - dynamic_bitset bits; - for (const auto &m : modules) - bits[m.getId()] = true; - write_int(fd, static_cast(bits.slots())); - for (int i = 0; i < bits.slots(); ++i) { - auto l = bits.get_slot(i); - xwrite(fd, &l, sizeof(l)); + // Find all failed module ids and send it back to magiskd + vector failed_ids; + for (int i = 0; i < module_fds.size(); ++i) { + if (module_fds[i] < 0) { + failed_ids.push_back(i); + } } + write_vector(fd, failed_ids); } - close(fd); } } @@ -435,6 +448,7 @@ void ZygiskContext::nativeSpecializeAppProcess_post() { void ZygiskContext::nativeForkSystemServer_pre() { ZLOGV("pre forkSystemServer\n"); flags |= SERVER_FORK_AND_SPECIALIZE; + process = "system_server"; fork_pre(); if (is_child()) { diff --git a/native/src/core/zygisk/module.hpp b/native/src/core/zygisk/module.hpp index fcc292abe..f7bfb4954 100644 --- a/native/src/core/zygisk/module.hpp +++ b/native/src/core/zygisk/module.hpp @@ -122,15 +122,13 @@ struct module_abi_v1 { void (*postServerSpecialize)(void *, const void *); }; +// Assert the flag values to be the same as the public API +static_assert(+ZygiskStateFlags::ProcessGrantedRoot == zygisk::StateFlag::PROCESS_GRANTED_ROOT); +static_assert(+ZygiskStateFlags::ProcessOnDenyList == zygisk::StateFlag::PROCESS_ON_DENYLIST); + enum : uint32_t { - PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT, - PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST, - - DENYLIST_ENFORCING = (1u << 30), - PROCESS_IS_MAGISK_APP = (1u << 31), - - UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING), - PRIVATE_MASK = (DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP) + UNMOUNT_MASK = (+ZygiskStateFlags::ProcessOnDenyList | +ZygiskStateFlags::DenyListEnforced), + PRIVATE_MASK = (+ZygiskStateFlags::DenyListEnforced | +ZygiskStateFlags::ProcessIsMagiskApp) }; struct api_abi_base { @@ -188,7 +186,6 @@ struct ZygiskModule { static uint32_t getFlags(); void tryUnload() const; void clearApi() { memset(&api, 0, sizeof(api)); } - int getId() const { return id; } ZygiskModule(int id, void *handle, void *entry); @@ -264,7 +261,7 @@ struct ZygiskContext { ZygiskContext(JNIEnv *env, void *args); ~ZygiskContext(); - void run_modules_pre(const std::vector &fds); + void run_modules_pre(std::vector &fds); void run_modules_post(); DCL_PRE_POST(fork) DCL_PRE_POST(app_specialize) @@ -273,6 +270,7 @@ struct ZygiskContext { DCL_PRE_POST(nativeSpecializeAppProcess) DCL_PRE_POST(nativeForkSystemServer) + int get_module_info(int uid, std::vector &fds); void sanitize_fds(); bool exempt_fd(int fd); bool can_exempt_fd() const; diff --git a/native/src/core/zygisk/zygisk.hpp b/native/src/core/zygisk/zygisk.hpp index 939a05fe2..4cfad8fd4 100644 --- a/native/src/core/zygisk/zygisk.hpp +++ b/native/src/core/zygisk/zygisk.hpp @@ -6,15 +6,6 @@ #include #include -namespace ZygiskRequest { -enum : int { - GET_INFO, - CONNECT_COMPANION, - GET_MODDIR, - END -}; -} - #if defined(__LP64__) #define ZLOGD(...) LOGD("zygisk64: " __VA_ARGS__) #define ZLOGE(...) LOGE("zygisk64: " __VA_ARGS__) @@ -34,8 +25,6 @@ enum : int { void hook_entry(); void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods); -int remote_get_info(int uid, const char *process, uint32_t *flags, std::vector &fds); - inline int zygisk_request(int req) { int fd = connect_daemon(+RequestCode::ZYGISK); if (fd < 0) return fd;