You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
run companion in standalone process
This commit is contained in:
@@ -18,15 +18,8 @@ int main(int argc, char **argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
} else if (argc >= 3 && argv[1] == "trace"sv) {
|
} else if (argc >= 3 && argv[1] == "trace"sv) {
|
||||||
if (argc >= 4 && argv[3] == "--restart"sv) {
|
if (argc >= 4 && argv[3] == "--restart"sv) {
|
||||||
constexpr auto companion = "./bin/zygisk-cp" LP_SELECT("32", "64");
|
|
||||||
zygiskd::Init(getenv(MAGIC_PATH_ENV));
|
zygiskd::Init(getenv(MAGIC_PATH_ENV));
|
||||||
zygiskd::ZygoteRestart();
|
zygiskd::ZygoteRestart();
|
||||||
if (fork_dont_care() == 0) {
|
|
||||||
LOGI("creating new zygisk companion");
|
|
||||||
execl(companion, basename(companion), nullptr);
|
|
||||||
PLOGE("failed to exec zygisk companion");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
auto pid = strtol(argv[2], 0, 0);
|
auto pid = strtol(argv[2], 0, 0);
|
||||||
if (!trace_zygote(pid)) {
|
if (!trace_zygote(pid)) {
|
||||||
|
|||||||
@@ -24,3 +24,7 @@ if [ "$(which magisk)" ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||||
|
unshare -m sh -c "bin/zygisk-cp64 &"
|
||||||
|
unshare -m sh -c "bin/zygisk-cp32 &"
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
[build]
|
[build]
|
||||||
target-dir = "build/intermediates/rust"
|
target-dir = "build/intermediates/rust"
|
||||||
|
target = "aarch64-linux-android"
|
||||||
|
|||||||
@@ -23,7 +23,14 @@ proc-maps = "0.3"
|
|||||||
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread" ] }
|
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread" ] }
|
||||||
tokio = { version = "1.28", features = ["full"] }
|
tokio = { version = "1.28", features = ["full"] }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
# strip = true
|
||||||
|
# split-debuginfo = "packed"
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
split-debuginfo = "packed"
|
||||||
|
panic = "abort"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
72
zygiskd/src/companion.rs
Normal file
72
zygiskd/src/companion.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use std::ffi::c_void;
|
||||||
|
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::thread;
|
||||||
|
use anyhow::Result;
|
||||||
|
use passfd::FdPassingExt;
|
||||||
|
use rustix::fs::fstat;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use crate::utils::{check_unix_socket, UnixStreamExt};
|
||||||
|
use crate::dl;
|
||||||
|
|
||||||
|
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||||
|
|
||||||
|
pub fn entry(fd: i32) {
|
||||||
|
log::info!("companion entry fd={}", fd);
|
||||||
|
let mut stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||||
|
let name = stream.read_string().expect("read name");
|
||||||
|
let library = stream.recv_fd().expect("receive library fd");
|
||||||
|
let entry = load_module(library).expect("load module");
|
||||||
|
unsafe { libc::close(library) };
|
||||||
|
|
||||||
|
let entry = match entry {
|
||||||
|
Some(entry) => {
|
||||||
|
log::debug!("Companion process created for `{name}`");
|
||||||
|
stream.write_u8(1).expect("reply 1");
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::debug!("No companion entry for `{name}`");
|
||||||
|
stream.write_u8(0).expect("reply 0");
|
||||||
|
return ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if !check_unix_socket(&stream, true) {
|
||||||
|
log::info!("Something bad happened in zygiskd, terminate companion");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
let fd = stream.recv_fd().expect("recv fd");
|
||||||
|
log::trace!("New companion request from module `{name}` fd=`{fd}`");
|
||||||
|
let mut stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||||
|
stream.write_u8(1).expect("reply success");
|
||||||
|
thread::spawn(move || {
|
||||||
|
let st0 = fstat(&stream).expect("failed to stat stream");
|
||||||
|
unsafe { entry(stream.as_raw_fd()); }
|
||||||
|
// 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 let Ok(st1) = fstat(&stream) {
|
||||||
|
if st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino {
|
||||||
|
std::mem::forget(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_module(fd: RawFd) -> Result<Option<ZygiskCompanionEntryFn>> {
|
||||||
|
unsafe {
|
||||||
|
let path = format!("/proc/self/fd/{fd}");
|
||||||
|
let handle = dl::dlopen(&path, libc::RTLD_NOW)?;
|
||||||
|
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
||||||
|
let entry = libc::dlsym(handle, symbol.as_ptr());
|
||||||
|
if entry.is_null() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
||||||
|
Ok(Some(fnptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ mod dl;
|
|||||||
mod root_impl;
|
mod root_impl;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod zygiskd;
|
mod zygiskd;
|
||||||
|
mod companion;
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -15,19 +16,17 @@ fn init_android_logger(tag: &str) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn async_start<F: Future>(future: F) -> F::Output {
|
fn start() {
|
||||||
let async_runtime = tokio::runtime::Runtime::new().unwrap();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
async_runtime.block_on(future)
|
if args.len() == 3 && args[1] == "companion" {
|
||||||
}
|
let fd: i32 = args[2].parse().unwrap();
|
||||||
|
companion::entry(fd);
|
||||||
fn start(name: &str) -> Result<()> {
|
return;
|
||||||
utils::switch_mount_namespace(1)?;
|
|
||||||
root_impl::setup();
|
|
||||||
match name.trim_start_matches("zygisk-") {
|
|
||||||
lp_select!("cp32", "cp64") => zygiskd::main()?,
|
|
||||||
_ => println!("Available command: cp[32|64]"),
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
utils::switch_mount_namespace(1).expect("switch mnt ns");
|
||||||
|
root_impl::setup();
|
||||||
|
zygiskd::main().expect("zygiskd main");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -35,7 +34,5 @@ fn main() {
|
|||||||
let nice_name = process.split('/').last().unwrap();
|
let nice_name = process.split('/').last().unwrap();
|
||||||
init_android_logger(nice_name);
|
init_android_logger(nice_name);
|
||||||
|
|
||||||
if let Err(e) = start(nice_name) {
|
start();
|
||||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
|
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
|
||||||
use std::ffi::{c_char, c_void, CStr, CString};
|
use std::ffi::{c_char, c_void, CStr, CString};
|
||||||
use std::os::fd::AsFd;
|
use std::os::fd::{AsFd, AsRawFd};
|
||||||
use std::os::unix::net::UnixListener;
|
use std::os::unix::net::UnixListener;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
@@ -195,6 +195,22 @@ pub fn unix_listener_from_path(path: &str) -> Result<UnixListener> {
|
|||||||
Ok(UnixListener::from(socket))
|
Ok(UnixListener::from(socket))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_unix_socket(stream: &UnixStream, block: bool) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let mut pfd = libc::pollfd {
|
||||||
|
fd: stream.as_raw_fd(),
|
||||||
|
events: libc::POLLIN,
|
||||||
|
revents: 0,
|
||||||
|
};
|
||||||
|
let timeout = if block { -1 } else { 0 };
|
||||||
|
libc::poll(&mut pfd, 1, timeout);
|
||||||
|
if pfd.revents != 0 && pfd.revents & libc::POLLIN == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
||||||
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
use std::ffi::c_void;
|
|
||||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||||
use crate::utils::UnixStreamExt;
|
use crate::utils::{check_unix_socket, UnixStreamExt};
|
||||||
use crate::{constants, dl, lp_select, root_impl, utils};
|
use crate::{constants, dl, lp_select, root_impl, utils};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use passfd::FdPassingExt;
|
use passfd::FdPassingExt;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::os::fd::OwnedFd;
|
use std::io::Error;
|
||||||
|
use std::os::fd::{OwnedFd, RawFd};
|
||||||
use std::os::unix::{
|
use std::os::unix::{
|
||||||
net::{UnixListener, UnixStream},
|
net::{UnixListener, UnixStream},
|
||||||
prelude::AsRawFd,
|
prelude::AsRawFd,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::{Command, exit};
|
||||||
use log::info;
|
use log::info;
|
||||||
use rustix::fs::fstat;
|
use std::os::unix::process::CommandExt;
|
||||||
use rustix::process::{set_parent_process_death_signal, Signal};
|
use bitflags::Flags;
|
||||||
|
|
||||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||||
|
|
||||||
struct Module {
|
struct Module {
|
||||||
name: String,
|
name: String,
|
||||||
lib_fd: OwnedFd,
|
lib_fd: OwnedFd,
|
||||||
entry: Option<ZygiskCompanionEntryFn>,
|
companion: Mutex<Option<Option<UnixStream>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
@@ -32,7 +32,6 @@ struct Context {
|
|||||||
|
|
||||||
pub fn main() -> Result<()> {
|
pub fn main() -> Result<()> {
|
||||||
log::info!("Start zygisk companion");
|
log::info!("Start zygisk companion");
|
||||||
set_parent_process_death_signal(Some(Signal::Kill))?;
|
|
||||||
|
|
||||||
let arch = get_arch()?;
|
let arch = get_arch()?;
|
||||||
log::debug!("Daemon architecture: {arch}");
|
log::debug!("Daemon architecture: {arch}");
|
||||||
@@ -60,8 +59,11 @@ pub fn main() -> Result<()> {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
DaemonSocketAction::ZygoteRestart => {
|
DaemonSocketAction::ZygoteRestart => {
|
||||||
info!("Zygote restarted, exit");
|
info!("Zygote restarted, clean up companions");
|
||||||
exit(0);
|
for module in &context.modules {
|
||||||
|
let mut companion = module.companion.lock().unwrap();
|
||||||
|
companion.take();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@@ -112,8 +114,8 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let entry = resolve_module(&so_path.to_string_lossy())?;
|
let companion = Mutex::new(None);
|
||||||
let module = Module { name, lib_fd, entry };
|
let module = Module { name, lib_fd, companion };
|
||||||
modules.push(module);
|
modules.push(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,17 +155,43 @@ fn create_daemon_socket() -> Result<UnixListener> {
|
|||||||
Ok(listener)
|
Ok(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_module(path: &str) -> Result<Option<ZygiskCompanionEntryFn>> {
|
fn spawn_companion(name: &str, lib_fd: RawFd) -> Result<Option<UnixStream>> {
|
||||||
|
let (mut daemon, companion) = UnixStream::pair()?;
|
||||||
|
|
||||||
|
let process = std::env::args().next().unwrap();
|
||||||
|
let nice_name = process.split('/').last().unwrap();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let handle = dl::dlopen(path, libc::RTLD_NOW)?;
|
let pid = libc::fork();
|
||||||
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
if pid < 0 {
|
||||||
let entry = libc::dlsym(handle, symbol.as_ptr());
|
bail!(Error::last_os_error());
|
||||||
if entry.is_null() {
|
} else if pid > 0 {
|
||||||
return Ok(None);
|
drop(companion);
|
||||||
|
let mut status: libc::c_int = 0;
|
||||||
|
libc::waitpid(pid, &mut status, 0);
|
||||||
|
if libc::WIFEXITED(status) && libc::WEXITSTATUS(status) == 0 {
|
||||||
|
daemon.write_string(name)?;
|
||||||
|
daemon.send_fd(lib_fd)?;
|
||||||
|
return match daemon.read_u8()? {
|
||||||
|
0 => Ok(None),
|
||||||
|
1 => Ok(Some(daemon)),
|
||||||
|
_ => bail!("Invalid companion response"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("exited with status {}", status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Remove FD_CLOEXEC flag
|
||||||
|
unsafe { libc::fcntl(companion.as_raw_fd() as libc::c_int, libc::F_SETFD, 0i32); };
|
||||||
}
|
}
|
||||||
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
|
||||||
Ok(Some(fnptr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Command::new(&process)
|
||||||
|
.arg0(format!("{}-{}", nice_name, name))
|
||||||
|
.arg("companion")
|
||||||
|
.arg(format!("{}", companion.as_raw_fd()))
|
||||||
|
.spawn()?;
|
||||||
|
exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, context: &Context) -> Result<()> {
|
fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, context: &Context) -> Result<()> {
|
||||||
@@ -207,26 +235,41 @@ fn handle_daemon_action(action: DaemonSocketAction, mut stream: UnixStream, cont
|
|||||||
DaemonSocketAction::RequestCompanionSocket => {
|
DaemonSocketAction::RequestCompanionSocket => {
|
||||||
let index = stream.read_usize()?;
|
let index = stream.read_usize()?;
|
||||||
let module = &context.modules[index];
|
let module = &context.modules[index];
|
||||||
match module.entry {
|
let mut companion = module.companion.lock().unwrap();
|
||||||
None => {
|
if let Some(Some(sock)) = companion.as_ref() {
|
||||||
stream.write_u8(0)?;
|
if !check_unix_socket(sock, false) {
|
||||||
return Ok(());
|
log::error!("Poll companion for module `{}` crashed", module.name);
|
||||||
}
|
companion.take();
|
||||||
Some(companion) => {
|
|
||||||
stream.write_u8(1)?;
|
|
||||||
let st0 = fstat(&stream)?;
|
|
||||||
unsafe { companion(stream.as_raw_fd()); }
|
|
||||||
// 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 let Ok(st1) = fstat(&stream) {
|
|
||||||
if st0.st_dev != st1.st_dev || st0.st_ino != st1.st_ino {
|
|
||||||
std::mem::forget(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if companion.is_none() {
|
||||||
|
match spawn_companion(&module.name, module.lib_fd.as_raw_fd()) {
|
||||||
|
Ok(c) => {
|
||||||
|
if c.is_some() {
|
||||||
|
log::trace!(" Spawned companion for `{}`", module.name);
|
||||||
|
} else {
|
||||||
|
log::trace!(" No companion spawned for `{}` because it has not entry", module.name);
|
||||||
|
}
|
||||||
|
*companion = Some(c);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!(" Failed to spawn companion for `{}`: {}", module.name, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
match companion.as_ref() {
|
||||||
|
Some(Some(sock)) => {
|
||||||
|
if let Err(_) = sock.send_fd(stream.as_raw_fd()) {
|
||||||
|
log::error!("Failed to send companion fd socket of module `{}`", module.name);
|
||||||
|
stream.write_u8(0)?;
|
||||||
|
}
|
||||||
|
// Ok: Send by companion
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
stream.write_u8(0)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("companion done");
|
||||||
}
|
}
|
||||||
DaemonSocketAction::GetModuleDir => {
|
DaemonSocketAction::GetModuleDir => {
|
||||||
let index = stream.read_usize()?;
|
let index = stream.read_usize()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user