Skip to content

Commit baf4442

Browse files
committed
Handle zygote death
1 parent 5c00071 commit baf4442

File tree

9 files changed

+87
-59
lines changed

9 files changed

+87
-59
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "loader/src/external/parallel-hashmap"]
55
path = loader/src/external/parallel-hashmap
66
url = https://github.com/greg7mdp/parallel-hashmap
7+
[submodule "zygiskd/src/external/binder_rs"]
8+
path = zygiskd/src/external/binder_rs
9+
url = https://github.com/Kernel-SU/binder_rs

README.md

+4-10
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
44

5-
Warning: The current version of Zygisksu is UNSTABLE. You may suffer boot loop or even data loss so use with caution.
5+
Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD. (Coming soon)
66

77
## Requirements
88

99
+ Minimal KernelSU version: 10575
10-
+ Minimal ksud version: 10200
10+
+ Minimal ksud version: 10616
1111
+ Full SELinux patch support (If non-gki kernel)
1212

1313
## Compatibility
@@ -22,11 +22,5 @@ Should work with everything except those rely on Magisk internal behaviors.
2222
- [x] [Daemon] Linker namespace
2323
- [x] [Daemon] Separate zygiskd process
2424
- [x] [Daemon] Handle 64 bit only devices
25-
- [ ] [Daemon] Handle zygote death
26-
27-
## Running on Magisk
28-
29-
It is possible to run Zygisksu on Magisk with a few steps:
30-
31-
1. `mkdir /data/adb/ksu`
32-
2. `ln -s /data/adb/modules /data/adb/ksu/`
25+
- [x] [Daemon] Handle zygote death
26+
- [ ] [ Misc ] Support Magisk out of box

module/src/customize.sh

+19-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# shellcheck disable=SC2034
22
SKIPUNZIP=1
33

4+
DEBUG=@DEBUG@
5+
46
if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then
57
ui_print "- Installing from KernelSU app"
68
else
@@ -14,8 +16,8 @@ VERSION=$(grep_prop version "${TMPDIR}/module.prop")
1416
ui_print "- Installing Zygisksu $VERSION"
1517

1618
# check KernelSU
17-
ui_print "- KernelSU version: $KSU_VER ($KSU_VER_CODE)"
18-
if [ "$KSU_VER_CODE" -lt 10200 ]; then
19+
ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
20+
if [ "$KSU_KERNEL_VER_CODE" -lt 10575 ]; then
1921
ui_print "*********************************************************"
2022
ui_print "! KernelSU version is too old!"
2123
ui_print "! Please update KernelSU to latest version"
@@ -25,7 +27,7 @@ fi
2527
# check android
2628
if [ "$API" -lt 29 ]; then
2729
ui_print "! Unsupported sdk: $API"
28-
abort "! Minimal supported sdk is 29 (Android 10.0)"
30+
abort "! Minimal supported sdk is 29 (Android 10)"
2931
else
3032
ui_print "- Device sdk: $API"
3133
fi
@@ -51,7 +53,7 @@ extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
5153
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
5254

5355
ui_print "- Checking SELinux patches"
54-
if ! /data/adb/ksud sepolicy check "$TMPDIR/sepolicy.rule"; then
56+
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
5557
ui_print "*********************************************************"
5658
ui_print "! Unable to apply SELinux patches!"
5759
ui_print "! Your kernel may not support SELinux patch fully"
@@ -110,17 +112,19 @@ else
110112
fi
111113
fi
112114

113-
ui_print "- Hex patching"
114-
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
115-
if [ "$HAS32BIT" = true ]; then
116-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
117-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
118-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
119-
fi
120-
if [ "$HAS64BIT" = true ]; then
121-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
122-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libinjector.so"
123-
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
115+
if [ $DEBUG = false ]; then
116+
ui_print "- Hex patching"
117+
SOCKET_PATCH=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
118+
if [ "$HAS32BIT" = true ]; then
119+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd32"
120+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libinjector.so"
121+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib/libzygiskloader.so"
122+
fi
123+
if [ "$HAS64BIT" = true ]; then
124+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/bin/zygiskd64"
125+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libinjector.so"
126+
sed -i "s/socket_placeholder/$SOCKET_PATCH/g" "$MODPATH/system/lib64/libzygiskloader.so"
127+
fi
124128
fi
125129

126130
ui_print "- Setting permissions"

zygiskd/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ android_logger = "0.12.0"
1010
anyhow = { version = "1.0.68", features = ["backtrace"] }
1111
clap = { version = "4.1.4", features = ["derive"] }
1212
const_format = "0.2.5"
13+
lazy_static = "1.4.0"
1314
log = "0.4.17"
1415
memfd = "0.6.2"
1516
nix = "0.26.2"
1617
num_enum = "0.5.9"
1718
passfd = "0.1.5"
1819
rand = "0.8.5"
1920

21+
binder = { path = "src/external/binder_rs/binder" }
22+
2023
[profile.release]
2124
strip = true
2225
opt-level = "z"

zygiskd/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ cargo {
2020
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
2121
profile = if (isDebug) "debug" else "release"
2222
exec = { spec, _ ->
23+
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
2324
spec.environment("VERSION_CODE", verCode)
2425
spec.environment("VERSION_NAME", verName)
2526
}

zygiskd/src/constants.rs

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ macro_rules! lp_select {
2323
}
2424

2525
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
26+
pub const PROP_SVC_ZYGOTE: &str = "init.svc.zygote";
27+
pub const ZYGISK_LOADER: &str = "libzygiskloader.so";
2628

2729
pub const SOCKET_PLACEHOLDER: &str = "socket_placeholder";
2830

zygiskd/src/external/binder_rs

Submodule binder_rs added at 6d958bb

zygiskd/src/utils.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::constants;
21
use anyhow::Result;
32
use nix::unistd::gettid;
43
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
@@ -27,10 +26,10 @@ pub fn get_native_bridge() -> String {
2726
std::env::var("NATIVE_BRIDGE").unwrap_or_default()
2827
}
2928

30-
pub fn restore_native_bridge() -> Result<()> {
29+
pub fn set_property(name: &str, value: &str) -> Result<()> {
3130
Command::new("resetprop")
32-
.arg(constants::PROP_NATIVE_BRIDGE)
33-
.arg(get_native_bridge())
31+
.arg(name)
32+
.arg(value)
3433
.spawn()?.wait()?;
3534
Ok(())
3635
}

zygiskd/src/watchdog.rs

+51-30
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::{constants, utils};
22
use anyhow::{bail, Result};
3-
use nix::unistd::{getgid, getuid};
3+
use nix::unistd::{getgid, getuid, Pid};
44
use std::process::{Child, Command};
55
use std::sync::mpsc;
66
use std::{fs, thread};
77
use std::os::unix::net::UnixListener;
8-
use std::path::Path;
98
use std::time::Duration;
9+
use binder::IBinder;
10+
use nix::sys::signal::{kill, Signal};
1011

1112
static mut LOCK: Option<UnixListener> = None;
1213

@@ -49,36 +50,56 @@ fn ensure_single_instance() -> Result<()> {
4950
}
5051

5152
fn spawn_daemon() -> Result<()> {
52-
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
53-
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
54-
let (sender, receiver) = mpsc::channel();
55-
let mut waiting = vec![];
56-
let mut spawn = |mut daemon: Child, socket: &'static str| {
57-
waiting.push(socket);
58-
let sender = sender.clone();
59-
thread::spawn(move || {
60-
let result = daemon.wait().unwrap();
61-
log::error!("Daemon process {} died: {}", daemon.id(), result);
62-
drop(daemon);
63-
sender.send(()).unwrap();
64-
});
65-
};
66-
if let Ok(it) = daemon32 { spawn(it, "/dev/socket/zygote_secondary") }
67-
if let Ok(it) = daemon64 { spawn(it, "/dev/socket/zygote") }
53+
let mut lives = 5;
54+
loop {
55+
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
56+
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
57+
let mut child_ids = vec![];
58+
let (sender, receiver) = mpsc::channel();
59+
let mut spawn = |mut daemon: Child| {
60+
child_ids.push(daemon.id());
61+
let sender = sender.clone();
62+
thread::spawn(move || {
63+
let result = daemon.wait().unwrap();
64+
log::error!("Daemon process {} died: {}", daemon.id(), result);
65+
drop(daemon);
66+
let _ = sender.send(());
67+
});
68+
};
69+
if let Ok(it) = daemon32 { spawn(it) }
70+
if let Ok(it) = daemon64 { spawn(it) }
6871

69-
waiting.into_iter().for_each(|socket| wait_zygote(socket));
70-
log::info!("Zygote ready, restore native bridge");
71-
utils::restore_native_bridge()?;
72+
let mut binder = loop {
73+
if receiver.try_recv().is_ok() {
74+
bail!("Daemon died before system server ready");
75+
}
76+
match binder::get_service("activity") {
77+
Some(binder) => break binder,
78+
None => {
79+
log::trace!("System server not ready, wait for 1s...");
80+
thread::sleep(Duration::from_secs(1));
81+
}
82+
};
83+
};
84+
log::info!("System server ready, restore native bridge");
85+
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
7286

73-
let _ = receiver.recv();
74-
bail!("Daemon process died");
75-
}
87+
loop {
88+
if receiver.try_recv().is_ok() || binder.ping_binder().is_err() { break; }
89+
thread::sleep(Duration::from_secs(1))
90+
}
91+
for child in child_ids {
92+
let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL);
93+
}
7694

77-
fn wait_zygote(socket: &str) -> () {
78-
let path = Path::new(socket);
79-
loop {
80-
if path.exists() { return; }
81-
log::debug!("{socket} not exists, wait for 1s...");
82-
thread::sleep(Duration::from_secs(1));
95+
lives -= 1;
96+
if lives == 0 {
97+
bail!("Too many crashes, abort");
98+
}
99+
100+
log::error!("Restarting zygote...");
101+
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
102+
utils::set_property(constants::PROP_SVC_ZYGOTE, "restart")?;
103+
thread::sleep(Duration::from_secs(2));
83104
}
84105
}

0 commit comments

Comments
 (0)