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

View File

@@ -6,7 +6,7 @@ edition = "2021"
rust-version = "1.67"
[dependencies]
android_logger = "0.12.0"
android_logger = "0.13.0"
anyhow = { version = "1.0.68", features = ["backtrace"] }
clap = { version = "4.1.4", features = ["derive"] }
const_format = "0.2.5"

View File

@@ -16,26 +16,25 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
#[cfg(not(debug_assertions))]
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_SVC_ZYGOTE: &str = "init.svc.zygote";
pub const PROP_CTL_RESTART: &str = "ctl.restart";
pub const ZYGISK_LOADER: &str = "libzygiskloader.so";
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_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)]
#[repr(u8)]

View File

@@ -38,7 +38,7 @@ fn init_android_logger(tag: &str) {
}
fn start() -> Result<()> {
root_impl::setup()?;
root_impl::setup();
let cli = Args::parse();
match cli.command {
Commands::Watchdog => watchdog::entry()?,

View File

@@ -1,4 +1,3 @@
use anyhow::{bail, Result};
use nix::libc::prctl;
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_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;
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
return match version {
0 => Ok(false),
MIN_KSU_VERSION..=MAX_KSU_VERSION => Ok(true),
1..=MIN_KSU_VERSION => bail!("KernelSU version too old: {}", version),
_ => bail!("KernelSU version abnormal: {}", version)
match version {
0 => None,
MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
1..=MIN_KSU_VERSION => Some(Version::TooOld),
_ => Some(Version::Abnormal)
}
}

View File

@@ -1,8 +1,12 @@
use anyhow::{bail, Result};
use std::process::{Command, Stdio};
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")
.arg("-V")
.stdout(Stdio::piped())
@@ -10,13 +14,13 @@ pub fn is_magisk() -> Result<bool> {
.and_then(|child| child.wait_with_output().ok())
.and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|output| output.trim().parse().ok());
if let Some(version) = version {
if version < MIN_MAGISK_VERSION {
bail!("Magisk version too old: {}", version);
version.map(|version| {
if version >= MIN_MAGISK_VERSION {
Version::Supported
} else {
Version::TooOld
}
return Ok(true);
}
Ok(false)
})
}
pub fn uid_on_allowlist(uid: i32) -> bool {

View File

@@ -2,33 +2,52 @@ mod kernelsu;
mod magisk;
use once_cell::sync::OnceCell;
use anyhow::{bail, Result};
enum RootImpl {
pub enum RootImpl {
None,
TooOld,
Abnormal,
Multiple,
KernelSU,
Magisk,
}
static ROOT_IMPL: OnceCell<RootImpl> = OnceCell::new();
pub fn setup() -> Result<()> {
if kernelsu::is_kernel_su()? {
if let Ok(true) = magisk::is_magisk() {
bail!("Multiple root implementation");
pub fn setup() {
let ksu_version = kernelsu::get_kernel_su();
let magisk_version = magisk::get_magisk();
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);
} else if magisk::is_magisk()? {
let _ = ROOT_IMPL.set(RootImpl::Magisk);
} else {
bail!("Unknown root implementation");
}
Ok(())
(None, Some(magisk_version)) => {
let val = match magisk_version {
magisk::Version::Supported => RootImpl::Magisk,
magisk::Version::TooOld => RootImpl::TooOld,
};
ROOT_IMPL.set(val)
}
};
}
pub fn get_impl() -> &'static RootImpl {
ROOT_IMPL.get().unwrap()
}
pub fn uid_on_allowlist(uid: i32) -> bool {
match ROOT_IMPL.get().unwrap() {
RootImpl::KernelSU => kernelsu::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() {
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
RootImpl::Magisk => magisk::uid_on_denylist(uid),
_ => unreachable!(),
}
}

View File

@@ -6,6 +6,17 @@ use std::os::unix::net::UnixListener;
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
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 {
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
}

View File

@@ -1,21 +1,35 @@
use crate::{constants, utils};
use crate::{constants, root_impl, utils};
use anyhow::{bail, Result};
use nix::unistd::{getgid, getuid, Pid};
use std::process::{Child, Command};
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::time::Duration;
use binder::IBinder;
use nix::errno::Errno;
use nix::libc;
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<()> {
log::info!("Start zygisksu watchdog");
check_permission()?;
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<()> {
@@ -43,12 +57,83 @@ fn ensure_single_instance() -> Result<()> {
log::info!("Ensure single instance");
let name = String::from("zygiskwd") + constants::SOCKET_PLACEHOLDER;
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?")
}
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<()> {
let mut lives = 5;
loop {
@@ -99,7 +184,6 @@ fn spawn_daemon() -> Result<()> {
log::error!("Restarting zygote...");
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
utils::set_property(constants::PROP_SVC_ZYGOTE, "restart")?;
thread::sleep(Duration::from_secs(2));
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
}
}

View File

@@ -77,7 +77,7 @@ fn get_arch() -> Result<&'static str> {
fn load_modules(arch: &str) -> Result<Vec<Module>> {
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,
Err(e) => {
log::warn!("Failed reading modules directory: {}", e);
@@ -232,7 +232,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
DaemonSocketAction::GetModuleDir => {
let index = stream.read_usize()?;
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)?;
stream.send_fd(dir.as_raw_fd())?;
}