Code reorganization

This commit is contained in:
topjohnwu 2025-01-30 02:13:54 +08:00 committed by John Wu
parent b25aa8295a
commit 7f7f625864
10 changed files with 420 additions and 448 deletions

View File

@ -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 \

View File

@ -1,131 +0,0 @@
#include <sys/mount.h>
#include <sys/wait.h>
#include <sys/sysmacros.h>
#include <linux/input.h>
#include <libgen.h>
#include <set>
#include <string>
#include <consts.hpp>
#include <base.hpp>
#include <core.hpp>
#include <selinux.hpp>
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<int> 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;
}

View File

@ -2,6 +2,8 @@
#include <libgen.h>
#include <sys/un.h>
#include <sys/mount.h>
#include <sys/sysmacros.h>
#include <linux/input.h>
#include <consts.hpp>
#include <base.hpp>
@ -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<int> 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;
}

View File

@ -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<i32>;
fn ipc_read_string(&mut self) -> io::Result<String>;
fn ipc_read_vec<E: Pod>(&mut self) -> io::Result<Vec<E>>;
}
impl<T: Read> IpcRead for T {
fn ipc_read_int(&mut self) -> io::Result<i32> {
let mut val: i32 = 0;
self.read_pod(&mut val)?;
Ok(val)
}
fn ipc_read_string(&mut self) -> io::Result<String> {
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<E: Pod>(&mut self) -> io::Result<Vec<E>> {
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<T: Write> 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<Option<OwnedFd>>;
fn recv_fds(&mut self) -> io::Result<Vec<OwnedFd>>;
}
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<Option<OwnedFd>> {
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<Vec<OwnedFd>> {
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<OwnedFd> = 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<RawFd> {
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) }
}

View File

@ -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;

View File

@ -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;

166
native/src/core/socket.rs Normal file
View File

@ -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<i32>;
fn ipc_read_string(&mut self) -> io::Result<String>;
fn ipc_read_vec<E: Pod>(&mut self) -> io::Result<Vec<E>>;
}
impl<T: Read> IpcRead for T {
fn ipc_read_int(&mut self) -> io::Result<i32> {
let mut val: i32 = 0;
self.read_pod(&mut val)?;
Ok(val)
}
fn ipc_read_string(&mut self) -> io::Result<String> {
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<E: Pod>(&mut self) -> io::Result<Vec<E>> {
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<T: Write> 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<Option<OwnedFd>>;
fn recv_fds(&mut self) -> io::Result<Vec<OwnedFd>>;
}
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<Option<OwnedFd>> {
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<Vec<OwnedFd>> {
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<OwnedFd> = 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<RawFd> {
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) }
}

View File

@ -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();

View File

@ -1,35 +1,116 @@
#include <libgen.h>
#include <dlfcn.h>
#include <sys/prctl.h>
#include <sys/mount.h>
#include <android/log.h>
#include <android/dlext.h>
#include <dlfcn.h>
#include <base.hpp>
#include <consts.hpp>
#include <base.hpp>
#include <core.hpp>
#include <selinux.hpp>
#include "zygisk.hpp"
#include "module.hpp"
using namespace std;
string native_bridge = "0";
static bool is_compatible_with(uint32_t) {
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<comp_entry> 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 = [](auto) {
zygisk_logging();
hook_entry();
ZLOGD("load success\n");
return false;
}
extern "C" [[maybe_unused]] NativeBridgeCallbacks NativeBridgeItf {
.version = 2,
.padding = {},
.isCompatibleWith = &is_compatible_with,
},
};
// The following code runs in magiskd
void restore_zygisk_prop() {
string native_bridge_orig = "0";
if (native_bridge.length() > strlen(ZYGISKLDR)) {

View File

@ -1,98 +0,0 @@
#include <sys/mount.h>
#include <android/dlext.h>
#include <dlfcn.h>
#include <consts.hpp>
#include <base.hpp>
#include <core.hpp>
#include <selinux.hpp>
#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<comp_entry> 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;
}