Compare commits

...

5 Commits

Author SHA1 Message Date
Nullptr
47e515e2fc Update CI 2023-10-24 17:22:34 +08:00
Nullptr
44918d332e Rename project to Zygisk Next 2023-10-24 17:18:59 +08:00
Nullptr
cbf5920d02 Bump to 0.8.1 2023-10-24 16:07:21 +08:00
5ec1cff
a2af28dc6f add 32 bit support back & fix x86-64 support & fix jni hook restore (#59)
* Add back 32 bit support & some fix

* fix system server crash on android 10

* Refine code

---------

Co-authored-by: Nullptr <noreply@nullptr.icu>
2023-10-24 15:27:21 +08:00
Nullptr
a956dba77d Show crash info correctly 2023-10-23 22:23:25 +08:00
26 changed files with 479 additions and 337 deletions

View File

@@ -6,11 +6,11 @@ body:
- type: markdown
attributes:
value: |
Thanks for reporting issues of Zygisk on KernelSU!
Thanks for reporting issues for Zygisk Next!
To make it easier for us to help you please enter detailed information below.
Note: We will **NEVER** handle any issue related to root detection or writable system.
感谢给 Zygisk on KernelSU 汇报问题!
感谢给 Zygisk Next 汇报问题!
为了使我们更好地帮助你,请提供以下信息。
为了防止重复汇报,标题请务必使用英文。
请注意:我们**不会**处理任何有关 root 检测和 system 分区可写相关的问题。
@@ -43,7 +43,7 @@ body:
required: true
- type: input
attributes:
label: Zygisk on KernelSU version/Zygisk on KernelSU 版本
label: Zygisk Next version/Zygisk Next 版本
description: Don't use 'latest'. Specify actual version, otherwise your issue will be closed./不要填用“最新版”。给出具体版本号,不然 issue 会被关闭。
validations:
required: true
@@ -62,7 +62,7 @@ body:
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 且启用详细日志
- label: I am using latest debug CI version of Zygisk Next and enable verbose log/我正在使用最新 CI 调试版本 Zygisk Next 且启用详细日志
required: true
- type: textarea
attributes:

View File

@@ -46,6 +46,8 @@ jobs:
rustup override set nightly
rustup target add aarch64-linux-android
rustup target add x86_64-linux-android
rustup target add i686-linux-android
rustup target add armv7-linux-androideabi
- name: Set up ccache
uses: hendrikmuhs/ccache-action@v1.2
@@ -69,10 +71,10 @@ jobs:
if: success()
id: prepareArtifact
run: |
releaseName=`ls module/build/outputs/release/Zygisk-on-KernelSU-v*-release.zip | awk -F '(/|.zip)' '{print $5}'` && echo "releaseName=$releaseName" >> $GITHUB_OUTPUT
debugName=`ls module/build/outputs/release/Zygisk-on-KernelSU-v*-debug.zip | awk -F '(/|.zip)' '{print $5}'` && echo "debugName=$debugName" >> $GITHUB_OUTPUT
unzip module/build/outputs/release/Zygisk-on-KernelSU-v*-release.zip -d zksu-release
unzip module/build/outputs/release/Zygisk-on-KernelSU-v*-debug.zip -d zksu-debug
releaseName=`ls module/build/outputs/release/Zygisk-Next-v*-release.zip | awk -F '(/|.zip)' '{print $5}'` && echo "releaseName=$releaseName" >> $GITHUB_OUTPUT
debugName=`ls module/build/outputs/release/Zygisk-Next-v*-debug.zip | awk -F '(/|.zip)' '{print $5}'` && echo "debugName=$debugName" >> $GITHUB_OUTPUT
unzip module/build/outputs/release/Zygisk-Next-v*-release.zip -d zksu-release
unzip module/build/outputs/release/Zygisk-Next-v*-debug.zip -d zksu-debug
- name: Upload release
uses: actions/upload-artifact@v3

View File

@@ -1,15 +1,13 @@
# Zygisk on KernelSU
# Zygisk Next
Zygisk loader for KernelSU, allowing Zygisk modules to run without Magisk environment.
Also works as standalone loader for Magisk.
Standalone implementation of Zygisk, providing Zygisk API support for KernelSU and a replacement of Magisk's built-in Zygisk.
## Requirements
### General
+ No multiple root implementation installed
+ SELinux enforcing: We now rely on SELinux to prevent `vold` from aborting our fuse connection
+ SELinux enforcing: Zygisk Next rely on SELinux to prevent `vold` from aborting our fuse connection
### KernelSU
@@ -20,8 +18,10 @@ Also works as standalone loader for Magisk.
### Magisk
+ Minimal version: 26300
+ Original Zygisk turned off
+ Built-in Zygisk turned off
## Compatibility
Should work with everything except those rely on Magisk internal behaviors.
`PROCESS_ON_DENYLIST` cannot be flagged correctly for isolated processes on Magisk DenyList currently.
Zygisk Next only guarantees the same behavior of Zygisk API, but will NOT ensure Magisk's internal features.

View File

@@ -19,8 +19,8 @@ val gitCommitCount = "git rev-list HEAD --count".execute().toInt()
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.8.0")
val moduleName by extra("Zygisk Next")
val verName by extra("v4-0.8.1")
val verCode by extra(gitCommitCount)
val commitHash by extra(gitCommitHash)
val minKsuVersion by extra(10940)
@@ -42,7 +42,7 @@ tasks.register("Delete", Delete::class) {
fun Project.configureBaseExtension() {
extensions.findByType(LibraryExtension::class)?.run {
namespace = "icu.nullptr.zygisksu"
namespace = "icu.nullptr.zygisk.next"
compileSdk = androidCompileSdkVersion
ndkVersion = androidCompileNdkVersion
buildToolsVersion = androidBuildToolsVersion

View File

@@ -35,7 +35,6 @@ android {
defaultConfig {
externalNativeBuild {
ndkBuild {
abiFilters("arm64-v8a", "x86_64")
ccachePatch?.let {
arguments += "NDK_CCACHE=$it"
}

View File

@@ -5,7 +5,13 @@
#include <unistd.h>
#include <vector>
constexpr auto kCPSocketPath = "/dev/zygisk/cp.sock";
#if defined(__LP64__)
# define LP_SELECT(lp32, lp64) lp64
#else
# define LP_SELECT(lp32, lp64) lp32
#endif
constexpr auto kCPSocketPath = "/dev/zygisk/" LP_SELECT("cp32", "cp64") ".sock";
class UniqueFd {
using Fd = int;

View File

@@ -5,16 +5,13 @@
#include <string.h>
#ifndef LOG_TAG
#define LOG_TAG "zygisk-core"
#if defined(__LP64__)
# define LOG_TAG "zygisk-core64"
#else
# define LOG_TAG "zygisk-core32"
#endif
#endif
#ifdef LOG_DISABLED
#define LOGD(...)
#define LOGV(...)
#define LOGI(...)
#define LOGW(...)
#define LOGE(...)
#else
#ifndef NDEBUG
#define LOGD(...) logging::log(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGV(...) logging::log(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
@@ -27,7 +24,6 @@
#define LOGE(...) logging::log(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) logging::log(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#endif
namespace logging {
void setfd(int fd);

View File

@@ -26,6 +26,7 @@ using xstring = jni_hook::string;
static void hook_unloader();
static void unhook_functions();
static void restore_jni_env(JNIEnv *env);
namespace {
@@ -84,8 +85,16 @@ struct HookContext {
vector<RegisterInfo> register_info;
vector<IgnoreInfo> ignore_info;
HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0),
hook_info_lock(PTHREAD_MUTEX_INITIALIZER) { g_ctx = this; }
HookContext(JNIEnv *env, void *args) :
env(env), args{args}, process(nullptr), pid(-1), info_flags(0),
hook_info_lock(PTHREAD_MUTEX_INITIALIZER) {
static bool restored_env = false;
if (!restored_env) {
restore_jni_env(env);
restored_env = true;
}
g_ctx = this;
}
~HookContext();
/* Zygisksu changed: Load module fds */
@@ -685,12 +694,6 @@ HookContext::~HookContext() {
should_unmap_zygisk = true;
// Restore JNIEnv
if (env->functions == new_functions) {
env->functions = old_functions;
delete new_functions;
}
// Unhook JNI methods
for (const auto &[clz, methods] : *jni_hook_list) {
if (!methods.empty() && env->RegisterNatives(
@@ -713,6 +716,12 @@ HookContext::~HookContext() {
} // namespace
static void restore_jni_env(JNIEnv *env) {
env->functions = old_functions;
delete new_functions;
new_functions = nullptr;
}
static bool hook_commit() {
if (lsplt::CommitHook()) {
return true;

View File

@@ -5,9 +5,7 @@ namespace {
void *nativeForkAndSpecialize_orig = nullptr;
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_l)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, instruction_set, app_data_dir
@@ -18,9 +16,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_o)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
@@ -32,9 +28,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_p)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
@@ -47,9 +41,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
args.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_q_alt)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app
@@ -66,9 +58,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
args.whitelisted_data_info_list = &whitelisted_data_info_list;
args.mount_data_dirs = &mount_data_dirs;
args.mount_storage_dirs = &mount_storage_dirs;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_r)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
@@ -78,9 +68,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
}
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_m(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_m)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _0, _1, nice_name, fds_to_close, instruction_set, app_data_dir
@@ -90,9 +78,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
}
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_n(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_n)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _2, _3, nice_name, fds_to_close, instruction_set, app_data_dir, _4
@@ -103,9 +89,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
[[clang::no_stack_protector]] jint nativeForkAndSpecialize_samsung_o(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_o)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _5, _6, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
@@ -117,9 +101,7 @@ void *nativeForkAndSpecialize_orig = nullptr;
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<decltype(&nativeForkAndSpecialize_samsung_p)>(nativeForkAndSpecialize_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _7, _8, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
@@ -180,9 +162,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.is_child_zygote = &is_child_zygote;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<decltype(&nativeSpecializeAppProcess_q)>(nativeSpecializeAppProcess_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir
@@ -193,9 +173,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<decltype(&nativeSpecializeAppProcess_q_alt)>(nativeSpecializeAppProcess_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app
@@ -210,9 +188,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
args.whitelisted_data_info_list = &whitelisted_data_info_list;
args.mount_data_dirs = &mount_data_dirs;
args.mount_storage_dirs = &mount_storage_dirs;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<decltype(&nativeSpecializeAppProcess_r)>(nativeSpecializeAppProcess_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
@@ -222,9 +198,7 @@ void *nativeSpecializeAppProcess_orig = nullptr;
[[clang::no_stack_protector]] void nativeSpecializeAppProcess_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
AppSpecializeArgs_v3 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.is_child_zygote = &is_child_zygote;
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<decltype(&nativeSpecializeAppProcess_samsung_q)>(nativeSpecializeAppProcess_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _9, _10, nice_name, is_child_zygote, instruction_set, app_data_dir
@@ -258,9 +232,7 @@ constexpr int nativeSpecializeAppProcess_methods_num = std::size(nativeSpecializ
void *nativeForkSystemServer_orig = nullptr;
[[clang::no_stack_protector]] jint nativeForkSystemServer_l(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkSystemServer_pre();
reinterpret_cast<decltype(&nativeForkSystemServer_l)>(nativeForkSystemServer_orig)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities
@@ -270,9 +242,7 @@ void *nativeForkSystemServer_orig = nullptr;
}
[[clang::no_stack_protector]] jint nativeForkSystemServer_samsung_q(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities) {
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
HookContext ctx;
ctx.env = env;
ctx.args = { &args };
HookContext ctx(env, &args);
ctx.nativeForkSystemServer_pre();
reinterpret_cast<decltype(&nativeForkSystemServer_samsung_q)>(nativeForkSystemServer_orig)(
env, clazz, uid, gid, gids, runtime_flags, _11, _12, rlimits, permitted_capabilities, effective_capabilities

View File

@@ -39,7 +39,7 @@ androidComponents.onVariants { variant ->
into(moduleDir)
from("${rootProject.projectDir}/README.md")
from("$projectDir/src") {
exclude("module.prop", "customize.sh", "service.sh")
exclude("module.prop", "customize.sh", "post-fs-data.sh", "service.sh")
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
from("$projectDir/src") {
@@ -52,7 +52,7 @@ androidComponents.onVariants { variant ->
)
}
from("$projectDir/src") {
include("customize.sh", "service.sh")
include("customize.sh", "post-fs-data.sh", "service.sh")
val tokens = mapOf(
"DEBUG" to if (buildTypeLowered == "debug") "true" else "false",
"MIN_KSU_VERSION" to "$minKsuVersion",

View File

@@ -31,7 +31,7 @@ if [ "$BOOTMODE" ] && [ "$KSU" ]; then
if [ "$(which magisk)" ]; then
ui_print "*********************************************************"
ui_print "! Multiple root implementation is NOT supported!"
ui_print "! Please uninstall Magisk before installing Zygisksu"
ui_print "! Please uninstall Magisk before installing Zygisk Next"
abort "*********************************************************"
fi
elif [ "$BOOTMODE" ] && [ "$MAGISK_VER_CODE" ]; then
@@ -50,7 +50,7 @@ else
fi
VERSION=$(grep_prop version "${TMPDIR}/module.prop")
ui_print "- Installing Zygisksu $VERSION"
ui_print "- Installing Zygisk Next $VERSION"
# check android
if [ "$API" -lt 29 ]; then
@@ -61,7 +61,7 @@ else
fi
# check architecture
if [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x64" ]; then
if [ "$ARCH" != "arm" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x86" ] && [ "$ARCH" != "x64" ]; then
abort "! Unsupported platform: $ARCH"
else
ui_print "- Device platform: $ARCH"
@@ -101,28 +101,54 @@ extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH"
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
mkdir "$MODPATH/bin"
mkdir "$MODPATH/system"
mkdir "$MODPATH/system/lib64"
[ "$HAS32BIT" = true ] && mkdir "$MODPATH/system/lib"
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
if [ "$HAS32BIT" = true ]; then
ui_print "- Extracting x86 libraries"
extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
extract "$ZIPFILE" 'lib/x86/libzygisk.so' "$MODPATH/system/lib" true
ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32"
ln -sf "zygiskd32" "$MODPATH/bin/zygisk-ptrace32"
fi
if [ "$ARCH" = "x64" ]; then
ui_print "- Extracting x64 libraries"
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
extract "$ZIPFILE" 'lib/x86_64/libzygisk.so' "$MODPATH/system/lib64" true
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-fuse"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-ptrace64"
else
if [ "$HAS32BIT" = true ]; then
ui_print "- Extracting arm libraries"
extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk.so' "$MODPATH/system/lib" true
ln -sf "zygiskd32" "$MODPATH/bin/zygisk-cp32"
ln -sf "zygiskd32" "$MODPATH/bin/zygisk-ptrace32"
fi
ui_print "- Extracting arm64 libraries"
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk.so' "$MODPATH/system/lib64" true
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-wd"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-fuse"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-cp64"
ln -sf "zygiskd64" "$MODPATH/bin/zygisk-ptrace64"
fi
ui_print "- Setting permissions"
set_perm_recursive "$MODPATH/bin" 0 0 0755 0755
set_perm_recursive "$MODPATH/system/lib" 0 0 0755 0644 u:object_r:system_lib_file:s0
set_perm_recursive "$MODPATH/system/lib64" 0 0 0755 0644 u:object_r:system_lib_file:s0
# If Huawei's Maple is enabled, system_server is created with a special way which is out of Zygisk's control

View File

@@ -3,5 +3,5 @@ name=${moduleName}
version=${versionName}
versionCode=${versionCode}
author=Nullptr, 5ec1cff
description=Run Zygisk on KernelSU.
updateJson=https://api.nullptr.icu/android/zygisk-on-kernelsu/static/update.json
description=Standalone implementation of Zygisk.
updateJson=https://api.nullptr.icu/android/zygisk-next/static/update.json

View File

@@ -12,7 +12,7 @@ if [ "$(which magisk)" ]; then
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/post-fs-data.sh" ]; then
cd "$file"
log -p i -t "zygisksu" "Manually trigger post-fs-data.sh for $file"
log -p i -t "zygisk-sh" "Manually trigger post-fs-data.sh for $file"
sh "$(realpath ./post-fs-data.sh)"
cd "$MODDIR"
fi

View File

@@ -14,7 +14,7 @@ if [ "$(which magisk)" ]; then
if [ -d "$file" ] && [ -d "$file/zygisk" ] && ! [ -f "$file/disable" ]; then
if [ -f "$file/service.sh" ]; then
cd "$file"
log -p i -t "zygisksu" "Manually trigger service.sh for $file"
log -p i -t "zygisk-sh" "Manually trigger service.sh for $file"
sh "$(realpath ./service.sh)"
cd "$MODDIR"
fi

View File

@@ -16,7 +16,7 @@ dependencyResolutionManagement {
}
}
rootProject.name = "ZygiskOnKernelSU"
rootProject.name = "ZygiskNext"
include(
":loader",
":module",

View File

@@ -19,12 +19,13 @@ memfd = "0.6"
num_enum = "0.5"
passfd = "0.1"
proc-maps = "0.3"
ptrace-do = "0.1"
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
tokio = { version = "1.28", features = ["full"] }
binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" }
fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false }
ptrace-do = { git = "https://github.com/5ec1cff/ptrace-do" }
[profile.release]
strip = true

View File

@@ -3,8 +3,6 @@ plugins {
alias(libs.plugins.rust.android)
}
val verName: String 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
@@ -18,14 +16,12 @@ cargo {
module = "."
libname = "zygiskd"
targetIncludes = arrayOf("zygiskd")
targets = listOf("arm64", "x86_64")
targets = listOf("arm64", "arm", "x86", "x86_64")
targetDirectory = "build/intermediates/rust"
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)
spec.environment("MIN_KSU_VERSION", minKsuVersion)
spec.environment("MAX_KSU_VERSION", maxKsuVersion)
spec.environment("MIN_MAGISK_VERSION", minMagiskVersion)

View File

@@ -4,10 +4,8 @@ use konst::primitive::parse_i32;
use konst::unwrap_ctx;
use log::LevelFilter;
use num_enum::TryFromPrimitive;
use crate::lp_select;
pub const VERSION_NAME: &str = env!("VERSION_NAME");
pub const VERSION_CODE: &str = env!("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")));
@@ -18,22 +16,25 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
pub const PROP_CTL_RESTART: &str = "ctl.restart";
pub const ZYGISK_LIBRARY: &str = "libzygisk.so";
pub const PATH_PCL: &str = "/system/etc/preloaded-classes";
pub const PATH_SYSTEM_LIB: &str = "/system/lib64";
pub const PATH_ZYGISK_LIB: &str = concatcp!(lp_select!("/system/lib", "/system/lib64"), "/libzygisk.so");
pub const PATH_WORK_DIR: &str = "/dev/zygisk"; // TODO: Replace with /debug_ramdisk/zygisk
pub const PATH_PROP_OVERLAY: &str = concatcp!(PATH_WORK_DIR, "/module.prop");
pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, "/cp.sock");
pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, lp_select!("/cp32.sock", "/cp64.sock"));
pub const PATH_FUSE_DIR: &str = concatcp!(PATH_WORK_DIR, "/fuse");
pub const PATH_FUSE_PCL: &str = concatcp!(PATH_FUSE_DIR, "/preloaded-classes");
pub const PATH_MODULES_DIR: &str = "..";
pub const PATH_MODULE_PROP: &str = "module.prop";
pub const PATH_CP_BIN: &str = "bin/zygisk-cp";
pub const PATH_CP_BIN32: &str = "bin/zygisk-cp32";
pub const PATH_CP_BIN64: &str = "bin/zygisk-cp64";
pub const PATH_PTRACE_BIN32: &str = "bin/zygisk-ptrace32";
pub const PATH_PTRACE_BIN64: &str = "bin/zygisk-ptrace64";
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
pub const STATUS_CRASHED: &str = " Zygiskd has crashed";
pub const STATUS_LOADED: &str = "😋 Zygisk Next is loaded";
pub const STATUS_CRASHED: &str = "❌ Zygisk Next has crashed";
pub const STATUS_ROOT_IMPL_NONE: &str = "❌ Unknown root implementation";
pub const STATUS_ROOT_IMPL_TOO_OLD: &str = "❌ Root implementation version too old";
pub const STATUS_ROOT_IMPL_ABNORMAL: &str = "❌ Abnormal root implementation version";

View File

@@ -1,18 +1,18 @@
use std::cmp::min;
use anyhow::{bail, Result};
use std::ffi::{CString, OsStr};
use std::ffi::OsStr;
use std::{fs, thread};
use std::io::Read;
use std::process::{Command, Stdio};
use std::sync::{mpsc, Mutex};
use std::time::{Duration, SystemTime};
use fuser::{FileAttr, Filesystem, FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, ReplyOpen, Request};
use libc::ENOENT;
use log::{debug, error, info};
use proc_maps::{get_process_maps, MapRange, Pid};
use ptrace_do::{RawProcess, TracedProcess};
use rustix::mount::mount_bind;
use log::{error, info};
use rustix::fs::UnmountFlags;
use rustix::mount::{mount_bind, unmount};
use rustix::path::Arg;
use rustix::process::getpid;
use crate::{constants, dl};
use crate::constants;
use crate::utils::LateInit;
pub struct DelegateFilesystem;
@@ -37,9 +37,6 @@ const fn attr(inode: u64, size: u64, kind: FileType) -> FileAttr {
}
}
const ANDROID_LIBC: &str = "bionic/libc.so";
const ANDROID_LIBDL: &str = "bionic/libdl.so";
const INO_DIR: u64 = 1;
const INO_PCL: u64 = 2;
@@ -56,6 +53,66 @@ const ENTRIES: &[(u64, FileType, &str)] = &[
const TTL: Duration = Duration::from_secs(1);
fn ptrace_zygote64(pid: u32) -> Result<()> {
static LAST: Mutex<u32> = Mutex::new(0);
let mut last = LAST.lock().unwrap();
if *last == pid {
return Ok(());
}
*last = pid;
let (sender, receiver) = mpsc::channel::<()>();
let worker = move || -> Result<()> {
let mut child = Command::new(constants::PATH_PTRACE_BIN64).stdout(Stdio::piped()).arg(format!("{}", pid)).spawn()?;
child.stdout.as_mut().unwrap().read_exact(&mut [0u8; 1])?;
info!("child attached");
sender.send(())?;
let result = child.wait()?;
info!("ptrace64 process status {}", result);
Ok(())
};
thread::spawn(move || {
if let Err(e) = worker() {
error!("Crashed: {:?}", e);
}
});
receiver.recv()?;
Ok(())
}
fn ptrace_zygote32(pid: u32) -> Result<()> {
static LAST: Mutex<u32> = Mutex::new(0);
let mut last = LAST.lock().unwrap();
if *last == pid {
return Ok(());
}
*last = pid;
let (sender, receiver) = mpsc::channel::<()>();
let worker = move || -> Result<()> {
let mut child = Command::new(constants::PATH_PTRACE_BIN32).stdout(Stdio::piped()).arg(format!("{}", pid)).spawn()?;
child.stdout.as_mut().unwrap().read_exact(&mut [0u8; 1])?;
info!("child attached");
sender.send(())?;
let result = child.wait()?;
info!("ptrace32 process status {}", result);
Ok(())
};
thread::spawn(move || {
if let Err(e) = worker() {
error!("Crashed: {:?}", e);
}
});
receiver.recv()?;
Ok(())
}
impl Filesystem for DelegateFilesystem {
fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
if parent != INO_DIR {
@@ -83,8 +140,10 @@ impl Filesystem for DelegateFilesystem {
let process = fs::read_to_string(process).unwrap();
let process = &process[..process.find('\0').unwrap()];
info!("Process {} is reading preloaded-classes", process);
if process == "zygote64" {
ptrace_zygote(pid).unwrap();
match process {
"zygote64" => ptrace_zygote64(pid).unwrap(),
"zygote" => ptrace_zygote32(pid).unwrap(),
_ => (),
}
}
reply.opened(0, 0);
@@ -120,190 +179,6 @@ impl Filesystem for DelegateFilesystem {
}
}
fn find_module_for_pid(pid: Pid, library: &str) -> Result<MapRange> {
let maps = get_process_maps(pid)?;
for map in maps.into_iter() {
if let Some(p) = map.filename() {
if p.as_str()?.contains(library) {
return Ok(map);
}
}
}
bail!("Cannot find module {library} for pid {pid}");
}
fn find_remote_procedure(
pid: Pid,
library: &str,
local_addr: usize,
) -> Result<usize> {
let local_module = find_module_for_pid(getpid().as_raw_nonzero().get(), library)?;
debug!(
"Identifed local range {library} ({:?}) at {:x}",
local_module.filename(),
local_module.start()
);
let remote_module = find_module_for_pid(pid, library)?;
debug!(
"Identifed remote range {library} ({:?}) at {:x}",
remote_module.filename(),
remote_module.start()
);
Ok(local_addr - local_module.start() + remote_module.start())
}
fn ptrace_zygote(pid: u32) -> Result<()> {
static LAST: Mutex<u32> = Mutex::new(0);
let mut last = LAST.lock().unwrap();
if *last == pid {
return Ok(());
}
*last = pid;
let (sender, receiver) = mpsc::channel::<()>();
let worker = move || -> Result<()> {
info!("Injecting into pid {}", pid);
let zygisk_lib = format!("{}/{}", constants::PATH_SYSTEM_LIB, constants::ZYGISK_LIBRARY);
let lib_dir = CString::new(constants::PATH_SYSTEM_LIB)?;
let zygisk_lib = CString::new(zygisk_lib)?;
let libc_base = find_module_for_pid(pid as i32, ANDROID_LIBC)?.start();
let mmap_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::mmap as usize,
)?;
let munmap_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::munmap as usize,
)?;
let dlopen_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBDL,
dl::android_dlopen_ext as usize,
)?;
let dlsym_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBDL,
libc::dlsym as usize,
)?;
let tracer = TracedProcess::attach(RawProcess::new(pid as i32))?;
sender.send(())?;
let frame = tracer.next_frame()?;
debug!("Waited for a frame");
// Map a buffer in the remote process
let mmap_params: [usize; 6] = [
0,
0x1000,
(libc::PROT_READ | libc::PROT_WRITE) as usize,
(libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as usize,
0,
0,
];
let (regs, mut frame) = frame.invoke_remote(
mmap_remote,
libc_base,
&mmap_params,
)?;
let buf_addr = regs.return_value();
debug!("Buffer addr: {:x}", buf_addr);
// Find the address of __loader_android_create_namespace
let sym = CString::new("__loader_android_create_namespace")?;
frame.write_memory(buf_addr, sym.as_bytes_with_nul())?;
let (regs, mut frame) = frame.invoke_remote(
dlsym_remote,
libc_base,
&[libc::RTLD_DEFAULT as usize, buf_addr],
)?;
let android_create_namespace_remote = regs.return_value();
debug!("__loader_android_create_namespace addr: {:x}", android_create_namespace_remote);
// Create a linker namespace for remote process
frame.write_memory(buf_addr, zygisk_lib.as_bytes_with_nul())?;
frame.write_memory(buf_addr + 0x100, lib_dir.as_bytes_with_nul())?;
let ns_params: [usize; 7] = [
buf_addr, // name
buf_addr + 0x100, // ld_library_path
0, // default_library_path
dl::ANDROID_NAMESPACE_TYPE_SHARED as usize, // type
0, // permitted_when_isolated_path
0, // parent
dlopen_remote, // caller_addr
];
let (regs, mut frame) = frame.invoke_remote(
android_create_namespace_remote,
libc_base,
&ns_params,
)?;
let ns_addr = regs.return_value();
debug!("Linker namespace addr: {:x}", ns_addr);
// Load zygisk into remote process
let info = dl::AndroidDlextinfo {
flags: dl::ANDROID_DLEXT_USE_NAMESPACE,
reserved_addr: std::ptr::null_mut(),
reserved_size: 0,
relro_fd: 0,
library_fd: 0,
library_fd_offset: 0,
library_namespace: ns_addr as *mut _,
};
let info = unsafe {
std::slice::from_raw_parts(
&info as *const _ as *const u8,
std::mem::size_of::<dl::AndroidDlextinfo>(),
)
};
frame.write_memory(buf_addr + 0x200, info)?;
let (regs, mut frame) = frame.invoke_remote(
dlopen_remote,
libc_base,
&[buf_addr, libc::RTLD_NOW as usize, buf_addr + 0x200],
)?;
let handle = regs.return_value();
debug!("Load zygisk into remote process: {:x}", handle);
let entry = CString::new("entry")?;
frame.write_memory(buf_addr, entry.as_bytes_with_nul())?;
let (regs, frame) = frame.invoke_remote(
dlsym_remote,
libc_base,
&[handle, buf_addr],
)?;
let entry = regs.return_value();
debug!("Call zygisk entry: {:x}", entry);
let (_, frame) = frame.invoke_remote(
entry,
libc_base,
&[handle],
)?;
// Cleanup
let _ = frame.invoke_remote(
munmap_remote,
libc_base,
&[buf_addr],
)?;
debug!("Cleaned up");
Ok(())
};
thread::spawn(move || {
if let Err(e) = worker() {
error!("Crashed: {:?}", e);
}
});
receiver.recv()?;
Ok(())
}
pub fn main() -> Result<()> {
info!("Start zygisk fuse");
fs::create_dir(constants::PATH_WORK_DIR)?;
@@ -321,7 +196,9 @@ pub fn main() -> Result<()> {
&options,
)?;
mount_bind(constants::PATH_FUSE_PCL, constants::PATH_PCL)?;
match session.guard.join() {
let crash = session.guard.join();
unmount(constants::PATH_PCL, UnmountFlags::DETACH)?;
match crash {
Err(e) => bail!("Fuse mount crashed: {:?}", e),
_ => bail!("Fuse mount exited unexpectedly"),
}

View File

@@ -1,9 +1,7 @@
#![feature(exclusive_range_pattern)]
#![allow(dead_code)]
mod constants;
mod dl;
mod fuse;
mod ptrace;
mod root_impl;
mod utils;
mod watchdog;
@@ -12,7 +10,6 @@ mod zygiskd;
use std::future::Future;
use anyhow::Result;
fn init_android_logger(tag: &str) {
android_logger::init_once(
android_logger::Config::default()
@@ -32,8 +29,9 @@ fn start(name: &str) -> Result<()> {
match name.trim_start_matches("zygisk-") {
"wd" => async_start(watchdog::main())?,
"fuse" => fuse::main()?,
"cp" => zygiskd::main()?,
_ => println!("Available commands: wd, fuse, cp"),
lp_select!("cp32", "cp64") => zygiskd::main()?,
lp_select!("ptrace32", "ptrace64") => ptrace::main()?,
_ => println!("Available commands: wd, fuse, cp, ptrace"),
}
Ok(())
}

233
zygiskd/src/ptrace.rs Normal file
View File

@@ -0,0 +1,233 @@
use log::{debug, info};
use std::ffi::CString;
use std::env;
use std::io::Write;
use rustix::path::Arg;
use proc_maps::{get_process_maps, MapRange, Pid};
use ptrace_do::{RawProcess, TracedProcess};
use rustix::process::getpid;
use crate::{constants, lp_select};
use anyhow::{bail, Result};
const ANDROID_LIBC: &str = "bionic/libc.so";
const ANDROID_LIBDL: &str = "bionic/libdl.so";
fn find_module_for_pid(pid: Pid, library: &str) -> Result<MapRange> {
let maps = get_process_maps(pid)?;
for map in maps.into_iter() {
if let Some(p) = map.filename() {
if p.as_str()?.contains(library) {
return Ok(map);
}
}
}
bail!("Cannot find module {library} for pid {pid}");
}
fn find_remote_procedure(
pid: Pid,
library: &str,
local_addr: usize,
) -> Result<usize> {
let local_module = find_module_for_pid(getpid().as_raw_nonzero().get(), library)?;
debug!(
"Identifed local range {library} ({:?}) at {:x}",
local_module.filename(),
local_module.start()
);
let remote_module = find_module_for_pid(pid, library)?;
debug!(
"Identifed remote range {library} ({:?}) at {:x}",
remote_module.filename(),
remote_module.start()
);
Ok(local_addr - local_module.start() + remote_module.start())
}
fn ptrace_zygote(pid: u32) -> Result<()> {
info!("Injecting into pid {}", pid);
let zygisk_lib = CString::new(constants::PATH_ZYGISK_LIB)?;
let libc_base = find_module_for_pid(pid as i32, ANDROID_LIBC)?.start();
let mmap_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::mmap as usize,
)?;
let munmap_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::munmap as usize,
)?;
let dlopen_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBDL,
libc::dlopen as usize,
)?;
let dlsym_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBDL,
libc::dlsym as usize,
)?;
let errno_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::__errno as usize,
)?;
let dlerror_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBDL,
libc::dlerror as usize,
)?;
let strlen_remote = find_remote_procedure(
pid as i32,
ANDROID_LIBC,
libc::strlen as usize,
)?;
let tracer = TracedProcess::attach(RawProcess::new(pid as i32))?;
std::io::stdout().write(b"1")?;
info!("attached process {}", pid);
std::io::stdout().flush()?;
let frame = tracer.next_frame()?;
debug!("Waited for a frame");
// Map a buffer in the remote process
debug!("remote mmap addr {:x}", mmap_remote);
let mmap_params: [usize; 6] = [
0,
0x1000,
(libc::PROT_READ | libc::PROT_WRITE) as usize,
(libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as usize,
0,
0,
];
let mut arr: Vec<u8> = Vec::new();
for p in mmap_params {
arr.extend_from_slice(&p.to_le_bytes());
}
arr.as_slice();
let (regs, mut frame) = frame.invoke_remote(
mmap_remote,
libc_base,
&mmap_params,
)?;
let buf_addr = regs.return_value();
debug!("remote stopped at addr {:x}", regs.program_counter());
if regs.program_counter() != libc_base {
let data = std::mem::MaybeUninit::<libc::siginfo_t>::uninit();
let siginfo = unsafe {
libc::ptrace(libc::PTRACE_GETSIGINFO, pid, 0, &data);
data.assume_init()
};
bail!(
"stopped at unexpected addr {:x} signo {} si_code {} si_addr {:?}",
regs.program_counter(),
siginfo.si_signo,
siginfo.si_code,
unsafe { siginfo.si_addr() },
);
}
if buf_addr == usize::MAX {
debug!("errno remote {:x}", errno_remote);
let (regs, frame) = frame.invoke_remote(
errno_remote,
libc_base,
&[],
)?;
debug!("errno called");
if regs.program_counter() != libc_base {
bail!("stopped at unexpected addr {:x} when getting errno", regs.program_counter());
}
let err_addr = regs.return_value();
let mut buf = [0u8; 4];
frame.read_memory_mut(err_addr, &mut buf)?;
let err = i32::from_le_bytes(buf);
bail!("remote failed with {}", err);
}
debug!("Buffer addr: {:x}", buf_addr);
// Load zygisk into remote process
frame.write_memory(buf_addr, zygisk_lib.as_bytes_with_nul())?;
let (regs, mut frame) = frame.invoke_remote(
dlopen_remote,
libc_base,
&[buf_addr, libc::RTLD_NOW as usize],
)?;
let handle = regs.return_value();
debug!("Load zygisk into remote process: {:x}", handle);
if regs.program_counter() != libc_base {
let data = std::mem::MaybeUninit::<libc::siginfo_t>::uninit();
let siginfo = unsafe {
libc::ptrace(libc::PTRACE_GETSIGINFO, pid, 0, &data);
data.assume_init()
};
bail!(
"stopped at unexpected addr {:x} signo {} si_code {} si_addr {:?}",
regs.program_counter(),
siginfo.si_signo,
siginfo.si_code,
unsafe { siginfo.si_addr() },
);
}
if handle == 0 {
debug!("got handle 0");
let (regs, frame) = frame.invoke_remote(
dlerror_remote,
libc_base,
&[],
)?;
let err_addr = regs.return_value();
if err_addr == 0 {
bail!("dlerror err addr 0");
}
debug!("err addr {:x}", err_addr);
let (regs, frame) = frame.invoke_remote(
strlen_remote,
libc_base,
&[err_addr],
)?;
let len = regs.return_value();
if len == 0 {
bail!("dlerror len 0");
}
debug!("err len {}", len);
let mut buf = vec![0u8; len];
frame.read_memory_mut(err_addr, buf.as_mut_slice())?;
bail!("err {:?}", buf);
}
let entry = CString::new("entry")?;
frame.write_memory(buf_addr, entry.as_bytes_with_nul())?;
let (regs, frame) = frame.invoke_remote(
dlsym_remote,
libc_base,
&[handle, buf_addr],
)?;
let entry = regs.return_value();
debug!("Call zygisk entry: {:x}", entry);
let (_, frame) = frame.invoke_remote(
entry,
libc_base,
&[handle],
)?;
// Cleanup
let _ = frame.invoke_remote(
munmap_remote,
libc_base,
&[buf_addr],
)?;
debug!("Cleaned up");
Ok(())
}
pub fn main() -> Result<()> {
info!("Start zygisk ptrace");
let args: Vec<String> = env::args().collect();
let pid = args[1].parse::<u32>().unwrap();
info!("ptracing {} pid {}", lp_select!("zygote32", "zygote64"), pid);
ptrace_zygote(pid)?;
Ok(())
}

View File

@@ -26,7 +26,7 @@ pub fn get_kernel_su() -> Option<Version> {
match version {
0 => None,
MIN_KSU_VERSION..=MAX_KSU_VERSION => Some(Version::Supported),
1..MIN_KSU_VERSION => Some(Version::TooOld),
1..=MIN_KSU_VERSION => Some(Version::TooOld),
_ => Some(Version::Abnormal),
}
}

View File

@@ -10,7 +10,6 @@ pub enum RootImpl {
Magisk,
}
// FIXME: OnceCell bugs on 32 bit
static mut ROOT_IMPL: RootImpl = RootImpl::None;
pub fn setup() {

View File

@@ -8,6 +8,17 @@ use std::sync::OnceLock;
use rustix::net::{AddressFamily, bind_unix, listen, socket, SocketAddrUnix, SocketType};
use rustix::thread::gettid;
#[cfg(target_pointer_width = "64")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp64 };
}
#[cfg(target_pointer_width = "32")]
#[macro_export]
macro_rules! lp_select {
($lp32:expr, $lp64:expr) => { $lp32 };
}
#[cfg(debug_assertions)]
#[macro_export]
macro_rules! debug_select {

View File

@@ -18,7 +18,9 @@ static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
pub async fn main() -> Result<()> {
let result = run().await;
set_prop_hint(constants::STATUS_CRASHED)?;
if result.is_err() {
set_prop_hint(constants::STATUS_CRASHED)?;
}
result
}
@@ -34,11 +36,6 @@ async fn run() -> Result<()> {
Ok(())
}
fn spawn_fuse() -> Result<()> {
Command::new("bin/zygisk-fuse").spawn()?;
Ok(())
}
fn check_permission() -> Result<()> {
info!("Check permission");
let uid = getuid();
@@ -114,14 +111,22 @@ async fn spawn_daemon() -> Result<()> {
let mut lives = 5;
loop {
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
let daemon = Command::new(constants::PATH_CP_BIN).spawn()?;
let daemon_pid = daemon.id().unwrap();
async fn daemon_holder(mut daemon: Child) -> Result<()> {
let mut child_ids = vec![];
let daemon32 = Command::new(constants::PATH_CP_BIN32).arg("daemon").spawn();
let daemon64 = Command::new(constants::PATH_CP_BIN64).arg("daemon").spawn();
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
let result = daemon.wait().await?;
bail!("Daemon process {} died: {}", daemon.id().unwrap(), result);
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)));
}
futures.push(Box::pin(daemon_holder(daemon)));
async fn binder_listener() -> Result<()> {
let mut binder = loop {
@@ -148,8 +153,10 @@ async fn spawn_daemon() -> Result<()> {
error!("{}", e);
}
debug!("Killing child process {}", daemon_pid);
let _ = kill_process(Pid::from_raw(daemon_pid as i32).unwrap(), Signal::Kill);
for child in child_ids {
debug!("Killing child process {}", child);
let _ = kill_process(Pid::from_raw(child as i32).unwrap(), Signal::Kill);
}
lives -= 1;
if lives == 0 {

View File

@@ -1,8 +1,8 @@
use std::ffi::c_void;
use crate::constants::{DaemonSocketAction, ProcessFlags};
use crate::utils::UnixStreamExt;
use crate::{constants, dl, root_impl, utils};
use anyhow::Result;
use crate::{constants, dl, lp_select, root_impl, utils};
use anyhow::{bail, Result};
use passfd::FdPassingExt;
use std::sync::Arc;
use std::thread;
@@ -32,11 +32,11 @@ pub fn main() -> Result<()> {
log::info!("Start zygisk companion");
set_parent_process_death_signal(Some(Signal::Kill))?;
let arch = utils::get_property("ro.product.cpu.abi")?;
let arch = get_arch()?;
log::debug!("Daemon architecture: {arch}");
log::info!("Load modules");
let modules = load_modules(&arch)?;
let modules = load_modules(arch)?;
let context = Context {
modules,
@@ -60,6 +60,17 @@ pub fn main() -> Result<()> {
Ok(())
}
fn get_arch() -> Result<&'static str> {
let system_arch = utils::get_property("ro.product.cpu.abi")?;
if system_arch.contains("arm") {
return Ok(lp_select!("armeabi-v7a", "arm64-v8a"));
}
if system_arch.contains("x86") {
return Ok(lp_select!("x86", "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::PATH_MODULES_DIR) {