Update zygiskd

This commit is contained in:
Nullptr
2023-01-29 12:10:05 +08:00
parent c63cfbb6ea
commit e4b797a7ac
7 changed files with 91 additions and 91 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,5 @@
.cxx
build
local.properties
target
Cargo.lock

View File

@@ -7,7 +7,7 @@ rust-version = "1.67"
[dependencies]
android_logger = "0.12.0"
anyhow = "1.0.68"
anyhow = { version = "1.0.68", features = ["backtrace"] }
const_format = "0.2.5"
log = "0.4.17"
memfd = "0.6.2"

View File

@@ -10,10 +10,6 @@ cargo {
targets = listOf("arm64", "arm", "x86", "x86_64")
val isDebug = gradle.startParameter.taskNames.any { it.toLowerCase().contains("debug") }
profile = if (isDebug) "debug" else "release"
targetDirectory = "build/intermediates/rust"
exec = { spec, _ ->
spec.environment("CARGO_TARGET_DIR", targetDirectory)
}
}
androidComponents.onVariants { variant ->
@@ -25,11 +21,16 @@ androidComponents.onVariants { variant ->
}
}
task<Exec>("pushAndRun$variantCapped") {
task("push$variantCapped") {
group = "zygiskd"
dependsOn("build$variantCapped")
dependsOn("cargoBuildArm", "cargoBuildArm64")
doLast {
commandLine("adb", "push", "target/")
val moduleDir = "/data/adb/ksu/modules/zygisksu"
exec { commandLine("adb", "push", "build/rustJniLibs/android/armeabi-v7a/zygiskd", "/data/local/tmp/zygiskd32") }
exec { commandLine("adb", "push", "build/rustJniLibs/android/arm64-v8a/zygiskd", "/data/local/tmp/zygiskd64") }
exec { commandLine("adb", "shell", "su", "-c", "mv /data/local/tmp/zygiskd32 $moduleDir/zygiskd32") }
exec { commandLine("adb", "shell", "su", "-c", "mv /data/local/tmp/zygiskd64 $moduleDir/zygiskd64") }
exec { commandLine("adb", "shell", "su", "-c", "ln -sf zygiskd64 $moduleDir/zygiskwd") }
}
}
}

View File

@@ -3,12 +3,12 @@ use num_enum::TryFromPrimitive;
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
pub const KSU_MODULE_DIR: &str = "/data/adb/ksu/modules";
pub const ZYGISKSU_DIR: &str = concatcp!(KSU_MODULE_DIR, "/zygisksu");
pub const ZYGISKWD: &str = concatcp!(ZYGISKSU_DIR, "/zygiskwd");
pub const ZYGISKD32: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd32");
pub const ZYGISKD64: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd64");
pub const DAEMON_LOCK: &str = concatcp!(ZYGISKSU_DIR, "/zygiskd.lock");
pub const PATH_KSU_MODULE_DIR: &str = "/data/adb/ksu/modules";
pub const PATH_ZYGISKSU_DIR: &str = concatcp!(PATH_KSU_MODULE_DIR, "/zygisksu");
pub const PATH_ZYGISKWD: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskwd");
pub const PATH_ZYGISKD32: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd32");
pub const PATH_ZYGISKD64: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd64");
pub const PATH_DAEMON_LOCK: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd.lock");
#[derive(Debug, Eq, PartialEq, TryFromPrimitive)]
#[repr(u8)]

View File

@@ -1,11 +1,11 @@
#![allow(dead_code)]
mod watchdog;
mod constants;
mod zygisk;
mod utils;
mod watchdog;
mod zygisk;
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use log::LevelFilter;
use nix::libc;
@@ -17,7 +17,7 @@ fn init_android_logger(tag: &str) {
);
}
fn main() -> Result<()> {
fn entry() -> Result<()> {
let process = std::env::args().next().unwrap();
let process = process.split('/').last().unwrap();
init_android_logger(process);
@@ -39,7 +39,13 @@ fn main() -> Result<()> {
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); }
zygisk::start(true)?;
}
_ => return Err(anyhow!("Unexpected process name: {process}"))
_ => bail!("Unexpected process name: {process}")
}
Ok(())
}
fn main() {
if let Err(e) = entry() {
log::error!("Crashed: {}", e.backtrace());
}
}

View File

@@ -1,5 +1,5 @@
use crate::constants;
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::{getgid, getuid};
use std::os::unix::prelude::AsRawFd;
@@ -13,17 +13,19 @@ pub fn check_permission() -> Result<()> {
log::info!("Check permission");
let uid = getuid();
if uid.as_raw() != 0 {
return Err(anyhow!("UID is not 0"));
bail!("UID is not 0");
}
let gid = getgid();
if gid.as_raw() != 0 {
return Err(anyhow!("GID is not 0"));
bail!("GID is not 0");
}
let context = fs::read_to_string("/proc/self/attr/current")?;
if context != "u:r:su:s0" {
return Err(anyhow!("SELinux context is not u:r:su:s0"));
let context = context.trim_end_matches('\0');
//TODO: remove magisk context after debug finished
if context != "u:r:su:s0" && context != "u:r:magisk:s0" {
bail!("SELinux context incorrect: {context}");
}
Ok(())
@@ -31,28 +33,26 @@ pub fn check_permission() -> Result<()> {
pub fn ensure_single_instance() -> Result<()> {
log::info!("Ensure single instance");
let metadata = fs::metadata(constants::ZYGISKSU_DIR);
let metadata = fs::metadata(constants::PATH_ZYGISKSU_DIR);
if metadata.is_err() || !metadata.unwrap().is_dir() {
return Err(anyhow!("Zygisksu is not installed"));
bail!("Zygisksu is not installed");
}
unsafe {
match fs::File::create(constants::DAEMON_LOCK) {
match fs::File::create(constants::PATH_DAEMON_LOCK) {
Ok(file) => LOCK_FILE = Some(file),
Err(e) => return Err(anyhow!("Failed to open lock file: {e}")),
Err(e) => bail!("Failed to open lock file: {e}"),
};
let fd = LOCK_FILE.as_ref().unwrap().as_raw_fd();
if let Err(e) = flock(fd, FlockArg::LockExclusiveNonblock) {
return Err(anyhow!(
"Failed to acquire lock: {e}. Maybe another instance is running?"
));
bail!("Failed to acquire lock: {e}. Maybe another instance is running?");
}
}
Ok(())
}
pub fn spawn_daemon() -> Result<()> {
let daemon32 = Command::new(constants::ZYGISKD32).spawn()?;
let daemon64 = Command::new(constants::ZYGISKD64).spawn()?;
let daemon32 = Command::new(constants::PATH_ZYGISKD32).spawn()?;
let daemon64 = Command::new(constants::PATH_ZYGISKD64).spawn()?;
let (sender, receiver) = mpsc::channel();
let spawn = |mut daemon: Child| {
let sender = sender.clone();
@@ -66,5 +66,5 @@ pub fn spawn_daemon() -> Result<()> {
spawn(daemon32);
spawn(daemon64);
let _ = receiver.recv();
Err(anyhow!("Daemon process died"))
bail!("Daemon process died");
}

View File

@@ -1,26 +1,26 @@
use crate::constants::DaemonSocketAction;
use crate::utils::{restore_native_bridge, UnixStreamExt};
use crate::{constants, utils};
use anyhow::{anyhow, Result};
use anyhow::{bail, Result};
use memfd::Memfd;
use nix::{
libc::{self, dlsym},
unistd::getppid,
};
use passfd::FdPassingExt;
use std::cell::Cell;
use std::io::Write;
use std::sync::Arc;
use std::thread;
use std::{
ffi::c_void,
fs,
os::unix::{
net::{UnixListener, UnixStream},
prelude::AsRawFd,
},
path::PathBuf,
use std::ffi::c_void;
use std::fs;
use std::os::fd::FromRawFd;
use std::os::unix::{
net::{UnixListener, UnixStream},
prelude::AsRawFd,
};
use std::path::PathBuf;
use std::process::Command;
use nix::sys::socket::{AddressFamily, SockFlag, SockType, UnixAddr};
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
@@ -33,11 +33,11 @@ struct Module {
struct Context {
native_bridge: String,
modules: Arc<Vec<Module>>,
stream: Cell<UnixStream>,
listener: UnixListener,
}
pub fn start(is64: bool) -> Result<()> {
// check_parent()?;
check_parent()?;
let arch = get_arch(is64)?;
log::debug!("Daemon architecture: {arch}");
@@ -47,56 +47,44 @@ pub fn start(is64: bool) -> Result<()> {
log::info!("Create socket");
let listener = create_daemon_socket(is64)?;
log::info!("Waiting for connection");
let (stream, _) = listener.accept()?;
drop(listener);
let context = Context {
native_bridge: utils::get_native_bridge(),
modules: Arc::new(modules),
stream: Cell::new(stream),
listener,
};
log::info!("Connection established");
restore_native_bridge()?;
log::info!("Start to listen zygote connections");
handle_daemon_actions(context)?;
Ok(())
}
fn check_parent() -> Result<()> {
let parent = fs::read_link(format!("/proc/{}/exe", getppid().as_raw()))?;
let parent = parent.file_name().unwrap().to_str().unwrap();
if parent != constants::ZYGISKWD {
return Err(anyhow!("zygiskd is not started by watchdog"));
let parent = fs::read_to_string(format!("/proc/{}/cmdline", getppid().as_raw()))?;
let parent = parent.split('/').last().unwrap().trim_end_matches('\0');
if parent != "zygiskwd" {
bail!("Daemon is not started by watchdog: {parent}");
}
Ok(())
}
fn get_arch(is64: bool) -> Result<&'static str> {
// let output = Command::new("getprop ro.product.cpu.abi").output()?;
// let system_arch = String::from_utf8(output.stdout)?;
let system_arch = "x86_64"; // DEBUGGING
let output = Command::new("getprop").arg("ro.product.cpu.abi").output()?;
let system_arch = String::from_utf8(output.stdout)?;
let is_arm = system_arch.contains("arm");
let is_x86 = system_arch.contains("x86");
if is64 {
match (is_arm, is_x86) {
(true, _) => Ok("arm64-v8a"),
(_, true) => Ok("x86_64"),
_ => Err(anyhow!("Unsupported system architecture: {}", system_arch)),
}
} else {
match (is_arm, is_x86) {
(true, _) => Ok("armeabi-v7a"),
(_, true) => Ok("x86"),
_ => Err(anyhow!("Unsupported system architecture: {}", system_arch)),
}
match (is_arm, is_x86, is64) {
(true, _, false) => Ok("armeabi-v7a"),
(true, _, true) => Ok("arm64-v8a"),
(_, true, false) => Ok("x86"),
(_, true, true) => Ok("x86_64"),
_ => bail!("Unsupported system architecture: {}", system_arch),
}
}
fn load_modules(arch: &str) -> Result<Vec<Module>> {
let mut modules = Vec::new();
let dir = match fs::read_dir(constants::KSU_MODULE_DIR) {
let dir = match fs::read_dir(constants::PATH_KSU_MODULE_DIR) {
Ok(dir) => dir,
Err(e) => {
log::warn!("Failed reading modules directory: {}", e);
@@ -119,7 +107,7 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
}
};
let companion_entry = match preload_module(&memfd) {
Ok(companion_entry) => companion_entry,
Ok(entry) => entry,
Err(e) => {
log::warn!(" Failed to preload `{name}`: {e}");
continue;
@@ -136,7 +124,7 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
Ok(modules)
}
fn create_memfd(name: &str, so_path: &PathBuf) -> Result<memfd::Memfd> {
fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> {
let opts = memfd::MemfdOptions::default().allow_sealing(true);
let memfd = opts.create(name)?;
@@ -164,7 +152,7 @@ fn preload_module(memfd: &Memfd) -> Result<Option<ZygiskCompanionEntryFn>> {
let e = std::ffi::CStr::from_ptr(libc::dlerror())
.to_string_lossy()
.into_owned();
return Err(anyhow!("dlopen failed: {}", e));
bail!("dlopen failed: {}", e);
}
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
let entry = dlsym(handle, symbol.as_ptr());
@@ -178,17 +166,25 @@ fn preload_module(memfd: &Memfd) -> Result<Option<ZygiskCompanionEntryFn>> {
fn create_daemon_socket(is64: bool) -> Result<UnixListener> {
utils::set_socket_create_context("u:r:zygote:s0")?;
let socket_name = if is64 { "zygiskd64" } else { "zygiskd32" };
let listener = UnixListener::bind(socket_name)?;
let name = if is64 { "zygiskd64" } else { "zygiskd32" };
// TODO: Replace with SockAddrExt::from_abstract_name when it's stable
let addr = UnixAddr::new_abstract(name.as_bytes())?;
let socket = nix::sys::socket::socket(AddressFamily::Unix, SockType::Stream, SockFlag::empty(), None)?;
nix::sys::socket::bind(socket, &addr)?;
log::debug!("Listening on {}", addr);
log::debug!("Socket fd: {}", socket);
let listener = unsafe { UnixListener::from_raw_fd(socket) };
Ok(listener)
}
fn handle_daemon_actions(mut context: Context) -> Result<()> {
let stream = context.stream.get_mut();
fn handle_daemon_actions(context: Context) -> Result<()> {
loop {
let (mut stream, _) = context.listener.accept()?;
let action = stream.read_u8()?;
match DaemonSocketAction::try_from(action) {
// First connection from zygote
Ok(DaemonSocketAction::ReadNativeBridge) => {
restore_native_bridge()?;
stream.write_usize(context.native_bridge.len())?;
stream.write_all(context.native_bridge.as_bytes())?;
}
@@ -210,9 +206,7 @@ fn handle_daemon_actions(mut context: Context) -> Result<()> {
}
});
}
Err(_) => {
return Err(anyhow!("Invalid action code: {action}"));
}
Err(_) => bail!("Invalid action code: {action}")
}
}
}
@@ -226,15 +220,13 @@ fn create_companion(mut server: UnixStream, modules: &Vec<Module>) -> Result<()>
let module = &modules[index];
log::debug!("New companion request from module {}", module.name);
unsafe {
match module.companion_entry {
Some(entry) => {
let (sock_app, sock_companion) = UnixStream::pair()?;
server.send_fd(sock_app.as_raw_fd())?;
entry(sock_companion.as_raw_fd());
}
None => (),
match module.companion_entry {
Some(entry) => {
let (sock_app, sock_companion) = UnixStream::pair()?;
server.send_fd(sock_app.as_raw_fd())?;
unsafe { entry(sock_companion.as_raw_fd()); }
}
None => (),
}
}
}