9 Commits

Author SHA1 Message Date
Nullptr
6d9cc560cc Bump to 0.5.1 2023-02-24 22:24:45 +08:00
Nullptr
f395cfb490 Fix stupid remount bug 2023-02-24 16:43:53 +08:00
Nullptr
03575edd96 Bump to 0.5.0 2023-02-24 09:34:48 +08:00
Nullptr
915749e59b Never allow multiple root implementation 2023-02-24 09:31:44 +08:00
Nullptr
d08b415577 Require ksud version 2023-02-24 08:57:24 +08:00
Nullptr
f27aed5068 Change memfd name to jit-cache 2023-02-20 16:35:02 +08:00
Nullptr
5365ab1f12 Check correct KernelSU version 2023-02-20 16:28:15 +08:00
Nullptr
b99d042002 Implement GetProcessFlags for KernelSU 2023-02-19 13:29:36 +08:00
Nullptr
57d3d8a0ba Refine unmount 2023-02-18 19:14:15 +08:00
27 changed files with 371 additions and 64 deletions

View File

@@ -6,9 +6,10 @@ Also works as standalone loader for Magisk on purpose of getting rid of LD_PRELO
## Requirements ## Requirements
+ Minimal KernelSU version: 10575 + Minimal KernelSU version: 10654
+ Minimal ksud version: 10616 + Minimal ksud version: 10647
+ Full SELinux patch support (If non-gki kernel) + Full SELinux patch support (If non-gki kernel)
+ No multiple root implementation installed
## Compatibility ## Compatibility

View File

@@ -31,8 +31,12 @@ val gitCommitHash = "git rev-parse --verify --short HEAD".execute()
val moduleId by extra("zygisksu") val moduleId by extra("zygisksu")
val moduleName by extra("Zygisk on KernelSU") val moduleName by extra("Zygisk on KernelSU")
val verName by extra("v4-0.4.1") val verName by extra("v4-0.5.1")
val verCode by extra(gitCommitCount) val verCode by extra(gitCommitCount)
val minKsuVersion by extra(10654)
val minKsudVersion by extra(10647)
val maxKsuVersion by extra(20000)
val minMagiskVersion by extra(25000)
val androidMinSdkVersion by extra(29) val androidMinSdkVersion by extra(29)
val androidTargetSdkVersion by extra(33) val androidTargetSdkVersion by extra(33)

View File

@@ -58,6 +58,17 @@ namespace zygiskd {
return socket_utils::read_string(fd); return socket_utils::read_string(fd);
} }
uint32_t GetProcessFlags(uid_t uid) {
UniqueFd fd = Connect(1);
if (fd == -1) {
PLOGE("GetProcessFlags");
return 0;
}
socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags);
socket_utils::write_u32(fd, uid);
return socket_utils::read_u32(fd);
}
std::vector<Module> ReadModules() { std::vector<Module> ReadModules() {
std::vector<Module> modules; std::vector<Module> modules;
UniqueFd fd = Connect(1); UniqueFd fd = Connect(1);

View File

@@ -90,6 +90,10 @@ namespace socket_utils {
return read_exact_or<uint8_t>(fd, 0); return read_exact_or<uint8_t>(fd, 0);
} }
uint32_t read_u32(int fd) {
return read_exact_or<uint32_t>(fd, 0);
}
size_t read_usize(int fd) { size_t read_usize(int fd) {
return read_exact_or<size_t>(fd, 0); return read_exact_or<size_t>(fd, 0);
} }
@@ -110,6 +114,10 @@ namespace socket_utils {
return write_exact<uint8_t>(fd, val); return write_exact<uint8_t>(fd, val);
} }
bool write_u32(int fd, uint32_t val) {
return write_exact<uint32_t>(fd, val);
}
bool write_string(int fd, std::string_view str) { bool write_string(int fd, std::string_view str) {
return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size()); return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size());
} }

View File

@@ -54,6 +54,7 @@ namespace zygiskd {
PingHeartBeat, PingHeartBeat,
RequestLogcatFd, RequestLogcatFd,
ReadNativeBridge, ReadNativeBridge,
GetProcessFlags,
ReadModules, ReadModules,
RequestCompanionSocket, RequestCompanionSocket,
GetModuleDir, GetModuleDir,
@@ -67,6 +68,8 @@ namespace zygiskd {
std::vector<Module> ReadModules(); std::vector<Module> ReadModules();
uint32_t GetProcessFlags(uid_t uid);
int ConnectCompanion(size_t index); int ConnectCompanion(size_t index);
int GetModuleDir(size_t index); int GetModuleDir(size_t index);

View File

@@ -13,12 +13,16 @@ namespace socket_utils {
uint8_t read_u8(int fd); uint8_t read_u8(int fd);
uint32_t read_u32(int fd);
size_t read_usize(int fd); size_t read_usize(int fd);
std::string read_string(int fd); std::string read_string(int fd);
bool write_u8(int fd, uint8_t val); bool write_u8(int fd, uint8_t val);
bool write_u32(int fd, uint32_t val);
int recv_fd(int fd); int recv_fd(int fd);
bool write_usize(int fd, size_t val); bool write_usize(int fd, size_t val);

View File

@@ -26,10 +26,3 @@ void entry(void *handle) {
LOGD("Load injector successfully"); LOGD("Load injector successfully");
hook_functions(); hook_functions();
} }
// The following code runs in zygote/app process
static inline bool should_load_modules(uint32_t flags) {
return (flags & UNMOUNT_MASK) != UNMOUNT_MASK &&
(flags & PROCESS_IS_MAGISK_APP) != PROCESS_IS_MAGISK_APP;
}

View File

@@ -1,22 +0,0 @@
#include <sys/mount.h>
#include "logging.h"
#include "misc.hpp"
#include "zygisk.hpp"
using namespace std::string_view_literals;
static void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1)
LOGD("Unmounted (%s)", mountpoint);
}
void revert_unmount() {
parse_mnt("/proc/self/mounts", [](mntent* mentry) {
if (mentry->mnt_fsname == "/data/adb/ksu/modules"sv ||
std::string_view(mentry->mnt_opts).find("/data/adb/ksu/modules") != std::string_view::npos) {
lazy_unmount(mentry->mnt_fsname);
}
return true;
});
}

View File

@@ -566,15 +566,13 @@ void HookContext::run_modules_post() {
/* Zygisksu changed: Load module fds */ /* Zygisksu changed: Load module fds */
void HookContext::app_specialize_pre() { void HookContext::app_specialize_pre() {
flags[APP_SPECIALIZE] = true; flags[APP_SPECIALIZE] = true;
info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid);
run_modules_pre(); run_modules_pre();
} }
void HookContext::app_specialize_post() { void HookContext::app_specialize_post() {
run_modules_post(); run_modules_post();
if (info_flags & PROCESS_IS_MAGISK_APP) {
setenv("ZYGISK_ENABLED", "1", 1);
}
// Cleanups // Cleanups
env->ReleaseStringUTFChars(args.app->nice_name, process); env->ReleaseStringUTFChars(args.app->nice_name, process);

View File

@@ -35,6 +35,31 @@ void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn) {
} }
} }
std::list<std::string> split_str(std::string_view s, std::string_view delimiter) {
std::list<std::string> ret;
size_t pos = 0;
while (pos < s.size()) {
auto next = s.find(delimiter, pos);
if (next == std::string_view::npos) {
ret.emplace_back(s.substr(pos));
break;
}
ret.emplace_back(s.substr(pos, next - pos));
pos = next + delimiter.size();
}
return ret;
}
std::string join_str(const std::list<std::string>& list, std::string_view delimiter) {
std::string ret;
for (auto& s : list) {
if (!ret.empty())
ret += delimiter;
ret += s;
}
return ret;
}
sDIR make_dir(DIR *dp) { sDIR make_dir(DIR *dp) {
return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; }); return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; });
} }

View File

@@ -2,10 +2,12 @@
#include <dirent.h> #include <dirent.h>
#include <functional> #include <functional>
#include <list>
#include <memory> #include <memory>
#include <mntent.h> #include <mntent.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <string>
#include <string_view> #include <string_view>
#include "logging.h" #include "logging.h"
@@ -34,6 +36,16 @@ private:
using thread_entry = void *(*)(void *); using thread_entry = void *(*)(void *);
int new_daemon_thread(thread_entry entry, void *arg); int new_daemon_thread(thread_entry entry, void *arg);
static inline bool str_contains(std::string_view s, std::string_view ss) {
return s.find(ss) != std::string_view::npos;
}
static inline bool str_starts(std::string_view s, std::string_view ss) {
return s.size() >= ss.size() && s.compare(0, ss.size(), ss) == 0;
}
static inline bool str_ends(std::string_view s, std::string_view ss) {
return s.size() >= ss.size() && s.compare(s.size() - ss.size(), std::string_view::npos, ss) == 0;
}
template<typename T, typename Impl> template<typename T, typename Impl>
class stateless_allocator { class stateless_allocator {
public: public:
@@ -78,6 +90,25 @@ static inline sFILE xopen_file(int fd, const char *mode) {
return make_file(fdopen(fd, mode)); return make_file(fdopen(fd, mode));
} }
template <typename T>
class reversed_container {
public:
reversed_container(T &base) : base(base) {}
decltype(std::declval<T>().rbegin()) begin() { return base.rbegin(); }
decltype(std::declval<T>().crbegin()) begin() const { return base.crbegin(); }
decltype(std::declval<T>().crbegin()) cbegin() const { return base.crbegin(); }
decltype(std::declval<T>().rend()) end() { return base.rend(); }
decltype(std::declval<T>().crend()) end() const { return base.crend(); }
decltype(std::declval<T>().crend()) cend() const { return base.crend(); }
private:
T &base;
};
template <typename T>
reversed_container<T> reversed(T &base) {
return reversed_container<T>(base);
}
template<class T> template<class T>
static inline void default_new(T *&p) { p = new T(); } static inline void default_new(T *&p) { p = new T(); }
@@ -97,6 +128,10 @@ int parse_int(std::string_view s);
void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn); void parse_mnt(const char* file, const std::function<bool(mntent*)>& fn);
std::list<std::string> split_str(std::string_view s, std::string_view delimiter);
std::string join_str(const std::list<std::string>& list, std::string_view delimiter);
template <typename T> template <typename T>
static inline T align_to(T v, int a) { static inline T align_to(T v, int a) {
static_assert(std::is_integral<T>::value); static_assert(std::is_integral<T>::value);

View File

@@ -111,12 +111,9 @@ namespace {
PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT, PROCESS_GRANTED_ROOT = zygisk::StateFlag::PROCESS_GRANTED_ROOT,
PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST, PROCESS_ON_DENYLIST = zygisk::StateFlag::PROCESS_ON_DENYLIST,
PROCESS_IS_SYS_UI = (1u << 29), PROCESS_IS_SYS_UI = (1u << 31),
DENYLIST_ENFORCING = (1u << 30),
PROCESS_IS_MAGISK_APP = (1u << 31),
UNMOUNT_MASK = (PROCESS_ON_DENYLIST | DENYLIST_ENFORCING), PRIVATE_MASK = PROCESS_IS_SYS_UI
PRIVATE_MASK = (PROCESS_IS_SYS_UI | DENYLIST_ENFORCING | PROCESS_IS_MAGISK_APP)
}; };
struct api_abi_base { struct api_abi_base {

View File

@@ -0,0 +1,71 @@
#include <sys/mount.h>
#include "logging.h"
#include "misc.hpp"
#include "zygisk.hpp"
using namespace std::string_view_literals;
static void lazy_unmount(const char* mountpoint) {
if (umount2(mountpoint, MNT_DETACH) != -1) {
LOGD("Unmounted (%s)", mountpoint);
} else {
LOGW("Failed to unmount: %s (%s)", strerror(errno), mountpoint);
}
}
#define PARSE_OPT(name, flag) \
if (opt == name) { \
flags |= (flag); \
return true; \
}
void revert_unmount() {
std::vector<std::string> targets;
std::list<std::pair<std::string, std::string>> backups;
targets.emplace_back("/data/adb/ksu/modules");
parse_mnt("/proc/self/mounts", [&](mntent* mentry) {
if (str_starts(mentry->mnt_fsname, "/data/adb/")) {
targets.emplace_back(mentry->mnt_dir);
}
if (mentry->mnt_type == "overlay"sv) {
if (str_contains(mentry->mnt_opts, "/data/adb/ksu/modules")) {
targets.emplace_back(mentry->mnt_dir);
} else {
backups.emplace_back(mentry->mnt_dir, mentry->mnt_opts);
}
}
return true;
});
for (auto& s: reversed(targets)) {
lazy_unmount(s.data());
}
parse_mnt("/proc/self/mounts", [&](mntent* mentry) {
if (mentry->mnt_type == "overlay"sv) {
backups.remove_if([&](auto& mnt) {
return mnt.first == mentry->mnt_dir && mnt.second == mentry->mnt_opts;
});
}
return true;
});
for (auto& mnt: backups) {
auto opts = split_str(mnt.second, ",");
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.first.data(), "overlay", flags, mnt_data.data()) != -1) {
LOGD("Remounted (%s)", mnt.first.data());
} else {
LOGW("Failed to remount: %s (%s, %s)", strerror(errno), mnt.first.data(), mnt_data.data());
}
}
}

View File

@@ -11,6 +11,10 @@ val moduleId: String by rootProject.extra
val moduleName: String by rootProject.extra val moduleName: String by rootProject.extra
val verCode: Int by rootProject.extra val verCode: Int by rootProject.extra
val verName: String by rootProject.extra val verName: String by rootProject.extra
val minKsuVersion: Int by rootProject.extra
val minKsudVersion: Int by rootProject.extra
val maxKsuVersion: Int by rootProject.extra
val minMagiskVersion: Int by rootProject.extra
android.buildFeatures { android.buildFeatures {
androidResources = false androidResources = false
@@ -49,7 +53,11 @@ androidComponents.onVariants { variant ->
from("$projectDir/src") { from("$projectDir/src") {
include("customize.sh", "daemon.sh") include("customize.sh", "daemon.sh")
val tokens = mapOf( val tokens = mapOf(
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false" "DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
"MIN_KSU_VERSION" to "$minKsuVersion",
"MIN_KSUD_VERSION" to "$minKsudVersion",
"MAX_KSU_VERSION" to "$maxKsuVersion",
"MIN_MAGISK_VERSION" to "$minMagiskVersion",
) )
filter<ReplaceTokens>("tokens" to tokens) filter<ReplaceTokens>("tokens" to tokens)
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf")) filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))

View File

@@ -2,19 +2,41 @@
SKIPUNZIP=1 SKIPUNZIP=1
DEBUG=@DEBUG@ DEBUG=@DEBUG@
MIN_KSU_VERSION=@MIN_KSU_VERSION@
MIN_KSUD_VERSION=@MIN_KSUD_VERSION@
MAX_KSU_VERSION=@MAX_KSU_VERSION@
MIN_MAGISK_VERSION=@MIN_MAGISK_VERSION@
if [ "$BOOTMODE" ] && [ "$KSU" ]; then if [ "$BOOTMODE" ] && [ "$KSU" ]; then
ui_print "- Installing from KernelSU app" ui_print "- Installing from KernelSU app"
ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)" ui_print "- KernelSU version: $KSU_KERNEL_VER_CODE (kernel) + $KSU_VER_CODE (ksud)"
if [ "$KSU_KERNEL_VER_CODE" ] && [ "$KSU_KERNEL_VER_CODE" -lt 10575 ]; then if ! [ "$KSU_KERNEL_VER_CODE" ] || [ "$KSU_KERNEL_VER_CODE" -lt "$MIN_KSU_VERSION" ]; then
ui_print "*********************************************************" ui_print "*********************************************************"
ui_print "! KernelSU version is too old!" ui_print "! KernelSU version is too old!"
ui_print "! Please update KernelSU to latest version" ui_print "! Please update KernelSU to latest version"
abort "*********************************************************" abort "*********************************************************"
elif [ "$KSU_KERNEL_VER_CODE" -ge "$MAX_KSU_VERSION" ]; then
ui_print "*********************************************************"
ui_print "! KernelSU version abnormal!"
ui_print "! Please integrate KernelSU into your kernel"
ui_print " as submodule instead of copying the source code"
abort "*********************************************************"
fi
if ! [ "$KSU_VER_CODE" ] || [ "$KSU_VER_CODE" -lt "$MIN_KSUD_VERSION" ]; then
ui_print "*********************************************************"
ui_print "! ksud version is too old!"
ui_print "! Please update KernelSU Manager to latest version"
abort "*********************************************************"
fi
if [ "$(which magisk)" ]; then
ui_print "*********************************************************"
ui_print "! Multiple root implementation is NOT supported!"
ui_print "! Please uninstall Magisk before installing Zygisksu"
abort "*********************************************************"
fi fi
elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then
ui_print "- Installing from Magisk app" ui_print "- Installing from Magisk app"
if [ "$MAGISK_VER_CODE" -lt 25000 ]; then if [ "$MAGISK_VER_CODE" -lt "$MIN_MAGISK_VERSION" ]; then
ui_print "*********************************************************" ui_print "*********************************************************"
ui_print "! Magisk version is too old!" ui_print "! Magisk version is too old!"
ui_print "! Please update Magisk to latest version" ui_print "! Please update Magisk to latest version"
@@ -72,10 +94,8 @@ ui_print "- Extracting module files"
extract "$ZIPFILE" 'daemon.sh' "$MODPATH" extract "$ZIPFILE" 'daemon.sh' "$MODPATH"
extract "$ZIPFILE" 'module.prop' "$MODPATH" extract "$ZIPFILE" 'module.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH" extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH" extract "$ZIPFILE" 'service.sh' "$MODPATH"
if [ "$KSU" ]; then
extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH"
fi
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true

View File

@@ -8,7 +8,7 @@ fi
cd "$MODDIR" cd "$MODDIR"
export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge) export NATIVE_BRIDGE=$(getprop ro.dalvik.vm.native.bridge)
if [ $(which magisk) ] && [ ".." -ef "/data/adb/modules" ]; then if [ "$(which magisk)" ] && [ ".." -ef "/data/adb/modules" ]; then
for file in ../*; do for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/post-fs-data.sh" ]; then if [ -f "$file/post-fs-data.sh" ]; then
@@ -21,4 +21,4 @@ if [ $(which magisk) ] && [ ".." -ef "/data/adb/modules" ]; then
done done
fi fi
unshare -m sh -c "./daemon.sh $@&" sh -c "./daemon.sh $@&"

View File

@@ -10,3 +10,6 @@ allow * magisk_file lnk_file *
allow * magisk_file sock_file * allow * magisk_file sock_file *
allow system_server system_server process execmem allow system_server system_server process execmem
allow zygote mnt_vendor_file dir search
allow zygote system_file dir mounton
allow zygote labeledfs filesystem mount

View File

@@ -7,7 +7,7 @@ fi
cd "$MODDIR" cd "$MODDIR"
if [ $(which magisk) ] && [ ".." -ef "/data/adb/modules" ]; then if [ "$(which magisk)" ] && [ ".." -ef "/data/adb/modules" ]; then
for file in ../*; do for file in ../*; do
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/service.sh" ]; then if [ -f "$file/service.sh" ]; then

View File

@@ -10,11 +10,13 @@ android_logger = "0.12.0"
anyhow = { version = "1.0.68", features = ["backtrace"] } anyhow = { version = "1.0.68", features = ["backtrace"] }
clap = { version = "4.1.4", features = ["derive"] } clap = { version = "4.1.4", features = ["derive"] }
const_format = "0.2.5" const_format = "0.2.5"
konst = "0.3.4"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4.17" log = "0.4.17"
memfd = "0.6.2" memfd = "0.6.2"
nix = "0.26.2" nix = "0.26.2"
num_enum = "0.5.9" num_enum = "0.5.9"
once_cell = "1.17.1"
passfd = "0.1.5" passfd = "0.1.5"
rand = "0.8.5" rand = "0.8.5"

View File

@@ -5,6 +5,9 @@ plugins {
val verName: String by rootProject.extra val verName: String by rootProject.extra
val verCode: Int by rootProject.extra val verCode: Int by rootProject.extra
val minKsuVersion: Int by rootProject.extra
val maxKsuVersion: Int by rootProject.extra
val minMagiskVersion: Int by rootProject.extra
android.buildFeatures { android.buildFeatures {
androidResources = false androidResources = false
@@ -23,5 +26,8 @@ cargo {
spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path) spec.environment("ANDROID_NDK_HOME", android.ndkDirectory.path)
spec.environment("VERSION_CODE", verCode) spec.environment("VERSION_CODE", verCode)
spec.environment("VERSION_NAME", verName) spec.environment("VERSION_NAME", verName)
spec.environment("MIN_KSU_VERSION", minKsuVersion)
spec.environment("MAX_KSU_VERSION", maxKsuVersion)
spec.environment("MIN_MAGISK_VERSION", minMagiskVersion)
} }
} }

View File

@@ -1,10 +1,15 @@
use const_format::concatcp; use const_format::concatcp;
use konst::primitive::parse_i32;
use konst::unwrap_ctx;
use log::LevelFilter; use log::LevelFilter;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
pub const VERSION_NAME: &str = env!("VERSION_NAME"); pub const VERSION_NAME: &str = env!("VERSION_NAME");
pub const VERSION_CODE: &str = env!("VERSION_CODE"); pub const VERSION_CODE: &str = env!("VERSION_CODE");
pub const VERSION_FULL: &str = concatcp!(VERSION_NAME, " (", VERSION_CODE, ")"); pub const VERSION_FULL: &str = concatcp!(VERSION_NAME, " (", VERSION_CODE, ")");
pub const MIN_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_KSU_VERSION")));
pub const MAX_KSU_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MAX_KSU_VERSION")));
pub const MIN_MAGISK_VERSION: i32 = unwrap_ctx!(parse_i32(env!("MIN_MAGISK_VERSION")));
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace; pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
@@ -38,7 +43,13 @@ pub enum DaemonSocketAction {
PingHeartbeat, PingHeartbeat,
RequestLogcatFd, RequestLogcatFd,
ReadNativeBridge, ReadNativeBridge,
GetProcessFlags,
ReadModules, ReadModules,
RequestCompanionSocket, RequestCompanionSocket,
GetModuleDir, GetModuleDir,
} }
// Zygisk process flags
pub const PROCESS_GRANTED_ROOT: u32 = 1 << 0;
pub const PROCESS_ON_DENYLIST: u32 = 1 << 1;
pub const PROCESS_IS_SYSUI: u32 = 1 << 31;

View File

@@ -3,10 +3,12 @@
mod companion; mod companion;
mod constants; mod constants;
mod dl; mod dl;
mod root_impl;
mod utils; mod utils;
mod watchdog; mod watchdog;
mod zygiskd; mod zygiskd;
use anyhow::Result;
use clap::{Subcommand, Parser}; use clap::{Subcommand, Parser};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -35,19 +37,23 @@ fn init_android_logger(tag: &str) {
); );
} }
fn start() -> Result<()> {
root_impl::setup()?;
let cli = Args::parse();
match cli.command {
Commands::Watchdog => watchdog::entry()?,
Commands::Daemon => zygiskd::entry()?,
Commands::Companion { fd } => companion::entry(fd)?,
};
Ok(())
}
fn main() { fn main() {
let process = std::env::args().next().unwrap(); let process = std::env::args().next().unwrap();
let nice_name = process.split('/').last().unwrap(); let nice_name = process.split('/').last().unwrap();
init_android_logger(nice_name); init_android_logger(nice_name);
let cli = Args::parse(); if let Err(e) = start() {
let result = match cli.command {
Commands::Watchdog => watchdog::entry(),
Commands::Daemon => zygiskd::entry(),
Commands::Companion { fd } => companion::entry(fd),
};
if let Err(e) = &result {
log::error!("Crashed: {}\n{}", e, e.backtrace()); log::error!("Crashed: {}\n{}", e, e.backtrace());
} }
} }

View File

@@ -0,0 +1,36 @@
use anyhow::{bail, Result};
use nix::libc::prctl;
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;
pub fn is_kernel_su() -> Result<bool> {
let mut version = 0;
unsafe { prctl(KERNEL_SU_OPTION, CMD_GET_VERSION, &mut version as *mut i32) };
return match version {
0 => Ok(false),
MIN_KSU_VERSION..=MAX_KSU_VERSION => Ok(true),
1..=MIN_KSU_VERSION => bail!("KernelSU version too old: {}", version),
_ => bail!("KernelSU version abnormal: {}", version)
}
}
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_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)
}

View File

@@ -0,0 +1,30 @@
use anyhow::{bail, Result};
use std::process::{Command, Stdio};
use crate::constants::MIN_MAGISK_VERSION;
pub fn is_magisk() -> Result<bool> {
let version: Option<i32> = Command::new("magisk")
.arg("-V")
.stdout(Stdio::piped())
.spawn().ok()
.and_then(|child| child.wait_with_output().ok())
.and_then(|output| String::from_utf8(output.stdout).ok())
.and_then(|output| output.trim().parse().ok());
if let Some(version) = version {
if version < MIN_MAGISK_VERSION {
bail!("Magisk version too old: {}", version);
}
return Ok(true);
}
Ok(false)
}
pub fn uid_on_allowlist(uid: i32) -> bool {
// TODO: uid_on_allowlist
return false;
}
pub fn uid_on_denylist(uid: i32) -> bool {
// TODO: uid_on_denylist
return false;
}

View File

@@ -0,0 +1,40 @@
mod kernelsu;
mod magisk;
use once_cell::sync::OnceCell;
use anyhow::{bail, Result};
enum RootImpl {
KernelSU,
Magisk,
}
static ROOT_IMPL: OnceCell<RootImpl> = OnceCell::new();
pub fn setup() -> Result<()> {
if kernelsu::is_kernel_su()? {
if let Ok(true) = magisk::is_magisk() {
bail!("Multiple root implementation");
}
let _ = ROOT_IMPL.set(RootImpl::KernelSU);
} else if magisk::is_magisk()? {
let _ = ROOT_IMPL.set(RootImpl::Magisk);
} else {
bail!("Unknown root implementation");
}
Ok(())
}
pub fn uid_on_allowlist(uid: i32) -> bool {
match ROOT_IMPL.get().unwrap() {
RootImpl::KernelSU => kernelsu::uid_on_allowlist(uid),
RootImpl::Magisk => magisk::uid_on_allowlist(uid),
}
}
pub fn uid_on_denylist(uid: i32) -> bool {
match ROOT_IMPL.get().unwrap() {
RootImpl::KernelSU => kernelsu::uid_on_denylist(uid),
RootImpl::Magisk => magisk::uid_on_denylist(uid),
}
}

View File

@@ -40,6 +40,7 @@ pub trait UnixStreamExt {
fn read_usize(&mut self) -> Result<usize>; fn read_usize(&mut self) -> Result<usize>;
fn read_string(&mut self) -> Result<String>; fn read_string(&mut self) -> Result<String>;
fn write_u8(&mut self, value: u8) -> Result<()>; fn write_u8(&mut self, value: u8) -> Result<()>;
fn write_u32(&mut self, value: u32) -> Result<()>;
fn write_usize(&mut self, value: usize) -> Result<()>; fn write_usize(&mut self, value: usize) -> Result<()>;
fn write_string(&mut self, value: &str) -> Result<()>; fn write_string(&mut self, value: &str) -> Result<()>;
} }
@@ -75,6 +76,11 @@ impl UnixStreamExt for UnixStream {
Ok(()) Ok(())
} }
fn write_u32(&mut self, value: u32) -> Result<()> {
self.write_all(&value.to_ne_bytes())?;
Ok(())
}
fn write_usize(&mut self, value: usize) -> Result<()> { fn write_usize(&mut self, value: usize) -> Result<()> {
self.write_all(&value.to_ne_bytes())?; self.write_all(&value.to_ne_bytes())?;
Ok(()) Ok(())

View File

@@ -1,6 +1,6 @@
use crate::constants::DaemonSocketAction; use crate::constants::DaemonSocketAction;
use crate::utils::UnixStreamExt; use crate::utils::UnixStreamExt;
use crate::{constants, lp_select, utils}; use crate::{constants, lp_select, root_impl, utils};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use memfd::Memfd; use memfd::Memfd;
use nix::{ use nix::{
@@ -67,10 +67,10 @@ fn get_arch() -> Result<&'static str> {
let output = Command::new("getprop").arg("ro.product.cpu.abi").output()?; let output = Command::new("getprop").arg("ro.product.cpu.abi").output()?;
let system_arch = String::from_utf8(output.stdout)?; let system_arch = String::from_utf8(output.stdout)?;
if system_arch.contains("arm") { if system_arch.contains("arm") {
return Ok(lp_select!("armeabi-v7a", "arm64-v8a")) return Ok(lp_select!("armeabi-v7a", "arm64-v8a"));
} }
if system_arch.contains("x86") { if system_arch.contains("x86") {
return Ok(lp_select!("x86", "x86_64")) return Ok(lp_select!("x86", "x86_64"));
} }
bail!("Unsupported system architecture: {}", system_arch); bail!("Unsupported system architecture: {}", system_arch);
} }
@@ -93,7 +93,7 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
continue; continue;
} }
log::info!(" Loading module `{name}`..."); log::info!(" Loading module `{name}`...");
let memfd = match create_memfd(&name, &so_path) { let memfd = match create_memfd(&so_path) {
Ok(memfd) => memfd, Ok(memfd) => memfd,
Err(e) => { Err(e) => {
log::warn!(" Failed to create memfd for `{name}`: {e}"); log::warn!(" Failed to create memfd for `{name}`: {e}");
@@ -116,10 +116,9 @@ fn load_modules(arch: &str) -> Result<Vec<Module>> {
Ok(modules) Ok(modules)
} }
fn create_memfd(name: &str, so_path: &PathBuf) -> Result<Memfd> { fn create_memfd(so_path: &PathBuf) -> Result<Memfd> {
let opts = memfd::MemfdOptions::default().allow_sealing(true); let opts = memfd::MemfdOptions::default().allow_sealing(true);
let memfd = opts.create(name)?; let memfd = opts.create("jit-cache")?;
let file = fs::File::open(so_path)?; let file = fs::File::open(so_path)?;
let mut reader = std::io::BufReader::new(file); let mut reader = std::io::BufReader::new(file);
let mut writer = memfd.as_file(); let mut writer = memfd.as_file();
@@ -193,6 +192,18 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
DaemonSocketAction::ReadNativeBridge => { DaemonSocketAction::ReadNativeBridge => {
stream.write_string(&context.native_bridge)?; stream.write_string(&context.native_bridge)?;
} }
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;
}
if root_impl::uid_on_denylist(uid) {
flags |= constants::PROCESS_ON_DENYLIST;
}
// TODO: PROCESS_IS_SYSUI?
stream.write_u32(flags)?;
}
DaemonSocketAction::ReadModules => { DaemonSocketAction::ReadModules => {
stream.write_usize(context.modules.len())?; stream.write_usize(context.modules.len())?;
for module in context.modules.iter() { for module in context.modules.iter() {