From baf444228d2c5e5b63560a13bcf49d29b962f155 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Wed, 15 Feb 2023 13:15:35 +0800 Subject: [PATCH] Handle zygote death --- .gitmodules | 3 ++ README.md | 14 ++---- module/src/customize.sh | 34 ++++++++------ zygiskd/Cargo.toml | 3 ++ zygiskd/build.gradle.kts | 1 + zygiskd/src/constants.rs | 2 + zygiskd/src/external/binder_rs | 1 + zygiskd/src/utils.rs | 7 ++- zygiskd/src/watchdog.rs | 85 +++++++++++++++++++++------------- 9 files changed, 89 insertions(+), 61 deletions(-) create mode 160000 zygiskd/src/external/binder_rs diff --git a/.gitmodules b/.gitmodules index 01ccb65..a1e26c2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "loader/src/external/parallel-hashmap"] path = loader/src/external/parallel-hashmap url = https://github.com/greg7mdp/parallel-hashmap +[submodule "zygiskd/src/external/binder_rs"] + path = zygiskd/src/external/binder_rs + url = https://github.com/Kernel-SU/binder_rs diff --git a/README.md b/README.md index 09df288..aa9194c 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment. -Warning: The current version of Zygisksu is UNSTABLE. You may suffer boot loop or even data loss so use with caution. +Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD. (Coming soon) ## Requirements + Minimal KernelSU version: 10575 -+ Minimal ksud version: 10200 ++ Minimal ksud version: 10616 + Full SELinux patch support (If non-gki kernel) ## Compatibility @@ -22,11 +22,5 @@ Should work with everything except those rely on Magisk internal behaviors. - [x] [Daemon] Linker namespace - [x] [Daemon] Separate zygiskd process - [x] [Daemon] Handle 64 bit only devices -- [ ] [Daemon] Handle zygote death - -## Running on Magisk - -It is possible to run Zygisksu on Magisk with a few steps: - -1. `mkdir /data/adb/ksu` -2. `ln -s /data/adb/modules /data/adb/ksu/` +- [x] [Daemon] Handle zygote death +- [ ] [ Misc ] Support Magisk out of box diff --git a/module/src/customize.sh b/module/src/customize.sh index 88fa98c..a9dea3a 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -1,6 +1,8 @@ # shellcheck disable=SC2034 SKIPUNZIP=1 +DEBUG=@DEBUG@ + if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then ui_print "- Installing from KernelSU app" else @@ -14,8 +16,8 @@ VERSION=$(grep_prop version "${TMPDIR}/module.prop") ui_print "- Installing Zygisksu $VERSION" # check KernelSU -ui_print "- KernelSU version: $KSU_VER ($KSU_VER_CODE)" -if [ "$KSU_VER_CODE" -lt 10200 ]; then +ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)" +if [ "$KSU_KERNEL_VER_CODE" -lt 10575 ]; then ui_print "*********************************************************" ui_print "! KernelSU version is too old!" ui_print "! Please update KernelSU to latest version" @@ -25,7 +27,7 @@ fi # check android if [ "$API" -lt 29 ]; then ui_print "! Unsupported sdk: $API" - abort "! Minimal supported sdk is 29 (Android 10.0)" + abort "! Minimal supported sdk is 29 (Android 10)" else ui_print "- Device sdk: $API" fi @@ -51,7 +53,7 @@ extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip" extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR" ui_print "- Checking SELinux patches" -if ! /data/adb/ksud sepolicy check "$TMPDIR/sepolicy.rule"; then +if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then ui_print "*********************************************************" ui_print "! Unable to apply SELinux patches!" ui_print "! Your kernel may not support SELinux patch fully" @@ -110,17 +112,19 @@ else fi fi -ui_print "- Hex patching" -SOCKET_PATCH=$(tr -dc 'a-f0-9' + spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path) spec.environment("VERSION_CODE", verCode) spec.environment("VERSION_NAME", verName) } diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index 976a4de..af88d37 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -23,6 +23,8 @@ macro_rules! lp_select { } pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge"; +pub const PROP_SVC_ZYGOTE: &str = "init.svc.zygote"; +pub const ZYGISK_LOADER: &str = "libzygiskloader.so"; pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder"; diff --git a/zygiskd/src/external/binder_rs b/zygiskd/src/external/binder_rs new file mode 160000 index 0000000..6d958bb --- /dev/null +++ b/zygiskd/src/external/binder_rs @@ -0,0 +1 @@ +Subproject commit 6d958bb94ab388b5623631a6044cfdb91790d135 diff --git a/zygiskd/src/utils.rs b/zygiskd/src/utils.rs index 19a4475..d5fb3f3 100644 --- a/zygiskd/src/utils.rs +++ b/zygiskd/src/utils.rs @@ -1,4 +1,3 @@ -use crate::constants; use anyhow::Result; use nix::unistd::gettid; use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command}; @@ -27,10 +26,10 @@ pub fn get_native_bridge() -> String { std::env::var("NATIVE_BRIDGE").unwrap_or_default() } -pub fn restore_native_bridge() -> Result<()> { +pub fn set_property(name: &str, value: &str) -> Result<()> { Command::new("resetprop") - .arg(constants::PROP_NATIVE_BRIDGE) - .arg(get_native_bridge()) + .arg(name) + .arg(value) .spawn()?.wait()?; Ok(()) } diff --git a/zygiskd/src/watchdog.rs b/zygiskd/src/watchdog.rs index 730de53..8798141 100644 --- a/zygiskd/src/watchdog.rs +++ b/zygiskd/src/watchdog.rs @@ -1,12 +1,13 @@ use crate::{constants, utils}; use anyhow::{bail, Result}; -use nix::unistd::{getgid, getuid}; +use nix::unistd::{getgid, getuid, Pid}; use std::process::{Child, Command}; use std::sync::mpsc; use std::{fs, thread}; use std::os::unix::net::UnixListener; -use std::path::Path; use std::time::Duration; +use binder::IBinder; +use nix::sys::signal::{kill, Signal}; static mut LOCK: Option = None; @@ -49,36 +50,56 @@ fn ensure_single_instance() -> Result<()> { } fn spawn_daemon() -> Result<()> { - let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn(); - let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn(); - let (sender, receiver) = mpsc::channel(); - let mut waiting = vec![]; - let mut spawn = |mut daemon: Child, socket: &'static str| { - waiting.push(socket); - let sender = sender.clone(); - thread::spawn(move || { - let result = daemon.wait().unwrap(); - log::error!("Daemon process {} died: {}", daemon.id(), result); - drop(daemon); - sender.send(()).unwrap(); - }); - }; - if let Ok(it) = daemon32 { spawn(it, "/dev/socket/zygote_secondary") } - if let Ok(it) = daemon64 { spawn(it, "/dev/socket/zygote") } - - waiting.into_iter().for_each(|socket| wait_zygote(socket)); - log::info!("Zygote ready, restore native bridge"); - utils::restore_native_bridge()?; - - let _ = receiver.recv(); - bail!("Daemon process died"); -} - -fn wait_zygote(socket: &str) -> () { - let path = Path::new(socket); + let mut lives = 5; loop { - if path.exists() { return; } - log::debug!("{socket} not exists, wait for 1s..."); - thread::sleep(Duration::from_secs(1)); + let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn(); + let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn(); + let mut child_ids = vec![]; + let (sender, receiver) = mpsc::channel(); + let mut spawn = |mut daemon: Child| { + child_ids.push(daemon.id()); + let sender = sender.clone(); + thread::spawn(move || { + let result = daemon.wait().unwrap(); + log::error!("Daemon process {} died: {}", daemon.id(), result); + drop(daemon); + let _ = sender.send(()); + }); + }; + if let Ok(it) = daemon32 { spawn(it) } + if let Ok(it) = daemon64 { spawn(it) } + + let mut binder = loop { + if receiver.try_recv().is_ok() { + bail!("Daemon died before system server ready"); + } + match binder::get_service("activity") { + Some(binder) => break binder, + None => { + log::trace!("System server not ready, wait for 1s..."); + thread::sleep(Duration::from_secs(1)); + } + }; + }; + log::info!("System server ready, restore native bridge"); + utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?; + + loop { + if receiver.try_recv().is_ok() || binder.ping_binder().is_err() { break; } + thread::sleep(Duration::from_secs(1)) + } + for child in child_ids { + let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL); + } + + lives -= 1; + if lives == 0 { + bail!("Too many crashes, abort"); + } + + 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)); } }