Fix zygote restart & Show zygisksu status on module.prop

This commit is contained in:
Nullptr
2023-02-26 11:54:52 +08:00
parent ec8475bca5
commit 8affc8f991
9 changed files with 175 additions and 52 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ edition = "2021"
rust-version = "1.67" rust-version = "1.67"
[dependencies] [dependencies]
android_logger = "0.12.0" android_logger = "0.13.0"
anyhow = { version = "1.0.68", features = ["backtrace"] } anyhow = { version = "1.0.68", features = ["backtrace"] }
clap = { version = "4.1.4", features = ["derive"] } clap = { version = "4.1.4", features = ["derive"] }
const_format = "0.2.5" const_format = "0.2.5"
+12 -13
View File
@@ -16,26 +16,25 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info; pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
#[cfg(target_pointer_width = "64")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp64 };
}
#[cfg(target_pointer_width = "32")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp32 };
}
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge"; pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
pub const PROP_SVC_ZYGOTE: &str = "init.svc.zygote"; pub const PROP_CTL_RESTART: &str = "ctl.restart";
pub const ZYGISK_LOADER: &str = "libzygiskloader.so"; pub const ZYGISK_LOADER: &str = "libzygiskloader.so";
pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder"; pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder";
pub const PATH_MODULE_DIR: &str = ".."; pub const PATH_MODULES_DIR: &str = "..";
pub const PATH_MODULE_PROP: &str = "module.prop";
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32"; pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64"; pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
pub const PATH_TMP_DIR: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER);
pub const PATH_TMP_PROP: &str = concatcp!("/dev/", SOCKET_PLACEHOLDER, "/module.prop");
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
pub const STATUS_ROOT_IMPL_NONE: &str = "❌ Unknown root implementation";
pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too old";
pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version";
pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations installed";
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
+1 -1
View File
@@ -38,7 +38,7 @@ fn init_android_logger(tag: &str) {
} }
fn start() -> Result<()> { fn start() -> Result<()> {
root_impl::setup()?; root_impl::setup();
let cli = Args::parse(); let cli = Args::parse();
match cli.command { match cli.command {
Commands::Watchdog => watchdog::entry()?, Commands::Watchdog => watchdog::entry()?,
+12 -7
View File
@@ -1,4 +1,3 @@
use anyhow::{bail, Result};
use nix::libc::prctl; use nix::libc::prctl;
use crate::constants::{MIN_KSU_VERSION, MAX_KSU_VERSION}; use crate::constants::{MIN_KSU_VERSION, MAX_KSU_VERSION};
@@ -8,14 +7,20 @@ const CMD_GET_VERSION: usize = 2;
const CMD_GET_ALLOW_LIST: usize = 5; const CMD_GET_ALLOW_LIST: usize = 5;
const CMD_GET_DENY_LIST: usize = 6; const CMD_GET_DENY_LIST: usize = 6;
pub fn is_kernel_su() -> Result<bool> { pub enum Version {
Supported,
TooOld,
Abnormal,
}
pub fn get_kernel_su() -> Option<Version> {
let mut version = 0; let mut version = 0;
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) }; unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
return match version { match version {
0 => Ok(false), 0 => None,
MIN_KSU_VERSION..=MAX_KSU_VERSION => Ok(true), MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
1..=MIN_KSU_VERSION => bail!("KernelSU version too old: {}", version), 1..=MIN_KSU_VERSION => Some(Version::TooOld),
_ => bail!("KernelSU version abnormal: {}", version) _ => Some(Version::Abnormal)
} }
} }
+12 -8
View File
@@ -1,8 +1,12 @@
use anyhow::{bail, Result};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use crate::constants::MIN_MAGISK_VERSION; use crate::constants::MIN_MAGISK_VERSION;
pub fn is_magisk() -> Result<bool> { pub enum Version {
Supported,
TooOld,
}
pub fn get_magisk() -> Option<Version> {
let version: Option<i32> = Command::new("magisk") let version: Option<i32> = Command::new("magisk")
.arg("-V") .arg("-V")
.stdout(Stdio::piped()) .stdout(Stdio::piped())
@@ -10,13 +14,13 @@ pub fn is_magisk() -> Result<bool> {
.and_then(|child| child.wait_with_output().ok()) .and_then(|child| child.wait_with_output().ok())
.and_then(|output| String::from_utf8(output.stdout).ok()) .and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|output| output.trim().parse().ok()); .and_then(|output| output.trim().parse().ok());
if let Some(version) = version { version.map(|version| {
if version < MIN_MAGISK_VERSION { if version >= MIN_MAGISK_VERSION {
bail!("Magisk version too old: {}", version); Version::Supported
} else {
Version::TooOld
} }
return Ok(true); })
}
Ok(false)
} }
pub fn uid_on_allowlist(uid: i32) -> bool { pub fn uid_on_allowlist(uid: i32) -> bool {
+33 -13
View File
@@ -2,33 +2,52 @@ mod kernelsu;
mod magisk; mod magisk;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use anyhow::{bail, Result};
enum RootImpl { pub enum RootImpl {
None,
TooOld,
Abnormal,
Multiple,
KernelSU, KernelSU,
Magisk, Magisk,
} }
static ROOT_IMPL: OnceCell<RootImpl> = OnceCell::new(); static ROOT_IMPL: OnceCell<RootImpl> = OnceCell::new();
pub fn setup() -> Result<()> { pub fn setup() {
if kernelsu::is_kernel_su()? { let ksu_version = kernelsu::get_kernel_su();
if let Ok(true) = magisk::is_magisk() { let magisk_version = magisk::get_magisk();
bail!("Multiple root implementation");
let _ = match (ksu_version, magisk_version) {
(None, None) => ROOT_IMPL.set(RootImpl::None),
(Some(_), Some(_)) => ROOT_IMPL.set(RootImpl::Multiple),
(Some(ksu_version), None) => {
let val = match ksu_version {
kernelsu::Version::Supported => RootImpl::KernelSU,
kernelsu::Version::TooOld => RootImpl::TooOld,
kernelsu::Version::Abnormal => RootImpl::Abnormal,
};
ROOT_IMPL.set(val)
} }
let _ = ROOT_IMPL.set(RootImpl::KernelSU); (None, Some(magisk_version)) => {
} else if magisk::is_magisk()? { let val = match magisk_version {
let _ = ROOT_IMPL.set(RootImpl::Magisk); magisk::Version::Supported => RootImpl::Magisk,
} else { magisk::Version::TooOld => RootImpl::TooOld,
bail!("Unknown root implementation"); };
} ROOT_IMPL.set(val)
Ok(()) }
};
}
pub fn get_impl() -> &'static RootImpl {
ROOT_IMPL.get().unwrap()
} }
pub fn uid_on_allowlist(uid: i32) -> bool { pub fn uid_on_allowlist(uid: i32) -> bool {
match ROOT_IMPL.get().unwrap() { match ROOT_IMPL.get().unwrap() {
RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid), RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid),
RootImpl::Magisk => magisk::uid_on_allowlist(uid), RootImpl::Magisk => magisk::uid_on_allowlist(uid),
_ => unreachable!(),
} }
} }
@@ -36,5 +55,6 @@ pub fn uid_on_denylist(uid: i32) -> bool {
match ROOT_IMPL.get().unwrap() { match ROOT_IMPL.get().unwrap() {
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid), RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
RootImpl::Magisk => magisk::uid_on_denylist(uid), RootImpl::Magisk => magisk::uid_on_denylist(uid),
_ => unreachable!(),
} }
} }
+11
View File
@@ -6,6 +6,17 @@ use std::os::unix::net::UnixListener;
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr}; use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
#[cfg(target_pointer_width = "64")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp64 };
}
#[cfg(target_pointer_width = "32")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp32 };
}
pub fn random_string() -> String { pub fn random_string() -> String {
Alphanumeric.sample_string(&mut rand::thread_rng(), 8) Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
} }
+91 -7
View File
@@ -1,21 +1,35 @@
use crate::{constants, utils}; use crate::{constants, root_impl, utils};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use nix::unistd::{getgid, getuid, Pid}; use nix::unistd::{getgid, getuid, Pid};
use std::process::{Child, Command}; use std::process::{Child, Command};
use std::sync::mpsc; use std::sync::mpsc;
use std::{fs, thread}; use std::{fs, io, thread};
use std::ffi::CString;
use std::io::{BufRead, Write};
use std::os::unix::net::UnixListener; use std::os::unix::net::UnixListener;
use std::time::Duration; use std::time::Duration;
use binder::IBinder; use binder::IBinder;
use nix::errno::Errno;
use nix::libc;
use nix::sys::signal::{kill, Signal}; use nix::sys::signal::{kill, Signal};
use once_cell::sync::OnceCell;
static mut LOCK: Option<UnixListener> = None; static LOCK: OnceCell<UnixListener> = OnceCell::new();
static PROP_SECTIONS: OnceCell<[String; 2]> = OnceCell::new();
pub fn entry() -> Result<()> { pub fn entry() -> Result<()> {
log::info!("Start zygisksu watchdog"); log::info!("Start zygisksu watchdog");
check_permission()?; check_permission()?;
ensure_single_instance()?; ensure_single_instance()?;
spawn_daemon() mount_prop()?;
if check_and_set_hint()? == false {
log::warn!("Requirements not met, exiting");
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
return Ok(())
}
let end = spawn_daemon();
set_prop_hint(constants::STATUS_CRASHED)?;
end
} }
fn check_permission() -> Result<()> { fn check_permission() -> Result<()> {
@@ -43,12 +57,83 @@ fn ensure_single_instance() -> Result<()> {
log::info!("Ensure single instance"); log::info!("Ensure single instance");
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER; let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
match utils::abstract_namespace_socket(&name) { match utils::abstract_namespace_socket(&name) {
Ok(socket) => unsafe { LOCK = Some(socket) }, Ok(socket) => { let _ = LOCK.set(socket); }
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?") Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
} }
Ok(()) Ok(())
} }
fn mount_prop() -> Result<()> {
let module_prop = fs::File::open(constants::PATH_MODULE_PROP)?;
let mut section = 0;
let mut sections: [String; 2] = [String::new(), String::new()];
let lines = io::BufReader::new(module_prop).lines();
for line in lines {
let line = line?;
if line.starts_with("description=") {
sections[0].push_str("description=");
sections[1].push_str(line.trim_start_matches("description="));
sections[1].push('\n');
section = 1;
} else {
sections[section].push_str(&line);
sections[section].push('\n');
}
}
let _ = PROP_SECTIONS.set(sections);
fs::create_dir(constants::PATH_TMP_DIR)?;
// FIXME: sys_mount cannot be compiled on 32 bit
unsafe {
let r = libc::mount(
CString::new("tmpfs")?.as_ptr(),
CString::new(constants::PATH_TMP_DIR)?.as_ptr(),
CString::new("tmpfs")?.as_ptr(),
0,
CString::new("mode=755")?.as_ptr() as *const libc::c_void,
);
Errno::result(r)?;
let _file = fs::File::create(constants::PATH_TMP_PROP)?;
let r = libc::mount(
CString::new(constants::PATH_TMP_PROP)?.as_ptr(),
CString::new(constants::PATH_MODULE_PROP)?.as_ptr(),
std::ptr::null(),
libc::MS_BIND,
std::ptr::null(),
);
Errno::result(r)?;
}
Ok(())
}
fn set_prop_hint(hint: &str) -> Result<()> {
let mut file = fs::File::create(constants::PATH_TMP_PROP)?;
let sections = PROP_SECTIONS.get().unwrap();
file.write_all(sections[0].as_bytes())?;
file.write_all(b"[")?;
file.write_all(hint.as_bytes())?;
file.write_all(b"] ")?;
file.write_all(sections[1].as_bytes())?;
Ok(())
}
fn check_and_set_hint() -> Result<bool> {
let root_impl = root_impl::get_impl();
match root_impl {
root_impl::RootImpl::None => set_prop_hint(constants::STATUS_ROOT_IMPL_NONE)?,
root_impl::RootImpl::TooOld => set_prop_hint(constants::STATUS_ROOT_IMPL_TOO_OLD)?,
root_impl::RootImpl::Abnormal => set_prop_hint(constants::STATUS_ROOT_IMPL_ABNORMAL)?,
root_impl::RootImpl::Multiple => set_prop_hint(constants::STATUS_ROOT_IMPL_MULTIPLE)?,
_ => {
set_prop_hint(constants::STATUS_LOADED)?;
return Ok(true);
}
}
Ok(false)
}
fn spawn_daemon() -> Result<()> { fn spawn_daemon() -> Result<()> {
let mut lives = 5; let mut lives = 5;
loop { loop {
@@ -99,7 +184,6 @@ fn spawn_daemon() -> Result<()> {
log::error!("Restarting zygote..."); log::error!("Restarting zygote...");
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?; utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
utils::set_property(constants::PROP_SVC_ZYGOTE, "restart")?; utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
thread::sleep(Duration::from_secs(2));
} }
} }
+2 -2
View File
@@ -77,7 +77,7 @@ fn get_arch() -> Result<&'static str> {
fn load_modules(arch: &str) -> Result<Vec<Module>> { fn load_modules(arch: &str) -> Result<Vec<Module>> {
let mut modules = Vec::new(); let mut modules = Vec::new();
let dir = match fs::read_dir(constants::PATH_MODULE_DIR) { let dir = match fs::read_dir(constants::PATH_MODULES_DIR) {
Ok(dir) => dir, Ok(dir) => dir,
Err(e) => { Err(e) => {
log::warn!("Failed reading modules directory: {}", e); log::warn!("Failed reading modules directory: {}", e);
@@ -232,7 +232,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
DaemonSocketAction::GetModuleDir => { DaemonSocketAction::GetModuleDir => {
let index = stream.read_usize()?; let index = stream.read_usize()?;
let module = &context.modules[index]; let module = &context.modules[index];
let dir = format!("{}/{}", constants::PATH_MODULE_DIR, module.name); let dir = format!("{}/{}", constants::PATH_MODULES_DIR, module.name);
let dir = fs::File::open(dir)?; let dir = fs::File::open(dir)?;
stream.send_fd(dir.as_raw_fd())?; stream.send_fd(dir.as_raw_fd())?;
} }