Handle zygote death

This commit is contained in:
Nullptr
2023-02-15 13:15:35 +08:00
parent 5c00071fed
commit baf444228d
9 changed files with 89 additions and 61 deletions

3
.gitmodules vendored
View File

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

View File

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

View File

@@ -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' </dev/urandom | head -c 18)
if [ "$HAS32BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
fi
if [ "$HAS64BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libinjector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
if [ $DEBUG = false ]; then
ui_print "- Hex patching"
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
if [ "$HAS32BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
fi
if [ "$HAS64BIT" = true ]; then
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libinjector.so"
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
fi
fi
ui_print "- Setting permissions"

View File

@@ -10,6 +10,7 @@ android_logger = "0.12.0"
anyhow = { version = "1.0.68", features = ["backtrace"] }
clap = { version = "4.1.4", features = ["derive"] }
const_format = "0.2.5"
lazy_static = "1.4.0"
log = "0.4.17"
memfd = "0.6.2"
nix = "0.26.2"
@@ -17,6 +18,8 @@ num_enum = "0.5.9"
passfd = "0.1.5"
rand = "0.8.5"
binder = { path = "src/external/binder_rs/binder" }
[profile.release]
strip = true
opt-level = "z"

View File

@@ -20,6 +20,7 @@ cargo {
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
profile = if (isDebug) "debug" else "release"
exec = { spec, _ ->
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
spec.environment("VERSION_CODE", verCode)
spec.environment("VERSION_NAME", verName)
}

View File

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

View File

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

View File

@@ -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<UnixListener> = 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));
}
}