You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8958e94b6 | ||
|
|
0338cdb0ed | ||
|
|
99e653c576 | ||
|
|
7c27c32861 | ||
|
|
bea5ed47b8 | ||
|
|
954a712089 | ||
|
|
f6195ddb43 | ||
|
|
8b5e9db347 | ||
|
|
a04f636ac4 | ||
|
|
00f0a6e3fa | ||
|
|
f5bf82fa93 | ||
|
|
33c4ea3c62 | ||
|
|
3eee57eb8f | ||
|
|
250b4b2f8c | ||
|
|
3772e23473 | ||
|
|
8c5acf1ebe |
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Bug report/反馈 Bug
|
||||
description: Report errors or unexpected behavior./反馈错误或异常行为。
|
||||
labels: [bug]
|
||||
title: "[Bug] Short description/简单描述"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting issues of Zygisk on KernelSU!
|
||||
|
||||
To make it easier for us to help you please enter detailed information below.
|
||||
|
||||
感谢给 Zygisk on KernelSU 汇报问题!
|
||||
为了使我们更好地帮助你,请提供以下信息。
|
||||
为了防止重复汇报,标题请务必使用英文。
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce/复现步骤
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behaviour/预期行为
|
||||
placeholder: Tell us what should happen/正常情况下应该发生什么
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual behaviour/实际行为
|
||||
placeholder: Tell us what happens instead/实际上发生了什么
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: KernelSU Module List/KernelSU 模块列表
|
||||
render: Shell
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Zygisk on KernelSU version/Zygisk on KernelSU 版本
|
||||
description: Don't use 'latest'. Specify actual version, otherwise your issue will be closed./不要填用“最新版”。给出具体版本号,不然 issue 会被关闭。
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Android version/Android 版本
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: KernelSU version/KernelSU 版本
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: latest
|
||||
attributes:
|
||||
label: Version requirement/版本要求
|
||||
options:
|
||||
- label: I am using latest debug CI version of Zygisk on KernelSU and enable verbose log/我正在使用最新 CI 调试版本 Zygisk on KernelSU 且启用详细日志
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs/日志
|
||||
description: For usage issues, please provide the log zip saved from KernelSU manager; for activation issues, please provide [bugreport](https://developer.android.com/studio/debug/bug-report). Without logs zip, the issue will be closed. /使用问题请提供从 KernelSU 管理器保存的日志压缩包;激活问题请提供 [bugreport](https://developer.android.google.cn/studio/debug/bug-report?hl=zh-cn) 日志。没有日志附件的问题会被关闭。
|
||||
placeholder: Upload logs zip by clicking the bar on the bottom. Upload logs to other websites or using external links is prohibited. /点击文本框底栏上传日志压缩包,禁止上传到其它网站或使用外链提供日志。
|
||||
validations:
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Feature request/新特性请求
|
||||
description: Suggest an idea./提出建议
|
||||
labels: [enhancement]
|
||||
title: "[Feature Request] Short description/简单描述"
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem?/你的请求是否与某个问题相关?
|
||||
placeholder: A clear and concise description of what the problem is./请清晰准确表述该问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like/描述你想要的解决方案
|
||||
placeholder: A clear and concise description of what you want to happen./请清晰准确描述新特性的预期行为
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context/其他信息
|
||||
placeholder: Add any other context or screenshots about the feature request here./其他关于新特性的信息或者截图
|
||||
validations:
|
||||
required: false
|
||||
23
.github/workflows/issue_moderator.yml
vendored
Normal file
23
.github/workflows/issue_moderator.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Issue moderator
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, reopened]
|
||||
|
||||
jobs:
|
||||
autoclose:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check issue
|
||||
uses: tachiyomiorg/issue-moderator-action@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
auto-close-rules: |
|
||||
[
|
||||
{
|
||||
"type": "title",
|
||||
"regex": ".*(Short description|简单描述).*",
|
||||
"message": "You did not fill out the description in the title/你没有填写标题"
|
||||
}
|
||||
]
|
||||
auto-close-ignore-label: do-not-autoclose
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
|
||||
|
||||
Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELOAD.
|
||||
Also works as standalone loader for Magisk.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -12,8 +12,8 @@ Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELO
|
||||
|
||||
### KernelSU
|
||||
|
||||
+ Minimal KernelSU version: 10654
|
||||
+ Minimal ksud version: 10670
|
||||
+ Minimal KernelSU version: 10940
|
||||
+ Minimal ksud version: 10942
|
||||
+ Kernel has full SELinux patch support
|
||||
|
||||
### Magisk
|
||||
|
||||
@@ -31,10 +31,10 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
|
||||
|
||||
val moduleId by extra("zygisksu")
|
||||
val moduleName by extra("Zygisk on KernelSU")
|
||||
val verName by extra("v4-0.6.4")
|
||||
val verName by extra("v4-0.7.1")
|
||||
val verCode by extra(gitCommitCount)
|
||||
val minKsuVersion by extra(10654)
|
||||
val minKsudVersion by extra(10670)
|
||||
val minKsuVersion by extra(10940)
|
||||
val minKsudVersion by extra(10942)
|
||||
val maxKsuVersion by extra(20000)
|
||||
val minMagiskVersion by extra(25208)
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace zygiskd {
|
||||
if (socket_utils::read_u8(fd) == 1) {
|
||||
return fd;
|
||||
} else {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,17 @@ void* DlopenExt(const char* path, int flags) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
void* DlopenMem(int memfd, int flags) {
|
||||
char path[PATH_MAX];
|
||||
sprintf(path, "/proc/self/fd/%d", memfd);
|
||||
return DlopenExt(path, flags);
|
||||
void* DlopenMem(int fd, int flags) {
|
||||
auto info = android_dlextinfo{
|
||||
.flags = ANDROID_DLEXT_USE_LIBRARY_FD,
|
||||
.library_fd = fd
|
||||
};
|
||||
|
||||
auto* handle = android_dlopen_ext("/jit-cache", flags, &info);
|
||||
if (handle) {
|
||||
LOGD("dlopen fd %d: %p", fd, handle);
|
||||
} else {
|
||||
LOGE("dlopen fd %d: %s", fd, dlerror());
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
|
||||
UniqueFd(Fd fd) : fd_(fd) {}
|
||||
|
||||
~UniqueFd() { close(fd_); }
|
||||
~UniqueFd() { if (fd_ >= 0) close(fd_); }
|
||||
|
||||
// Disallow copy
|
||||
UniqueFd(const UniqueFd&) = delete;
|
||||
|
||||
@@ -10,12 +10,8 @@ using namespace std::string_view_literals;
|
||||
|
||||
namespace {
|
||||
constexpr auto MODULE_DIR = "/data/adb/modules";
|
||||
|
||||
struct overlay_backup {
|
||||
std::string target;
|
||||
std::string vfs_option;
|
||||
std::string fs_option;
|
||||
};
|
||||
constexpr auto KSU_OVERLAY_SOURCE = "KSU";
|
||||
const std::vector<std::string> KSU_PARTITIONS{"/system", "/vendor", "/product", "/system_ext", "/odm", "/oem"};
|
||||
|
||||
void lazy_unmount(const char* mountpoint) {
|
||||
if (umount2(mountpoint, MNT_DETACH) != -1) {
|
||||
@@ -26,16 +22,9 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
#define PARSE_OPT(name, flag) \
|
||||
if (opt == (name)) { \
|
||||
flags |= (flag); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
void revert_unmount_ksu() {
|
||||
std::string ksu_loop;
|
||||
std::vector<std::string> targets;
|
||||
std::list<overlay_backup> backups;
|
||||
|
||||
// Unmount ksu module dir last
|
||||
targets.emplace_back(MODULE_DIR);
|
||||
@@ -50,17 +39,10 @@ void revert_unmount_ksu() {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
// Unmount ksu overlays
|
||||
if (info.type == "overlay") {
|
||||
if (str_contains(info.fs_option, MODULE_DIR)) {
|
||||
targets.emplace_back(info.target);
|
||||
} else {
|
||||
auto backup = overlay_backup{
|
||||
.target = info.target,
|
||||
.vfs_option = info.vfs_option,
|
||||
.fs_option = info.fs_option,
|
||||
};
|
||||
backups.emplace_back(backup);
|
||||
}
|
||||
if (info.type == "overlay"
|
||||
&& info.source == KSU_OVERLAY_SOURCE
|
||||
&& std::find(KSU_PARTITIONS.begin(), KSU_PARTITIONS.end(), info.target) != KSU_PARTITIONS.end()) {
|
||||
targets.emplace_back(info.target);
|
||||
}
|
||||
}
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
@@ -74,34 +56,6 @@ void revert_unmount_ksu() {
|
||||
for (auto& s: reversed(targets)) {
|
||||
lazy_unmount(s.data());
|
||||
}
|
||||
|
||||
// Affirm unmounted system overlays
|
||||
for (auto& info: parse_mount_info("self")) {
|
||||
if (info.type == "overlay") {
|
||||
backups.remove_if([&](overlay_backup& mnt) {
|
||||
return mnt.target == info.target && mnt.fs_option == info.fs_option;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Restore system overlays
|
||||
for (auto& mnt: backups) {
|
||||
auto opts = split_str(mnt.vfs_option, ",");
|
||||
opts.splice(opts.end(), split_str(mnt.fs_option, ","));
|
||||
unsigned long flags = 0;
|
||||
opts.remove_if([&](auto& opt) {
|
||||
PARSE_OPT(MNTOPT_RO, MS_RDONLY)
|
||||
PARSE_OPT(MNTOPT_NOSUID, MS_NOSUID)
|
||||
PARSE_OPT("relatime", MS_RELATIME)
|
||||
return false;
|
||||
});
|
||||
auto mnt_data = join_str(opts, ",");
|
||||
if (mount("overlay", mnt.target.data(), "overlay", flags, mnt_data.data()) != -1) {
|
||||
LOGD("Remounted (%s)", mnt.target.data());
|
||||
} else {
|
||||
PLOGE("Remount (%s, %s)", mnt.target.data(), mnt.fs_option.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void revert_unmount_magisk() {
|
||||
|
||||
@@ -46,7 +46,7 @@ androidComponents.onVariants { variant ->
|
||||
expand(
|
||||
"moduleId" to moduleId,
|
||||
"moduleName" to moduleName,
|
||||
"versionName" to "$verName ($verCode)",
|
||||
"versionName" to "$verName ($verCode-$variantLowered)",
|
||||
"versionCode" to verCode,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,12 +80,13 @@ extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip"
|
||||
extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip"
|
||||
extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR"
|
||||
|
||||
if [ "$DEBUG" = true ]; then
|
||||
ui_print "- Add debug SELinux policy"
|
||||
echo "allow crash_dump adb_data_file dir search" >> "$TMPDIR/sepolicy.rule"
|
||||
fi
|
||||
|
||||
if [ "$KSU" ]; then
|
||||
ui_print "- Checking SELinux patches"
|
||||
if [ "$(getprop ro.product.first_api_level)" -lt 31 ]; then
|
||||
echo "allow zygote appdomain_tmpfs file *" >> "$TMPDIR/sepolicy.rule"
|
||||
echo "allow zygote appdomain_tmpfs dir *" >> "$TMPDIR/sepolicy.rule"
|
||||
fi
|
||||
if ! check_sepolicy "$TMPDIR/sepolicy.rule"; then
|
||||
ui_print "*********************************************************"
|
||||
ui_print "! Unable to apply SELinux patches!"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
allow * tmpfs * *
|
||||
allow zygote appdomain_tmpfs dir *
|
||||
allow zygote appdomain_tmpfs file *
|
||||
|
||||
type magisk_file file_type
|
||||
typeattribute magisk_file mlstrustedobject
|
||||
|
||||
@@ -3,22 +3,25 @@ name = "zygiskd"
|
||||
authors = ["Nullptr"]
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.67"
|
||||
rust-version = "1.69"
|
||||
|
||||
[dependencies]
|
||||
android_logger = "0.13.0"
|
||||
anyhow = { version = "1.0.68", features = ["backtrace"] }
|
||||
clap = { version = "4.1.4", features = ["derive"] }
|
||||
const_format = "0.2.5"
|
||||
konst = "0.3.4"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.17"
|
||||
memfd = "0.6.2"
|
||||
nix = "0.26.2"
|
||||
num_enum = "0.5.9"
|
||||
once_cell = "1.17.1"
|
||||
passfd = "0.1.5"
|
||||
rand = "0.8.5"
|
||||
android_logger = "0.13"
|
||||
anyhow = { version = "1.0", features = ["backtrace"] }
|
||||
bitflags = { version = "2.3" }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
const_format = "0.2"
|
||||
futures = "0.3"
|
||||
konst = "0.3"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
memfd = "0.6"
|
||||
nix = { version = "0.26", features = ["process","poll"] }
|
||||
num_enum = "0.5"
|
||||
once_cell = "1.17"
|
||||
passfd = "0.1"
|
||||
rand = "0.8"
|
||||
tokio = { version = "1.28", features = ["full"] }
|
||||
|
||||
binder = { git = "https://github.com/Kernel-SU/binder_rs" }
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
use std::ffi::c_void;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::thread;
|
||||
use anyhow::Result;
|
||||
use nix::libc;
|
||||
use passfd::FdPassingExt;
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::dl;
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
pub fn entry(fd: i32) -> Result<()> {
|
||||
unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
|
||||
let mut stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||
let name = stream.read_string()?;
|
||||
let library = stream.recv_fd()?;
|
||||
let entry = load_module(library)?;
|
||||
unsafe { libc::close(library) };
|
||||
|
||||
let entry = match entry {
|
||||
Some(entry) => {
|
||||
log::debug!("Companion process created for `{name}`");
|
||||
stream.write_u8(1)?;
|
||||
entry
|
||||
}
|
||||
None => {
|
||||
log::debug!("No companion entry for `{name}`");
|
||||
stream.write_u8(0)?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let fd = stream.recv_fd()?;
|
||||
log::trace!("New companion request from module `{name}`");
|
||||
thread::spawn(move || {
|
||||
unsafe {
|
||||
let mut s = UnixStream::from_raw_fd(fd);
|
||||
match s.write_u8(1) { // Ack
|
||||
Ok(_) => entry(fd),
|
||||
Err(_) => log::warn!("Ack failed?"),
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn load_module(fd: RawFd) -> Result<Option<ZygiskCompanionEntryFn>> {
|
||||
unsafe {
|
||||
let path = format!("/proc/self/fd/{fd}");
|
||||
let handle = dl::dlopen(&path, libc::RTLD_NOW)?;
|
||||
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
||||
let entry = libc::dlsym(handle, symbol.as_ptr());
|
||||
if entry.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
||||
Ok(Some(fnptr))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use bitflags::bitflags;
|
||||
use const_format::concatcp;
|
||||
use konst::primitive::parse_i32;
|
||||
use konst::unwrap_ctx;
|
||||
@@ -46,8 +47,13 @@ pub enum DaemonSocketAction {
|
||||
}
|
||||
|
||||
// Zygisk process flags
|
||||
pub const PROCESS_GRANTED_ROOT: u32 = 1 << 0;
|
||||
pub const PROCESS_ON_DENYLIST: u32 = 1 << 1;
|
||||
pub const PROCESS_ROOT_IS_KSU: u32 = 1 << 29;
|
||||
pub const PROCESS_ROOT_IS_MAGISK: u32 = 1 << 30;
|
||||
pub const PROCESS_IS_SYSUI: u32 = 1 << 31;
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ProcessFlags: u32 {
|
||||
const PROCESS_GRANTED_ROOT = 1 << 0;
|
||||
const PROCESS_ON_DENYLIST = 1 << 1;
|
||||
const PROCESS_ROOT_IS_KSU = 1 << 29;
|
||||
const PROCESS_ROOT_IS_MAGISK = 1 << 30;
|
||||
const PROCESS_IS_SYSUI = 1 << 31;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod companion;
|
||||
mod constants;
|
||||
mod dl;
|
||||
mod magic;
|
||||
@@ -25,8 +24,6 @@ enum Commands {
|
||||
Watchdog,
|
||||
/// Start zygisk daemon
|
||||
Daemon,
|
||||
/// Start zygisk companion
|
||||
Companion { fd: i32 },
|
||||
}
|
||||
|
||||
|
||||
@@ -38,24 +35,24 @@ fn init_android_logger(tag: &str) {
|
||||
);
|
||||
}
|
||||
|
||||
fn start() -> Result<()> {
|
||||
async fn start() -> Result<()> {
|
||||
root_impl::setup();
|
||||
magic::setup()?;
|
||||
let cli = Args::parse();
|
||||
match cli.command {
|
||||
Commands::Watchdog => watchdog::entry()?,
|
||||
Commands::Watchdog => watchdog::entry().await?,
|
||||
Commands::Daemon => zygiskd::entry()?,
|
||||
Commands::Companion { fd } => companion::entry(fd)?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let process = std::env::args().next().unwrap();
|
||||
let nice_name = process.split('/').last().unwrap();
|
||||
init_android_logger(nice_name);
|
||||
|
||||
if let Err(e) = start() {
|
||||
if let Err(e) = start().await {
|
||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ use crate::constants::{MIN_KSU_VERSION, MAX_KSU_VERSION};
|
||||
const KERNEL_SU_OPTION: i32 = 0xdeadbeefu32 as i32;
|
||||
|
||||
const CMD_GET_VERSION: usize = 2;
|
||||
const CMD_GET_ALLOW_LIST: usize = 5;
|
||||
const CMD_GET_DENY_LIST: usize = 6;
|
||||
const CMD_UID_GRANTED_ROOT: usize = 12;
|
||||
const CMD_UID_SHOULD_UMOUNT: usize = 13;
|
||||
|
||||
pub enum Version {
|
||||
Supported,
|
||||
@@ -15,7 +15,7 @@ pub enum Version {
|
||||
|
||||
pub fn get_kernel_su() -> Option<Version> {
|
||||
let mut version = 0;
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32, 0, 0) };
|
||||
match version {
|
||||
0 => None,
|
||||
MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
|
||||
@@ -24,20 +24,14 @@ pub fn get_kernel_su() -> Option<Version> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
let mut size = 1024u32;
|
||||
let mut uids = vec![0; size as usize];
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_ALLOW_LIST, uids.as_mut_ptr(), &mut size as *mut u32) };
|
||||
uids.resize(size as usize, 0);
|
||||
uids.contains(&uid)
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
let mut granted = false;
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_UID_GRANTED_ROOT, uid, &mut granted as *mut bool, 0) };
|
||||
granted
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||
let mut size = 1024u32;
|
||||
let mut uids = vec![0; size as usize];
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_DENY_LIST, uids.as_mut_ptr(), &mut size as *mut u32) };
|
||||
uids.resize(size as usize, 0);
|
||||
uids.contains(&uid)
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
let mut umount = false;
|
||||
unsafe { prctl(KERNEL_SU_OPTION, CMD_UID_SHOULD_UMOUNT, uid, &mut umount as *mut bool, 0) };
|
||||
umount
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ pub fn get_magisk() -> Option<Version> {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
let output: Option<String> = Command::new("magisk")
|
||||
.arg("--sqlite")
|
||||
.arg("select uid from policies where policy=2")
|
||||
@@ -41,8 +40,7 @@ pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||
// TODO: uid_on_denylist
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
// TODO: uid_should_umount
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,21 +41,18 @@ pub fn get_impl() -> &'static RootImpl {
|
||||
unsafe { &ROOT_IMPL }
|
||||
}
|
||||
|
||||
// FIXME: Without #[inline(never)], this function will lag forever
|
||||
#[inline(never)]
|
||||
pub fn uid_on_allowlist(uid: i32) -> bool {
|
||||
pub fn uid_granted_root(uid: i32) -> bool {
|
||||
match get_impl() {
|
||||
RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid),
|
||||
RootImpl::Magisk => magisk::uid_on_allowlist(uid),
|
||||
RootImpl::KernelSU => kernelsu::uid_granted_root(uid),
|
||||
RootImpl::Magisk => magisk::uid_granted_root(uid),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn uid_on_denylist(uid: i32) -> bool {
|
||||
pub fn uid_should_umount(uid: i32) -> bool {
|
||||
match get_impl() {
|
||||
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
|
||||
RootImpl::Magisk => magisk::uid_on_denylist(uid),
|
||||
RootImpl::KernelSU => kernelsu::uid_should_umount(uid),
|
||||
RootImpl::Magisk => magisk::uid_should_umount(uid),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,17 @@ macro_rules! lp_select {
|
||||
($lp32:expr, $lp64:expr) => { $lp32 };
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $debug };
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[macro_export]
|
||||
macro_rules! debug_select {
|
||||
($debug:expr, $release:expr) => { $release };
|
||||
}
|
||||
|
||||
pub struct LateInit<T> {
|
||||
cell: OnceCell<T>,
|
||||
}
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
use crate::{constants, magic, 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, io, thread};
|
||||
use std::{fs, io};
|
||||
use std::ffi::CString;
|
||||
use std::future::Future;
|
||||
use std::io::{BufRead, Write};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
use binder::IBinder;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use nix::errno::Errno;
|
||||
use nix::libc;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use tokio::process::{Child, Command};
|
||||
use crate::utils::LateInit;
|
||||
|
||||
static LOCK: LateInit<UnixListener> = LateInit::new();
|
||||
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
||||
|
||||
pub fn entry() -> Result<()> {
|
||||
pub async fn entry() -> Result<()> {
|
||||
log::info!("Start zygisksu watchdog");
|
||||
check_permission()?;
|
||||
ensure_single_instance()?;
|
||||
mount_prop()?;
|
||||
mount_prop().await?;
|
||||
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();
|
||||
let end = spawn_daemon().await;
|
||||
set_prop_hint(constants::STATUS_CRASHED)?;
|
||||
end
|
||||
}
|
||||
@@ -63,9 +66,9 @@ fn ensure_single_instance() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mount_prop() -> Result<()> {
|
||||
async fn mount_prop() -> Result<()> {
|
||||
let module_prop = if let root_impl::RootImpl::Magisk = root_impl::get_impl() {
|
||||
let magisk_path = Command::new("magisk").arg("--path").output()?;
|
||||
let magisk_path = Command::new("magisk").arg("--path").output().await?;
|
||||
let mut magisk_path = String::from_utf8(magisk_path.stdout)?;
|
||||
magisk_path.pop(); // Removing '\n'
|
||||
let cwd = std::env::current_dir()?;
|
||||
@@ -136,46 +139,57 @@ fn check_and_set_hint() -> Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn spawn_daemon() -> Result<()> {
|
||||
async fn spawn_daemon() -> Result<()> {
|
||||
let mut lives = 5;
|
||||
loop {
|
||||
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
|
||||
let mut child_ids = vec![];
|
||||
|
||||
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))
|
||||
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
|
||||
let result = daemon.wait().await?;
|
||||
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
||||
Ok(())
|
||||
}
|
||||
if let Ok(it) = daemon32 {
|
||||
child_ids.push(it.id().unwrap());
|
||||
futures.push(Box::pin(spawn_daemon(it)));
|
||||
}
|
||||
if let Ok(it) = daemon64 {
|
||||
child_ids.push(it.id().unwrap());
|
||||
futures.push(Box::pin(spawn_daemon(it)));
|
||||
}
|
||||
|
||||
async fn binder_listener() -> Result<()> {
|
||||
let mut binder = loop {
|
||||
match binder::get_service("activity") {
|
||||
Some(binder) => break binder,
|
||||
None => {
|
||||
log::trace!("System server not ready, wait for 1s...");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
log::info!("System server ready, restore native bridge");
|
||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
||||
|
||||
loop {
|
||||
if binder.ping_binder().is_err() { break; }
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
log::error!("System server died");
|
||||
Ok(())
|
||||
}
|
||||
futures.push(Box::pin(binder_listener()));
|
||||
|
||||
if let Err(e) = futures.next().await.unwrap() {
|
||||
log::error!("{}", e);
|
||||
}
|
||||
|
||||
for child in child_ids {
|
||||
log::debug!("Killing child process {}", child);
|
||||
let _ = kill(Pid::from_raw(child as i32), Signal::SIGKILL);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
use crate::constants::DaemonSocketAction;
|
||||
use std::ffi::c_void;
|
||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||
use crate::utils::UnixStreamExt;
|
||||
use crate::{constants, lp_select, magic, root_impl, utils};
|
||||
use crate::{constants, dl, lp_select, magic, root_impl, utils};
|
||||
use anyhow::{bail, Result};
|
||||
use memfd::Memfd;
|
||||
use nix::{
|
||||
fcntl::{fcntl, FcntlArg, FdFlag},
|
||||
libc::self,
|
||||
};
|
||||
use passfd::FdPassingExt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::fs;
|
||||
use std::os::fd::{IntoRawFd, OwnedFd};
|
||||
use std::os::unix::{
|
||||
net::{UnixListener, UnixStream},
|
||||
prelude::AsRawFd,
|
||||
};
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use nix::libc;
|
||||
use nix::sys::stat::fstat;
|
||||
use nix::unistd::close;
|
||||
|
||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||
|
||||
struct Module {
|
||||
name: String,
|
||||
memfd: Memfd,
|
||||
companion: Mutex<Option<UnixStream>>,
|
||||
lib_fd: OwnedFd,
|
||||
entry: Option<ZygiskCompanionEntryFn>,
|
||||
}
|
||||
|
||||
struct Context {
|
||||
@@ -82,8 +82,8 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
return Ok(modules);
|
||||
}
|
||||
};
|
||||
for entry_result in dir.into_iter() {
|
||||
let entry = entry_result?;
|
||||
for entry in dir.into_iter() {
|
||||
let entry = entry?;
|
||||
let name = entry.file_name().into_string().unwrap();
|
||||
let so_path = entry.path().join(format!("zygisk/{arch}.so"));
|
||||
let disabled = entry.path().join("disable");
|
||||
@@ -91,30 +91,28 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
|
||||
continue;
|
||||
}
|
||||
log::info!(" Loading module `{name}`...");
|
||||
let memfd = match create_memfd(&so_path) {
|
||||
Ok(memfd) => memfd,
|
||||
let lib_fd = match create_library_fd(&so_path) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to create memfd for `{name}`: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let companion = match spawn_companion(&name, &memfd) {
|
||||
Ok(companion) => companion,
|
||||
Err(e) => {
|
||||
log::warn!(" Failed to spawn companion for `{name}`: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let companion = Mutex::new(companion);
|
||||
let module = Module { name, memfd, companion };
|
||||
let entry = resolve_module(&so_path.to_string_lossy())?;
|
||||
let module = Module { name, lib_fd, entry };
|
||||
modules.push(module);
|
||||
}
|
||||
|
||||
Ok(modules)
|
||||
}
|
||||
|
||||
fn create_memfd(so_path: &PathBuf) -> Result<Memfd> {
|
||||
#[cfg(debug_assertions)]
|
||||
fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
Ok(OwnedFd::from(fs::File::open(so_path)?))
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
||||
let opts = memfd::MemfdOptions::default().allow_sealing(true);
|
||||
let memfd = opts.create("jit-cache")?;
|
||||
let file = fs::File::open(so_path)?;
|
||||
@@ -129,7 +127,7 @@ fn create_memfd(so_path: &PathBuf) -> Result<Memfd> {
|
||||
seals.insert(memfd::FileSeal::SealSeal);
|
||||
memfd.add_seals(&seals)?;
|
||||
|
||||
Ok(memfd)
|
||||
Ok(OwnedFd::from(memfd.into_file()))
|
||||
}
|
||||
|
||||
fn create_daemon_socket() -> Result<UnixListener> {
|
||||
@@ -141,26 +139,16 @@ fn create_daemon_socket() -> Result<UnixListener> {
|
||||
Ok(listener)
|
||||
}
|
||||
|
||||
fn spawn_companion(name: &str, memfd: &Memfd) -> Result<Option<UnixStream>> {
|
||||
let (mut daemon, companion) = UnixStream::pair()?;
|
||||
// Remove FD_CLOEXEC flag
|
||||
fcntl(companion.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty()))?;
|
||||
|
||||
let process = std::env::args().next().unwrap();
|
||||
let nice_name = process.split('/').last().unwrap();
|
||||
Command::new(&process)
|
||||
.arg0(format!("{}-{}", nice_name, name))
|
||||
.arg("companion")
|
||||
.arg(format!("{}", companion.as_raw_fd()))
|
||||
.spawn()?;
|
||||
drop(companion);
|
||||
|
||||
daemon.write_string(name)?;
|
||||
daemon.send_fd(memfd.as_raw_fd())?;
|
||||
match daemon.read_u8()? {
|
||||
0 => Ok(None),
|
||||
1 => Ok(Some(daemon)),
|
||||
_ => bail!("Invalid companion response"),
|
||||
fn resolve_module(path: &str) -> Result<Option<ZygiskCompanionEntryFn>> {
|
||||
unsafe {
|
||||
let handle = dl::dlopen(path, libc::RTLD_NOW)?;
|
||||
let symbol = std::ffi::CString::new("zygisk_companion_entry")?;
|
||||
let entry = libc::dlsym(handle, symbol.as_ptr());
|
||||
if entry.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
let fnptr = std::mem::transmute::<*mut c_void, ZygiskCompanionEntryFn>(entry);
|
||||
Ok(Some(fnptr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,43 +176,51 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
||||
}
|
||||
DaemonSocketAction::GetProcessFlags => {
|
||||
let uid = stream.read_u32()? as i32;
|
||||
let mut flags = 0u32;
|
||||
if root_impl::uid_on_allowlist(uid) {
|
||||
flags |= constants::PROCESS_GRANTED_ROOT;
|
||||
let mut flags = ProcessFlags::empty();
|
||||
if root_impl::uid_granted_root(uid) {
|
||||
flags |= ProcessFlags::PROCESS_GRANTED_ROOT;
|
||||
}
|
||||
if root_impl::uid_on_denylist(uid) {
|
||||
flags |= constants::PROCESS_ON_DENYLIST;
|
||||
if root_impl::uid_should_umount(uid) {
|
||||
flags |= ProcessFlags::PROCESS_ON_DENYLIST;
|
||||
}
|
||||
match root_impl::get_impl() {
|
||||
root_impl::RootImpl::KernelSU => flags |= constants::PROCESS_ROOT_IS_KSU,
|
||||
root_impl::RootImpl::Magisk => flags |= constants::PROCESS_ROOT_IS_MAGISK,
|
||||
root_impl::RootImpl::KernelSU => flags |= ProcessFlags::PROCESS_ROOT_IS_KSU,
|
||||
root_impl::RootImpl::Magisk => flags |= ProcessFlags::PROCESS_ROOT_IS_MAGISK,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// TODO: PROCESS_IS_SYSUI?
|
||||
stream.write_u32(flags)?;
|
||||
log::trace!("Uid {} granted root: {}", uid, flags.contains(ProcessFlags::PROCESS_GRANTED_ROOT));
|
||||
log::trace!("Uid {} on denylist: {}", uid, flags.contains(ProcessFlags::PROCESS_ON_DENYLIST));
|
||||
stream.write_u32(flags.bits())?;
|
||||
}
|
||||
DaemonSocketAction::ReadModules => {
|
||||
stream.write_usize(context.modules.len())?;
|
||||
for module in context.modules.iter() {
|
||||
stream.write_string(&module.name)?;
|
||||
stream.send_fd(module.memfd.as_raw_fd())?;
|
||||
stream.send_fd(module.lib_fd.as_raw_fd())?;
|
||||
}
|
||||
}
|
||||
DaemonSocketAction::RequestCompanionSocket => {
|
||||
let index = stream.read_usize()?;
|
||||
let module = &context.modules[index];
|
||||
let mut companion = module.companion.lock().unwrap();
|
||||
match companion.as_ref() {
|
||||
Some(sock) => {
|
||||
if let Err(_) = sock.send_fd(stream.as_raw_fd()) {
|
||||
log::error!("Companion of module `{}` crashed", module.name);
|
||||
companion.take();
|
||||
stream.write_u8(0)?;
|
||||
}
|
||||
// Ok: Send by companion
|
||||
}
|
||||
match module.entry {
|
||||
None => {
|
||||
stream.write_u8(0)?;
|
||||
return Ok(());
|
||||
}
|
||||
Some(companion) => {
|
||||
stream.write_u8(1)?;
|
||||
let fd = stream.into_raw_fd();
|
||||
let st0 = fstat(fd)?;
|
||||
unsafe { companion(fd); }
|
||||
// Only close client if it is the same file so we don't
|
||||
// accidentally close a re-used file descriptor.
|
||||
// This check is required because the module companion
|
||||
// handler could've closed the file descriptor already.
|
||||
if let Ok(st1) = fstat(fd) {
|
||||
if st0.st_dev == st1.st_dev && st0.st_ino == st1.st_ino {
|
||||
close(fd)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user