diff --git a/native/src/Android.mk b/native/src/Android.mk index 1159712cf..a9cc53ec4 100644 --- a/native/src/Android.mk +++ b/native/src/Android.mk @@ -18,7 +18,6 @@ LOCAL_SRC_FILES := \ core/applets.cpp \ core/magisk.cpp \ core/daemon.cpp \ - core/bootstages.cpp \ core/socket.cpp \ core/scripting.cpp \ core/selinux.cpp \ @@ -32,7 +31,6 @@ LOCAL_SRC_FILES := \ core/su/pts.cpp \ core/su/su_daemon.cpp \ core/zygisk/entry.cpp \ - core/zygisk/main.cpp \ core/zygisk/module.cpp \ core/zygisk/hook.cpp \ core/deny/cli.cpp \ diff --git a/native/src/core/bootstages.cpp b/native/src/core/bootstages.cpp deleted file mode 100644 index 4d8ca336d..000000000 --- a/native/src/core/bootstages.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace std; - -/********* - * Setup * - *********/ - -bool setup_magisk_env() { - char buf[4096]; - - LOGI("* Initializing Magisk environment\n"); - - ssprintf(buf, sizeof(buf), "%s/0/%s/install", APP_DATA_DIR, JAVA_PACKAGE_NAME); - // Alternative binaries paths - const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf }; - for (auto alt : alt_bin) { - if (access(alt, F_OK) == 0) { - rm_rf(DATABIN); - cp_afc(alt, DATABIN); - rm_rf(alt); - } - } - rm_rf("/cache/data_adb"); - - // Directories in /data/adb - chmod(SECURE_DIR, 0700); - xmkdir(DATABIN, 0755); - xmkdir(MODULEROOT, 0755); - xmkdir(SECURE_DIR "/post-fs-data.d", 0755); - xmkdir(SECURE_DIR "/service.d", 0755); - restorecon(); - - if (access(DATABIN "/busybox", X_OK)) - return false; - - ssprintf(buf, sizeof(buf), "%s/" BBPATH "/busybox", get_magisk_tmp()); - mkdir(dirname(buf), 0755); - cp_afc(DATABIN "/busybox", buf); - exec_command_async(buf, "--install", "-s", dirname(buf)); - - // magisk32 and magiskpolicy are not installed into ramdisk and has to be copied - // from data to magisk tmp - if (access(DATABIN "/magisk32", X_OK) == 0) { - ssprintf(buf, sizeof(buf), "%s/magisk32", get_magisk_tmp()); - cp_afc(DATABIN "/magisk32", buf); - } - if (access(DATABIN "/magiskpolicy", X_OK) == 0) { - ssprintf(buf, sizeof(buf), "%s/magiskpolicy", get_magisk_tmp()); - cp_afc(DATABIN "/magiskpolicy", buf); - } - - return true; -} - -void unlock_blocks() { - int fd, dev, OFF = 0; - - auto dir = xopen_dir("/dev/block"); - if (!dir) - return; - dev = dirfd(dir.get()); - - for (dirent *entry; (entry = readdir(dir.get()));) { - if (entry->d_type == DT_BLK) { - if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0) - continue; - if (ioctl(fd, BLKROSET, &OFF) < 0) - PLOGE("unlock %s", entry->d_name); - close(fd); - } - } -} - -#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8))) - -bool check_key_combo() { - uint8_t bitmask[(KEY_MAX + 1) / 8]; - vector events; - constexpr char name[] = "/dev/.ev"; - - // First collect candidate events that accepts volume down - for (int minor = 64; minor < 96; ++minor) { - if (xmknod(name, S_IFCHR | 0444, makedev(13, minor))) - continue; - int fd = open(name, O_RDONLY | O_CLOEXEC); - unlink(name); - if (fd < 0) - continue; - memset(bitmask, 0, sizeof(bitmask)); - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask); - if (test_bit(KEY_VOLUMEDOWN, bitmask)) - events.push_back(fd); - else - close(fd); - } - if (events.empty()) - return false; - - run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); }); - - // Check if volume down key is held continuously for more than 3 seconds - for (int i = 0; i < 300; ++i) { - bool pressed = false; - for (const int &fd : events) { - memset(bitmask, 0, sizeof(bitmask)); - ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask); - if (test_bit(KEY_VOLUMEDOWN, bitmask)) { - pressed = true; - break; - } - } - if (!pressed) - return false; - // Check every 10ms - usleep(10000); - } - LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n"); - return true; -} diff --git a/native/src/core/daemon.cpp b/native/src/core/daemon.cpp index c84e0e6fd..21f3cd729 100644 --- a/native/src/core/daemon.cpp +++ b/native/src/core/daemon.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -456,3 +458,116 @@ int connect_daemon(int req, bool create) { } return fd; } + +bool setup_magisk_env() { + char buf[4096]; + + LOGI("* Initializing Magisk environment\n"); + + ssprintf(buf, sizeof(buf), "%s/0/%s/install", APP_DATA_DIR, JAVA_PACKAGE_NAME); + // Alternative binaries paths + const char *alt_bin[] = { "/cache/data_adb/magisk", "/data/magisk", buf }; + for (auto alt : alt_bin) { + if (access(alt, F_OK) == 0) { + rm_rf(DATABIN); + cp_afc(alt, DATABIN); + rm_rf(alt); + } + } + rm_rf("/cache/data_adb"); + + // Directories in /data/adb + chmod(SECURE_DIR, 0700); + xmkdir(DATABIN, 0755); + xmkdir(MODULEROOT, 0755); + xmkdir(SECURE_DIR "/post-fs-data.d", 0755); + xmkdir(SECURE_DIR "/service.d", 0755); + restorecon(); + + if (access(DATABIN "/busybox", X_OK)) + return false; + + ssprintf(buf, sizeof(buf), "%s/" BBPATH "/busybox", get_magisk_tmp()); + mkdir(dirname(buf), 0755); + cp_afc(DATABIN "/busybox", buf); + exec_command_async(buf, "--install", "-s", dirname(buf)); + + // magisk32 and magiskpolicy are not installed into ramdisk and has to be copied + // from data to magisk tmp + if (access(DATABIN "/magisk32", X_OK) == 0) { + ssprintf(buf, sizeof(buf), "%s/magisk32", get_magisk_tmp()); + cp_afc(DATABIN "/magisk32", buf); + } + if (access(DATABIN "/magiskpolicy", X_OK) == 0) { + ssprintf(buf, sizeof(buf), "%s/magiskpolicy", get_magisk_tmp()); + cp_afc(DATABIN "/magiskpolicy", buf); + } + + return true; +} + +void unlock_blocks() { + int fd, dev, OFF = 0; + + auto dir = xopen_dir("/dev/block"); + if (!dir) + return; + dev = dirfd(dir.get()); + + for (dirent *entry; (entry = readdir(dir.get()));) { + if (entry->d_type == DT_BLK) { + if ((fd = openat(dev, entry->d_name, O_RDONLY | O_CLOEXEC)) < 0) + continue; + if (ioctl(fd, BLKROSET, &OFF) < 0) + PLOGE("unlock %s", entry->d_name); + close(fd); + } + } +} + +#define test_bit(bit, array) (array[bit / 8] & (1 << (bit % 8))) + +bool check_key_combo() { + uint8_t bitmask[(KEY_MAX + 1) / 8]; + vector events; + constexpr char name[] = "/dev/.ev"; + + // First collect candidate events that accepts volume down + for (int minor = 64; minor < 96; ++minor) { + if (xmknod(name, S_IFCHR | 0444, makedev(13, minor))) + continue; + int fd = open(name, O_RDONLY | O_CLOEXEC); + unlink(name); + if (fd < 0) + continue; + memset(bitmask, 0, sizeof(bitmask)); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(bitmask)), bitmask); + if (test_bit(KEY_VOLUMEDOWN, bitmask)) + events.push_back(fd); + else + close(fd); + } + if (events.empty()) + return false; + + run_finally fin([&]{ std::for_each(events.begin(), events.end(), close); }); + + // Check if volume down key is held continuously for more than 3 seconds + for (int i = 0; i < 300; ++i) { + bool pressed = false; + for (const int &fd : events) { + memset(bitmask, 0, sizeof(bitmask)); + ioctl(fd, EVIOCGKEY(sizeof(bitmask)), bitmask); + if (test_bit(KEY_VOLUMEDOWN, bitmask)) { + pressed = true; + break; + } + } + if (!pressed) + return false; + // Check every 10ms + usleep(10000); + } + LOGD("KEY_VOLUMEDOWN detected: enter safe mode\n"); + return true; +} diff --git a/native/src/core/daemon.rs b/native/src/core/daemon.rs index 033258ac3..98ef22bee 100644 --- a/native/src/core/daemon.rs +++ b/native/src/core/daemon.rs @@ -10,16 +10,12 @@ use crate::mount::setup_mounts; use crate::package::ManagerInfo; use base::libc::{O_CLOEXEC, O_RDONLY}; use base::{ - cstr, error, info, libc, open_fd, warn, BufReadExt, FsPath, FsPathBuf, ReadExt, ResultExt, - Utf8CStr, Utf8CStrBufArr, WriteExt, + cstr, error, info, libc, open_fd, BufReadExt, FsPath, FsPathBuf, ResultExt, Utf8CStr, + Utf8CStrBufArr, }; -use bytemuck::{bytes_of, bytes_of_mut, Pod, Zeroable}; use std::fs::File; -use std::io; -use std::io::{BufReader, ErrorKind, IoSlice, IoSliceMut, Read, Write}; -use std::mem::ManuallyDrop; -use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream}; +use std::io::BufReader; +use std::os::unix::net::UnixStream; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Mutex, OnceLock}; @@ -308,162 +304,3 @@ fn check_data() -> bool { pub fn get_magiskd() -> &'static MagiskD { unsafe { MAGISKD.get().unwrap_unchecked() } } - -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 { - fn ipc_read_int(&mut self) -> io::Result { - let mut val: i32 = 0; - self.read_pod(&mut val)?; - Ok(val) - } - - fn ipc_read_string(&mut self) -> io::Result { - let len = self.ipc_read_int()?; - let mut val = "".to_string(); - 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 { - fn ipc_write_int(&mut self, val: i32) -> io::Result<()>; - fn ipc_write_string(&mut self, val: &str) -> io::Result<()>; -} - -impl IpcWrite for T { - fn ipc_write_int(&mut self, val: i32) -> io::Result<()> { - self.write_pod(&val) - } - - fn ipc_write_string(&mut self, val: &str) -> io::Result<()> { - self.ipc_write_int(val.len() as i32)?; - 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) - } -} - -pub fn send_fd(socket: RawFd, fd: RawFd) -> bool { - let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); - socket.send_fds(&[fd]).log().is_ok() -} - -pub fn send_fds(socket: RawFd, fds: &[RawFd]) -> bool { - let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); - socket.send_fds(fds).log().is_ok() -} - -pub fn recv_fd(socket: RawFd) -> RawFd { - let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); - socket - .recv_fd() - .log() - .unwrap_or(None) - .map_or(-1, IntoRawFd::into_raw_fd) -} - -pub fn recv_fds(socket: RawFd) -> Vec { - let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); - let fds = socket.recv_fds().log().unwrap_or(Vec::new()); - // SAFETY: OwnedFd and RawFd has the same layout - unsafe { std::mem::transmute(fds) } -} diff --git a/native/src/core/db.rs b/native/src/core/db.rs index 9c28423cd..a34ad953d 100644 --- a/native/src/core/db.rs +++ b/native/src/core/db.rs @@ -1,9 +1,10 @@ #![allow(improper_ctypes, improper_ctypes_definitions)] -use crate::daemon::{IpcRead, IpcWrite, MagiskD, MAGISKD}; +use crate::daemon::{MagiskD, MAGISKD}; use crate::ffi::{ open_and_init_db, sqlite3, sqlite3_errstr, DbEntryKey, DbSettings, DbStatement, DbValues, MntNsMode, MultiuserMode, RootAccess, }; +use crate::socket::{IpcRead, IpcWrite}; use base::{LoggedResult, ResultExt, Utf8CStr}; use std::ffi::c_void; use std::fs::File; diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index e6295c2fa..454f5b157 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -7,11 +7,12 @@ #![allow(clippy::missing_safety_doc)] use base::Utf8CStr; -use daemon::{daemon_entry, get_magiskd, recv_fd, recv_fds, send_fd, send_fds, MagiskD}; +use daemon::{daemon_entry, get_magiskd, MagiskD}; use db::get_default_db_settings; use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; use mount::{clean_mounts, find_preinit_device, revert_unmount}; use resetprop::{persist_delete_prop, persist_get_prop, persist_get_props, persist_set_prop}; +use socket::{recv_fd, recv_fds, send_fd, send_fds}; use su::get_default_root_settings; use zygisk::zygisk_should_load_module; @@ -23,6 +24,7 @@ mod logging; mod mount; mod package; mod resetprop; +mod socket; mod su; mod zygisk; diff --git a/native/src/core/socket.rs b/native/src/core/socket.rs new file mode 100644 index 000000000..f61e0664c --- /dev/null +++ b/native/src/core/socket.rs @@ -0,0 +1,166 @@ +use base::{libc, warn, ReadExt, ResultExt, WriteExt}; +use bytemuck::{bytes_of, bytes_of_mut, Pod, Zeroable}; +use std::io; +use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write}; +use std::mem::ManuallyDrop; +use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use std::os::unix::net::{AncillaryData, SocketAncillary, UnixStream}; + +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 { + fn ipc_read_int(&mut self) -> io::Result { + let mut val: i32 = 0; + self.read_pod(&mut val)?; + Ok(val) + } + + fn ipc_read_string(&mut self) -> io::Result { + let len = self.ipc_read_int()?; + let mut val = "".to_string(); + 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 { + fn ipc_write_int(&mut self, val: i32) -> io::Result<()>; + fn ipc_write_string(&mut self, val: &str) -> io::Result<()>; +} + +impl IpcWrite for T { + fn ipc_write_int(&mut self, val: i32) -> io::Result<()> { + self.write_pod(&val) + } + + fn ipc_write_string(&mut self, val: &str) -> io::Result<()> { + self.ipc_write_int(val.len() as i32)?; + 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) + } +} + +pub fn send_fd(socket: RawFd, fd: RawFd) -> bool { + let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); + socket.send_fds(&[fd]).log().is_ok() +} + +pub fn send_fds(socket: RawFd, fds: &[RawFd]) -> bool { + let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); + socket.send_fds(fds).log().is_ok() +} + +pub fn recv_fd(socket: RawFd) -> RawFd { + let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); + socket + .recv_fd() + .log() + .unwrap_or(None) + .map_or(-1, IntoRawFd::into_raw_fd) +} + +pub fn recv_fds(socket: RawFd) -> Vec { + let mut socket = ManuallyDrop::new(unsafe { UnixStream::from_raw_fd(socket) }); + let fds = socket.recv_fds().log().unwrap_or(Vec::new()); + // SAFETY: OwnedFd and RawFd has the same layout + unsafe { std::mem::transmute(fds) } +} diff --git a/native/src/core/zygisk/daemon.rs b/native/src/core/zygisk/daemon.rs index 7a106feb2..77c649619 100644 --- a/native/src/core/zygisk/daemon.rs +++ b/native/src/core/zygisk/daemon.rs @@ -1,8 +1,9 @@ use crate::consts::MODULEROOT; -use crate::daemon::{to_user_id, IpcRead, MagiskD, UnixSocketExt}; +use crate::daemon::{to_user_id, MagiskD}; use crate::ffi::{ get_magisk_tmp, restore_zygisk_prop, update_deny_flags, ZygiskRequest, ZygiskStateFlags, }; +use crate::socket::{IpcRead, UnixSocketExt}; use base::libc::{O_CLOEXEC, O_CREAT, O_RDONLY}; use base::{ cstr, error, fork_dont_care, libc, open_fd, raw_cstr, warn, Directory, FsPathBuf, LoggedError, @@ -21,6 +22,38 @@ pub fn zygisk_should_load_module(flags: u32) -> bool { flags & UNMOUNT_MASK != UNMOUNT_MASK && flags & ZygiskStateFlags::ProcessIsMagiskApp.repr == 0 } +fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) { + // This fd has to survive exec + unsafe { + libc::fcntl(remote.as_raw_fd(), libc::F_SETFD, 0); + } + + // Start building the exec arguments + let mut exe = Utf8CStrBufArr::<64>::new(); + + #[cfg(target_pointer_width = "64")] + let magisk = if is_64_bit { "magisk" } else { "magisk32" }; + + #[cfg(target_pointer_width = "32")] + let magisk = "magisk"; + + let exe = FsPathBuf::new(&mut exe).join(get_magisk_tmp()).join(magisk); + + let mut fd_str = Utf8CStrBufArr::<16>::new(); + write!(fd_str, "{}", remote.as_raw_fd()).ok(); + unsafe { + libc::execl( + exe.as_ptr(), + raw_cstr!(""), + raw_cstr!("zygisk"), + raw_cstr!("companion"), + fd_str.as_ptr(), + ptr::null() as *const libc::c_char, + ); + libc::exit(-1); + } +} + impl MagiskD { pub fn zygisk_handler(&self, client: i32) { let mut client = unsafe { UnixStream::from_raw_fd(client) }; @@ -66,38 +99,6 @@ impl MagiskD { }) } - fn exec_zygiskd(is_64_bit: bool, remote: UnixStream) { - // This fd has to survive exec - unsafe { - libc::fcntl(remote.as_raw_fd(), libc::F_SETFD, 0); - } - - // Start building the exec arguments - let mut exe = Utf8CStrBufArr::<64>::new(); - - #[cfg(target_pointer_width = "64")] - let magisk = if is_64_bit { "magisk" } else { "magisk32" }; - - #[cfg(target_pointer_width = "32")] - let magisk = "magisk"; - - let exe = FsPathBuf::new(&mut exe).join(get_magisk_tmp()).join(magisk); - - let mut fd_str = Utf8CStrBufArr::<16>::new(); - write!(fd_str, "{}", remote.as_raw_fd()).ok(); - unsafe { - libc::execl( - exe.as_ptr(), - raw_cstr!(""), - raw_cstr!("zygisk"), - raw_cstr!("companion"), - fd_str.as_ptr(), - ptr::null() as *const libc::c_char, - ); - libc::exit(-1); - } - } - fn connect_zygiskd(&self, mut client: UnixStream) { let mut zygiskd_sockets = self.zygiskd_sockets.lock().unwrap(); let result: LoggedResult<()> = try { @@ -127,7 +128,7 @@ impl MagiskD { // Create a new socket pair and fork zygiskd process let (local, remote) = UnixStream::pair()?; if fork_dont_care() == 0 { - Self::exec_zygiskd(is_64_bit, remote); + exec_zygiskd(is_64_bit, remote); } *socket = Some(local); let local = socket.as_mut().unwrap(); diff --git a/native/src/core/zygisk/entry.cpp b/native/src/core/zygisk/entry.cpp index 63cb45401..0f57a4216 100644 --- a/native/src/core/zygisk/entry.cpp +++ b/native/src/core/zygisk/entry.cpp @@ -1,35 +1,116 @@ -#include -#include -#include #include -#include #include +#include -#include #include +#include +#include +#include #include "zygisk.hpp" -#include "module.hpp" using namespace std; string native_bridge = "0"; -static bool is_compatible_with(uint32_t) { - zygisk_logging(); - hook_entry(); - ZLOGD("load success\n"); - return false; +static void zygiskd(int socket) { + if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) + exit(-1); + + init_thread_pool(); + +#if defined(__LP64__) + set_nice_name("zygiskd64"); + LOGI("* Launching zygiskd64\n"); +#else + set_nice_name("zygiskd32"); + LOGI("* Launching zygiskd32\n"); +#endif + + // Load modules + using comp_entry = void(*)(int); + vector modules; + { + auto module_fds = recv_fds(socket); + for (int fd : module_fds) { + comp_entry entry = nullptr; + struct stat s{}; + if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) { + android_dlextinfo info { + .flags = ANDROID_DLEXT_USE_LIBRARY_FD, + .library_fd = fd, + }; + if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) { + *(void **) &entry = dlsym(h, "zygisk_companion_entry"); + } else { + LOGW("Failed to dlopen zygisk module: %s\n", dlerror()); + } + } + modules.push_back(entry); + close(fd); + } + } + + // ack + write_int(socket, 0); + + // Start accepting requests + pollfd pfd = { socket, POLLIN, 0 }; + for (;;) { + poll(&pfd, 1, -1); + if (pfd.revents && !(pfd.revents & POLLIN)) { + // Something bad happened in magiskd, terminate zygiskd + exit(0); + } + int client = recv_fd(socket); + if (client < 0) { + // Something bad happened in magiskd, terminate zygiskd + exit(0); + } + int module_id = read_int(client); + if (module_id >= 0 && module_id < modules.size() && modules[module_id]) { + exec_task([=, entry = modules[module_id]] { + struct stat s1; + fstat(client, &s1); + entry(client); + // 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. + if (struct stat s2; fstat(client, &s2) == 0) { + if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { + close(client); + } + } + }); + } else { + close(client); + } + } } +// Entrypoint where we need to re-exec ourselves +// This should only ever be called internally +int zygisk_main(int argc, char *argv[]) { + android_logging(); + if (argc == 3 && argv[1] == "companion"sv) { + zygiskd(parse_int(argv[2])); + } + return 0; +} + +// Entrypoint of code injection extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf { .version = 2, .padding = {}, - .isCompatibleWith = &is_compatible_with, + .isCompatibleWith = [](auto) { + zygisk_logging(); + hook_entry(); + ZLOGD("load success\n"); + return false; + }, }; -// The following code runs in magiskd - void restore_zygisk_prop() { string native_bridge_orig = "0"; if (native_bridge.length() > strlen(ZYGISKLDR)) { diff --git a/native/src/core/zygisk/main.cpp b/native/src/core/zygisk/main.cpp deleted file mode 100644 index ea93bf20b..000000000 --- a/native/src/core/zygisk/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include - -#include "zygisk.hpp" - -using namespace std; - -static void zygiskd(int socket) { - if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) - exit(-1); - - init_thread_pool(); - -#if defined(__LP64__) - set_nice_name("zygiskd64"); - LOGI("* Launching zygiskd64\n"); -#else - set_nice_name("zygiskd32"); - LOGI("* Launching zygiskd32\n"); -#endif - - // Load modules - using comp_entry = void(*)(int); - vector modules; - { - auto module_fds = recv_fds(socket); - for (int fd : module_fds) { - comp_entry entry = nullptr; - struct stat s{}; - if (fstat(fd, &s) == 0 && S_ISREG(s.st_mode)) { - android_dlextinfo info { - .flags = ANDROID_DLEXT_USE_LIBRARY_FD, - .library_fd = fd, - }; - if (void *h = android_dlopen_ext("/jit-cache", RTLD_LAZY, &info)) { - *(void **) &entry = dlsym(h, "zygisk_companion_entry"); - } else { - LOGW("Failed to dlopen zygisk module: %s\n", dlerror()); - } - } - modules.push_back(entry); - close(fd); - } - } - - // ack - write_int(socket, 0); - - // Start accepting requests - pollfd pfd = { socket, POLLIN, 0 }; - for (;;) { - poll(&pfd, 1, -1); - if (pfd.revents && !(pfd.revents & POLLIN)) { - // Something bad happened in magiskd, terminate zygiskd - exit(0); - } - int client = recv_fd(socket); - if (client < 0) { - // Something bad happened in magiskd, terminate zygiskd - exit(0); - } - int module_id = read_int(client); - if (module_id >= 0 && module_id < modules.size() && modules[module_id]) { - exec_task([=, entry = modules[module_id]] { - struct stat s1; - fstat(client, &s1); - entry(client); - // 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. - if (struct stat s2; fstat(client, &s2) == 0) { - if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { - close(client); - } - } - }); - } else { - close(client); - } - } -} - -// Entrypoint where we need to re-exec ourselves -// This should only ever be called internally -int zygisk_main(int argc, char *argv[]) { - android_logging(); - if (argc == 3 && argv[1] == "companion"sv) { - zygiskd(parse_int(argv[2])); - } - return 0; -}