You've already forked ZygiskNext
mirror of
https://github.com/Dr-TSNG/ZygiskNext.git
synced 2025-08-27 23:46:34 +00:00
Replace native bridge with fuse + ptrace
This commit is contained in:
@@ -13,16 +13,7 @@ LOCAL_LDLIBS := -llog
|
|||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := zygisk_loader
|
LOCAL_MODULE := zygisk
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
|
||||||
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/loader))
|
|
||||||
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
|
|
||||||
LOCAL_STATIC_LIBRARIES := cxx common
|
|
||||||
LOCAL_LDLIBS := -llog
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := zygisk_injector
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||||
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/injector))
|
FILE_LIST := $(filter %.cpp, $(call walk, $(LOCAL_PATH)/injector))
|
||||||
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
|
LOCAL_SRC_FILES := $(FILE_LIST:COMMON_FILE_LIST:$(LOCAL_PATH)/%=%)
|
||||||
|
|||||||
@@ -8,32 +8,14 @@
|
|||||||
|
|
||||||
namespace zygiskd {
|
namespace zygiskd {
|
||||||
|
|
||||||
bool sMagicRead = false;
|
|
||||||
static std::string sSocketName;
|
|
||||||
|
|
||||||
void ReadMagic() {
|
|
||||||
sMagicRead = true;
|
|
||||||
char magic[PATH_MAX]{0};
|
|
||||||
auto fp = fopen(kZygiskMagic, "r");
|
|
||||||
if (fp == nullptr) {
|
|
||||||
PLOGE("Open magic file");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fgets(magic, PATH_MAX, fp);
|
|
||||||
fclose(fp);
|
|
||||||
sSocketName.append(LP_SELECT("zygiskd32", "zygiskd64")).append(magic);
|
|
||||||
LOGD("Socket name: %s", sSocketName.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
int Connect(uint8_t retry) {
|
int Connect(uint8_t retry) {
|
||||||
if (!sMagicRead) ReadMagic();
|
|
||||||
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
struct sockaddr_un addr{
|
struct sockaddr_un addr{
|
||||||
.sun_family = AF_UNIX,
|
.sun_family = AF_UNIX,
|
||||||
.sun_path={0},
|
.sun_path={0},
|
||||||
};
|
};
|
||||||
strcpy(addr.sun_path + 1, sSocketName.data());
|
strcpy(addr.sun_path, kCPSocketPath);
|
||||||
socklen_t socklen = sizeof(sa_family_t) + strlen(addr.sun_path + 1) + 1;
|
socklen_t socklen = sizeof(addr);
|
||||||
|
|
||||||
while (retry--) {
|
while (retry--) {
|
||||||
int r = connect(fd, reinterpret_cast<struct sockaddr*>(&addr), socklen);
|
int r = connect(fd, reinterpret_cast<struct sockaddr*>(&addr), socklen);
|
||||||
@@ -66,16 +48,6 @@ namespace zygiskd {
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ReadNativeBridge() {
|
|
||||||
UniqueFd fd = Connect(1);
|
|
||||||
if (fd == -1) {
|
|
||||||
PLOGE("ReadNativeBridge");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
socket_utils::write_u8(fd, (uint8_t) SocketAction::ReadNativeBridge);
|
|
||||||
return socket_utils::read_string(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t GetProcessFlags(uid_t uid) {
|
uint32_t GetProcessFlags(uid_t uid) {
|
||||||
UniqueFd fd = Connect(1);
|
UniqueFd fd = Connect(1);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
# define LP_SELECT(lp32, lp64) lp32
|
# define LP_SELECT(lp32, lp64) lp32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr auto kZygiskMagic = "/system/zygisk_magic";
|
constexpr auto kCPSocketPath = "/dev/zygisk/" LP_SELECT("cp32", "cp64") ".sock";
|
||||||
|
|
||||||
class UniqueFd {
|
class UniqueFd {
|
||||||
using Fd = int;
|
using Fd = int;
|
||||||
@@ -54,7 +54,6 @@ namespace zygiskd {
|
|||||||
enum class SocketAction {
|
enum class SocketAction {
|
||||||
PingHeartBeat,
|
PingHeartBeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
ReadNativeBridge,
|
|
||||||
GetProcessFlags,
|
GetProcessFlags,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
@@ -65,8 +64,6 @@ namespace zygiskd {
|
|||||||
|
|
||||||
int RequestLogcatFd();
|
int RequestLogcatFd();
|
||||||
|
|
||||||
std::string ReadNativeBridge();
|
|
||||||
|
|
||||||
std::vector<Module> ReadModules();
|
std::vector<Module> ReadModules();
|
||||||
|
|
||||||
uint32_t GetProcessFlags(uid_t uid);
|
uint32_t GetProcessFlags(uid_t uid);
|
||||||
|
|||||||
@@ -8,12 +8,19 @@ using namespace std;
|
|||||||
void *self_handle = nullptr;
|
void *self_handle = nullptr;
|
||||||
|
|
||||||
extern "C" [[gnu::visibility("default")]]
|
extern "C" [[gnu::visibility("default")]]
|
||||||
void entry(void *handle) {
|
void entry(void* handle) {
|
||||||
|
LOGI("Zygisk library injected");
|
||||||
|
self_handle = handle;
|
||||||
|
|
||||||
|
if (!zygiskd::PingHeartbeat()) {
|
||||||
|
LOGE("Zygisk daemon is not running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
logging::setfd(zygiskd::RequestLogcatFd());
|
logging::setfd(zygiskd::RequestLogcatFd());
|
||||||
#endif
|
#endif
|
||||||
self_handle = handle;
|
|
||||||
|
|
||||||
LOGD("Load injector successfully");
|
LOGD("Start hooking");
|
||||||
hook_functions();
|
hook_functions();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
#include <string_view>
|
|
||||||
#include <sys/system_properties.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
#include "daemon.h"
|
|
||||||
#include "dl.h"
|
|
||||||
#include "logging.h"
|
|
||||||
#include "native_bridge_callbacks.h"
|
|
||||||
|
|
||||||
extern "C" [[gnu::visibility("default")]]
|
|
||||||
uint8_t NativeBridgeItf[sizeof(NativeBridgeCallbacks<__ANDROID_API_R__>) * 2]{0};
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr auto kZygoteProcesses = {"zygote", "zygote32", "zygote64", "usap32", "usap64"};
|
|
||||||
constexpr auto kInjector = "/system/" LP_SELECT("lib", "lib64") "/libzygisk_injector.so";
|
|
||||||
|
|
||||||
void* sOriginalBridge = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
__used __attribute__((destructor))
|
|
||||||
void Destructor() {
|
|
||||||
if (sOriginalBridge) {
|
|
||||||
dlclose(sOriginalBridge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__used __attribute__((constructor))
|
|
||||||
void Constructor() {
|
|
||||||
if (getuid() != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view cmdline = getprogname();
|
|
||||||
if (std::none_of(
|
|
||||||
kZygoteProcesses.begin(), kZygoteProcesses.end(),
|
|
||||||
[&](const char* p) { return cmdline == p; }
|
|
||||||
)) {
|
|
||||||
LOGW("Not started as zygote (cmdline=%s)", cmdline.data());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string native_bridge;
|
|
||||||
do {
|
|
||||||
if (!zygiskd::PingHeartbeat()) break;
|
|
||||||
#ifdef NDEBUG
|
|
||||||
logging::setfd(zygiskd::RequestLogcatFd());
|
|
||||||
#endif
|
|
||||||
LOGI("Read native bridge");
|
|
||||||
native_bridge = zygiskd::ReadNativeBridge();
|
|
||||||
|
|
||||||
LOGI("Load injector");
|
|
||||||
auto handle = DlopenExt(kInjector, RTLD_NOW);
|
|
||||||
if (handle == nullptr) {
|
|
||||||
LOGE("Failed to dlopen injector: %s", dlerror());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto entry = dlsym(handle, "entry");
|
|
||||||
if (entry == nullptr) {
|
|
||||||
LOGE("Failed to dlsym injector entry: %s", dlerror());
|
|
||||||
dlclose(handle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
reinterpret_cast<void (*)(void*)>(entry)(handle);
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (native_bridge.empty() || native_bridge == "0") break;
|
|
||||||
|
|
||||||
LOGI("Load original native bridge: %s", native_bridge.data());
|
|
||||||
sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW);
|
|
||||||
if (sOriginalBridge == nullptr) {
|
|
||||||
LOGE("%s", dlerror());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf");
|
|
||||||
if (original_native_bridge_itf == nullptr) {
|
|
||||||
LOGE("%s", dlerror());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
long sdk = 0;
|
|
||||||
char value[PROP_VALUE_MAX + 1];
|
|
||||||
if (__system_property_get("ro.build.version.sdk", value) > 0) {
|
|
||||||
sdk = strtol(value, nullptr, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto callbacks_size = 0;
|
|
||||||
if (sdk >= __ANDROID_API_R__) {
|
|
||||||
callbacks_size = sizeof(NativeBridgeCallbacks<__ANDROID_API_R__>);
|
|
||||||
} else if (sdk == __ANDROID_API_Q__) {
|
|
||||||
callbacks_size = sizeof(NativeBridgeCallbacks<__ANDROID_API_Q__>);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(NativeBridgeItf, original_native_bridge_itf, callbacks_size);
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
logging::setfd(-1);
|
|
||||||
}
|
|
||||||
@@ -102,58 +102,44 @@ extract "$ZIPFILE" 'service.sh' "$MODPATH"
|
|||||||
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
mv "$TMPDIR/sepolicy.rule" "$MODPATH"
|
||||||
|
|
||||||
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
|
HAS32BIT=false && [ -d "/system/lib" ] && HAS32BIT=true
|
||||||
HAS64BIT=false && [ -d "/system/lib64" ] && HAS64BIT=true
|
|
||||||
|
|
||||||
mkdir "$MODPATH/bin"
|
mkdir "$MODPATH/bin"
|
||||||
mkdir "$MODPATH/system"
|
mkdir "$MODPATH/system"
|
||||||
|
mkdir "$MODPATH/system/lib64"
|
||||||
[ "$HAS32BIT" = true ] && mkdir "$MODPATH/system/lib"
|
[ "$HAS32BIT" = true ] && mkdir "$MODPATH/system/lib"
|
||||||
[ "$HAS64BIT" = true ] && mkdir "$MODPATH/system/lib64"
|
|
||||||
|
|
||||||
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
|
||||||
if [ "$HAS32BIT" = true ]; then
|
if [ "$HAS32BIT" = true ]; then
|
||||||
ui_print "- Extracting x86 libraries"
|
ui_print "- Extracting x86 libraries"
|
||||||
extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true
|
extract "$ZIPFILE" 'bin/x86/zygiskd' "$MODPATH/bin" true
|
||||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
|
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygisk-cp32"
|
||||||
extract "$ZIPFILE" 'lib/x86/libzygisk_injector.so' "$MODPATH/system/lib" true
|
extract "$ZIPFILE" 'lib/x86/libzygisk.so' "$MODPATH/system/lib" true
|
||||||
extract "$ZIPFILE" 'lib/x86/libzygisk_loader.so' "$MODPATH/system/lib" true
|
|
||||||
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$HAS64BIT" = true ]; then
|
ui_print "- Extracting x64 libraries"
|
||||||
ui_print "- Extracting x64 libraries"
|
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
|
||||||
extract "$ZIPFILE" 'bin/x86_64/zygiskd' "$MODPATH/bin" true
|
extract "$ZIPFILE" 'lib/x86_64/libzygisk.so' "$MODPATH/system/lib64" true
|
||||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
|
||||||
extract "$ZIPFILE" 'lib/x86_64/libzygisk_injector.so' "$MODPATH/system/lib64" true
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
|
||||||
extract "$ZIPFILE" 'lib/x86_64/libzygisk_loader.so' "$MODPATH/system/lib64" true
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp64"
|
||||||
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
if [ "$HAS32BIT" = true ]; then
|
if [ "$HAS32BIT" = true ]; then
|
||||||
ui_print "- Extracting arm libraries"
|
ui_print "- Extracting arm libraries"
|
||||||
extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true
|
extract "$ZIPFILE" 'bin/armeabi-v7a/zygiskd' "$MODPATH/bin" true
|
||||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd32"
|
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygisk-cp32"
|
||||||
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_injector.so' "$MODPATH/system/lib" true
|
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk.so' "$MODPATH/system/lib" true
|
||||||
extract "$ZIPFILE" 'lib/armeabi-v7a/libzygisk_loader.so' "$MODPATH/system/lib" true
|
|
||||||
ln -sf "zygiskd32" "$MODPATH/bin/zygiskwd"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$HAS64BIT" = true ]; then
|
ui_print "- Extracting arm64 libraries"
|
||||||
ui_print "- Extracting arm64 libraries"
|
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
|
||||||
extract "$ZIPFILE" 'bin/arm64-v8a/zygiskd' "$MODPATH/bin" true
|
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk.so' "$MODPATH/system/lib64" true
|
||||||
mv "$MODPATH/bin/zygiskd" "$MODPATH/bin/zygiskd64"
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-wd"
|
||||||
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_injector.so' "$MODPATH/system/lib64" true
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-fuse"
|
||||||
extract "$ZIPFILE" 'lib/arm64-v8a/libzygisk_loader.so' "$MODPATH/system/lib64" true
|
ln -sf "zygiskd" "$MODPATH/bin/zygisk-cp64"
|
||||||
ln -sf "zygiskd64" "$MODPATH/bin/zygiskwd"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ui_print "- Generating magic"
|
|
||||||
MAGIC=$(tr -dc 'a-f0-9' </dev/urandom | head -c 18)
|
|
||||||
echo -n "$MAGIC" > "$MODPATH/system/zygisk_magic"
|
|
||||||
|
|
||||||
ui_print "- Setting permissions"
|
ui_print "- Setting permissions"
|
||||||
chmod 0744 "$MODPATH/daemon.sh"
|
set_perm_recursive "$MODPATH/bin" 0 0 0755 0755
|
||||||
set_perm_recursive "$MODPATH/bin" 0 2000 0755 0755
|
|
||||||
set_perm_recursive "$MODPATH/system/lib" 0 0 0755 0644 u:object_r:system_lib_file:s0
|
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
|
set_perm_recursive "$MODPATH/system/lib64" 0 0 0755 0644 u:object_r:system_lib_file:s0
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ if [ "$ZYGISK_ENABLED" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$MODDIR"
|
cd "$MODDIR"
|
||||||
getprop ro.dalvik.vm.native.bridge > /dev/.native_bridge
|
|
||||||
resetprop ro.dalvik.vm.native.bridge libzygisk_loader.so
|
|
||||||
|
|
||||||
if [ "$(which magisk)" ]; then
|
if [ "$(which magisk)" ]; then
|
||||||
for file in ../*; do
|
for file in ../*; do
|
||||||
@@ -21,3 +19,5 @@ if [ "$(which magisk)" ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
sh -c "bin/zygisk-fuse &"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
deny vold fusectlfs file write
|
||||||
|
|
||||||
allow * tmpfs * *
|
allow * tmpfs * *
|
||||||
allow zygote appdomain_tmpfs dir *
|
allow zygote appdomain_tmpfs dir *
|
||||||
allow zygote appdomain_tmpfs file *
|
allow zygote appdomain_tmpfs file *
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ if [ "$ZYGISK_ENABLED" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$MODDIR"
|
cd "$MODDIR"
|
||||||
export NATIVE_BRIDGE=$(cat /dev/.native_bridge)
|
|
||||||
rm /dev/.native_bridge
|
|
||||||
|
|
||||||
if [ "$(which magisk)" ]; then
|
if [ "$(which magisk)" ]; then
|
||||||
for file in ../*; do
|
for file in ../*; do
|
||||||
@@ -26,4 +24,4 @@ fi
|
|||||||
|
|
||||||
log -p i -t "zygisksu" "Start watchdog"
|
log -p i -t "zygisksu" "Start watchdog"
|
||||||
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
[ "$DEBUG" = true ] && export RUST_BACKTRACE=1
|
||||||
exec "bin/zygiskwd" "watchdog" >/dev/null 2>&1
|
exec "bin/zygisk-wd" >/dev/null 2>&1
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ rust-version = "1.69"
|
|||||||
android_logger = "0.13"
|
android_logger = "0.13"
|
||||||
anyhow = { version = "1.0", features = ["backtrace"] }
|
anyhow = { version = "1.0", features = ["backtrace"] }
|
||||||
bitflags = { version = "2.3" }
|
bitflags = { version = "2.3" }
|
||||||
clap = { version = "4", features = ["derive"] }
|
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
konst = "0.3"
|
konst = "0.3"
|
||||||
@@ -19,11 +18,13 @@ log = "0.4"
|
|||||||
memfd = "0.6"
|
memfd = "0.6"
|
||||||
num_enum = "0.5"
|
num_enum = "0.5"
|
||||||
passfd = "0.1"
|
passfd = "0.1"
|
||||||
rand = "0.8"
|
proc-maps = "0.3"
|
||||||
|
ptrace-do = "0.1"
|
||||||
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
|
rustix = { version = "0.38", features = [ "fs", "process", "mount", "net", "thread"] }
|
||||||
tokio = { version = "1.28", features = ["full"] }
|
tokio = { version = "1.28", features = ["full"] }
|
||||||
|
|
||||||
binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" }
|
binder = { git = "https://github.com/Kernel-SU/binder_rs", rev = "c9f2b62d6a744fd2264056c638c1b061a6a2932d" }
|
||||||
|
fuser = { git = "https://github.com/Dr-TSNG/fuser", default-features = false }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
|||||||
@@ -17,15 +17,25 @@ pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Trace;
|
|||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
pub const MAX_LOG_LEVEL: LevelFilter = LevelFilter::Info;
|
||||||
|
|
||||||
pub const PROP_NATIVE_BRIDGE: &str = "ro.dalvik.vm.native.bridge";
|
|
||||||
pub const PROP_CTL_RESTART: &str = "ctl.restart";
|
pub const PROP_CTL_RESTART: &str = "ctl.restart";
|
||||||
pub const ZYGISK_LOADER: &str = "libzygisk_loader.so";
|
pub const ZYGISK_LIBRARY: &str = "libzygisk.so";
|
||||||
pub const ZYGISK_MAGIC: &str = "/system/zygisk_magic";
|
|
||||||
|
pub const PATH_PCL: &str = "/system/etc/preloaded-classes";
|
||||||
|
pub const PATH_SYSTEM_LIB32: &str = "/system/lib";
|
||||||
|
pub const PATH_SYSTEM_LIB64: &str = "/system/lib64";
|
||||||
|
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");
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, "/cp64.sock");
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
pub const PATH_CP_SOCKET: &str = concatcp!(PATH_WORK_DIR, "/cp32.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_MODULES_DIR: &str = "..";
|
||||||
pub const PATH_MODULE_PROP: &str = "module.prop";
|
pub const PATH_MODULE_PROP: &str = "module.prop";
|
||||||
pub const PATH_ZYGISKD32: &str = "bin/zygiskd32";
|
pub const PATH_CP32_BIN: &str = "bin/zygisk-cp32";
|
||||||
pub const PATH_ZYGISKD64: &str = "bin/zygiskd64";
|
pub const PATH_CP64_BIN: &str = "bin/zygisk-cp64";
|
||||||
|
|
||||||
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
|
pub const STATUS_LOADED: &str = "😋 Zygisksu is loaded";
|
||||||
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
|
pub const STATUS_CRASHED: &str = "❌ Zygiskd has crashed";
|
||||||
@@ -39,7 +49,6 @@ pub const STATUS_ROOT_IMPL_MULTIPLE: &str = "❌ Multiple root implementations i
|
|||||||
pub enum DaemonSocketAction {
|
pub enum DaemonSocketAction {
|
||||||
PingHeartbeat,
|
PingHeartbeat,
|
||||||
RequestLogcatFd,
|
RequestLogcatFd,
|
||||||
ReadNativeBridge,
|
|
||||||
GetProcessFlags,
|
GetProcessFlags,
|
||||||
ReadModules,
|
ReadModules,
|
||||||
RequestCompanionSocket,
|
RequestCompanionSocket,
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::ffi::{c_char, c_void};
|
use std::ffi::{c_char, c_void};
|
||||||
|
|
||||||
const ANDROID_NAMESPACE_TYPE_SHARED: u64 = 0x2;
|
pub const ANDROID_NAMESPACE_TYPE_SHARED: u64 = 0x2;
|
||||||
const ANDROID_DLEXT_USE_NAMESPACE: u64 = 0x200;
|
pub const ANDROID_DLEXT_USE_NAMESPACE: u64 = 0x200;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
struct AndroidNamespace {
|
pub struct AndroidNamespace {
|
||||||
_unused: [u8; 0],
|
_unused: [u8; 0],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct AndroidDlextinfo {
|
pub struct AndroidDlextinfo {
|
||||||
pub flags: u64,
|
pub flags: u64,
|
||||||
pub reserved_addr: *mut c_void,
|
pub reserved_addr: *mut c_void,
|
||||||
pub reserved_size: libc::size_t,
|
pub reserved_size: libc::size_t,
|
||||||
@@ -22,7 +22,7 @@ struct AndroidDlextinfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn android_dlopen_ext(
|
pub fn android_dlopen_ext(
|
||||||
filename: *const c_char,
|
filename: *const c_char,
|
||||||
flags: libc::c_int,
|
flags: libc::c_int,
|
||||||
extinfo: *const AndroidDlextinfo,
|
extinfo: *const AndroidDlextinfo,
|
||||||
|
|||||||
335
zygiskd/src/fuse.rs
Normal file
335
zygiskd/src/fuse.rs
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
use std::cmp::min;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use std::ffi::{CString, OsStr};
|
||||||
|
use std::{fs, thread};
|
||||||
|
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 rustix::path::Arg;
|
||||||
|
use rustix::process::getpid;
|
||||||
|
use crate::{constants, dl};
|
||||||
|
use crate::utils::LateInit;
|
||||||
|
|
||||||
|
pub struct DelegateFilesystem;
|
||||||
|
|
||||||
|
const fn attr(inode: u64, size: u64, kind: FileType) -> FileAttr {
|
||||||
|
FileAttr {
|
||||||
|
ino: inode,
|
||||||
|
size,
|
||||||
|
blocks: 0,
|
||||||
|
atime: SystemTime::UNIX_EPOCH,
|
||||||
|
mtime: SystemTime::UNIX_EPOCH,
|
||||||
|
ctime: SystemTime::UNIX_EPOCH,
|
||||||
|
crtime: SystemTime::UNIX_EPOCH,
|
||||||
|
kind,
|
||||||
|
perm: 0o644,
|
||||||
|
nlink: 0,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
blksize: 0,
|
||||||
|
flags: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ANDROID_LIBC: &str = "bionic/libc.so";
|
||||||
|
const ANDROID_LIBDL: &str = "bionic/libdl.so";
|
||||||
|
|
||||||
|
const INO_DIR: u64 = 1;
|
||||||
|
const INO_PCL: u64 = 2;
|
||||||
|
|
||||||
|
static ATTR_DIR: FileAttr = attr(INO_DIR, 0, FileType::Directory);
|
||||||
|
static ATTR_PCL: LateInit<FileAttr> = LateInit::new();
|
||||||
|
|
||||||
|
static PCL_CONTENT: LateInit<Vec<u8>> = LateInit::new();
|
||||||
|
|
||||||
|
const ENTRIES: &[(u64, FileType, &str)] = &[
|
||||||
|
(INO_DIR, FileType::Directory, "."),
|
||||||
|
(INO_DIR, FileType::Directory, ".."),
|
||||||
|
(INO_PCL, FileType::RegularFile, "preloaded-classes"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const TTL: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
impl Filesystem for DelegateFilesystem {
|
||||||
|
fn lookup(&mut self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
||||||
|
if parent != INO_DIR {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match name.as_str().unwrap() {
|
||||||
|
"preloaded-classes" => reply.entry(&TTL, &ATTR_PCL, 0),
|
||||||
|
_ => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) {
|
||||||
|
match ino {
|
||||||
|
INO_DIR => reply.attr(&TTL, &ATTR_DIR),
|
||||||
|
INO_PCL => reply.attr(&TTL, &ATTR_PCL),
|
||||||
|
_ => reply.error(ENOENT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self, req: &Request<'_>, ino: u64, _flags: i32, reply: ReplyOpen) {
|
||||||
|
if ino == INO_PCL {
|
||||||
|
let pid = req.pid();
|
||||||
|
let process = format!("/proc/{}/cmdline", pid);
|
||||||
|
let process = fs::read_to_string(process).unwrap();
|
||||||
|
let process = &process[..process.find('\0').unwrap()];
|
||||||
|
info!("Process {} is reading preloaded-classes", process);
|
||||||
|
match process {
|
||||||
|
// "zygote" => ptrace_zygote(pid, false).unwrap(),
|
||||||
|
"zygote64" => ptrace_zygote(pid, true).unwrap(),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply.opened(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, size: u32, _flags: i32, _lock_owner: Option<u64>, reply: ReplyData) {
|
||||||
|
let offset = offset as usize;
|
||||||
|
let size = size as usize;
|
||||||
|
if ino == INO_PCL {
|
||||||
|
let len = PCL_CONTENT.len();
|
||||||
|
if offset >= len {
|
||||||
|
reply.data(&[]);
|
||||||
|
} else {
|
||||||
|
let end = min(offset + size, len);
|
||||||
|
reply.data(&PCL_CONTENT[offset..end]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(&mut self, _req: &Request<'_>, ino: u64, _fh: u64, offset: i64, mut reply: ReplyDirectory) {
|
||||||
|
if ino != INO_DIR {
|
||||||
|
reply.error(ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i, entry) in ENTRIES.iter().enumerate().skip(offset as usize) {
|
||||||
|
if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, is64: bool) -> Result<()> {
|
||||||
|
static LAST_32: Mutex<u32> = Mutex::new(0);
|
||||||
|
static LAST_64: Mutex<u32> = Mutex::new(0);
|
||||||
|
|
||||||
|
let mut last = match is64 {
|
||||||
|
true => LAST_64.lock().unwrap(),
|
||||||
|
false => LAST_32.lock().unwrap(),
|
||||||
|
};
|
||||||
|
if *last == pid {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
*last = pid;
|
||||||
|
let (sender, receiver) = mpsc::channel::<()>();
|
||||||
|
|
||||||
|
let worker = move || -> Result<()> {
|
||||||
|
info!("Injecting into pid {}, is64 = {}", pid, is64);
|
||||||
|
let lib_dir = match is64 {
|
||||||
|
true => constants::PATH_SYSTEM_LIB64,
|
||||||
|
false => constants::PATH_SYSTEM_LIB32,
|
||||||
|
};
|
||||||
|
let zygisk_lib = format!("{}/{}", lib_dir, constants::ZYGISK_LIBRARY);
|
||||||
|
let lib_dir = CString::new(lib_dir)?;
|
||||||
|
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<()> {
|
||||||
|
fs::create_dir(constants::PATH_WORK_DIR)?;
|
||||||
|
fs::create_dir(constants::PATH_FUSE_DIR)?;
|
||||||
|
PCL_CONTENT.init(fs::read(constants::PATH_PCL)?);
|
||||||
|
ATTR_PCL.init(attr(INO_PCL, PCL_CONTENT.len() as u64, FileType::RegularFile));
|
||||||
|
let options = [
|
||||||
|
fuser::MountOption::FSName(String::from("zygisk")),
|
||||||
|
fuser::MountOption::AllowOther,
|
||||||
|
fuser::MountOption::RO,
|
||||||
|
];
|
||||||
|
let session = fuser::spawn_mount2(
|
||||||
|
DelegateFilesystem,
|
||||||
|
constants::PATH_FUSE_DIR,
|
||||||
|
&options,
|
||||||
|
)?;
|
||||||
|
mount_bind(constants::PATH_FUSE_PCL, constants::PATH_PCL)?;
|
||||||
|
session.join();
|
||||||
|
bail!("Fuse mount exited unexpectedly");
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
use std::fs;
|
|
||||||
use anyhow::Result;
|
|
||||||
use crate::constants;
|
|
||||||
use crate::utils::LateInit;
|
|
||||||
|
|
||||||
pub static MAGIC: LateInit<String> = LateInit::new();
|
|
||||||
pub static PATH_TMP_DIR: LateInit<String> = LateInit::new();
|
|
||||||
pub static PATH_TMP_PROP: LateInit<String> = LateInit::new();
|
|
||||||
|
|
||||||
pub fn setup() -> Result<()> {
|
|
||||||
let name = fs::read_to_string(constants::ZYGISK_MAGIC)?;
|
|
||||||
let path_tmp_dir = format!("/dev/{}", name);
|
|
||||||
let path_tmp_prop = format!("{}/module.prop", path_tmp_dir);
|
|
||||||
|
|
||||||
MAGIC.init(name);
|
|
||||||
PATH_TMP_DIR.init(path_tmp_dir);
|
|
||||||
PATH_TMP_PROP.init(path_tmp_prop);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -3,29 +3,13 @@
|
|||||||
|
|
||||||
mod constants;
|
mod constants;
|
||||||
mod dl;
|
mod dl;
|
||||||
mod magic;
|
mod fuse;
|
||||||
mod root_impl;
|
mod root_impl;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod watchdog;
|
mod watchdog;
|
||||||
mod zygiskd;
|
mod zygiskd;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Subcommand, Parser};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
#[command(author, version = constants::VERSION_FULL, about, long_about = None)]
|
|
||||||
struct Args {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Commands,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
|
||||||
enum Commands {
|
|
||||||
/// Start zygisk watchdog
|
|
||||||
Watchdog,
|
|
||||||
/// Start zygisk daemon
|
|
||||||
Daemon,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn init_android_logger(tag: &str) {
|
fn init_android_logger(tag: &str) {
|
||||||
@@ -36,14 +20,15 @@ fn init_android_logger(tag: &str) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start() -> Result<()> {
|
async fn start(name: &str) -> Result<()> {
|
||||||
root_impl::setup();
|
root_impl::setup();
|
||||||
magic::setup()?;
|
match name.trim_start_matches("zygisk-") {
|
||||||
let cli = Args::parse();
|
"wd" => watchdog::main().await?,
|
||||||
match cli.command {
|
"fuse" => fuse::main()?,
|
||||||
Commands::Watchdog => watchdog::entry().await?,
|
"cp32" => zygiskd::main()?,
|
||||||
Commands::Daemon => zygiskd::entry()?,
|
"cp64" => zygiskd::main()?,
|
||||||
};
|
_ => println!("Available commands: wd, fuse, cp"),
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +38,7 @@ async fn main() {
|
|||||||
let nice_name = process.split('/').last().unwrap();
|
let nice_name = process.split('/').last().unwrap();
|
||||||
init_android_logger(nice_name);
|
init_android_logger(nice_name);
|
||||||
|
|
||||||
if let Err(e) = start().await {
|
if let Err(e) = start(nice_name).await {
|
||||||
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
log::error!("Crashed: {}\n{}", e, e.backtrace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream, process::Command};
|
use std::{fs, io::{Read, Write}, os::unix::net::UnixStream};
|
||||||
use std::ffi::c_char;
|
use std::ffi::c_char;
|
||||||
use std::os::unix::net::UnixListener;
|
use std::os::unix::net::UnixListener;
|
||||||
|
use std::process::Command;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
|
||||||
use rustix::net::{AddressFamily, bind_unix, listen, socket, SocketAddrUnix, SocketType};
|
use rustix::net::{AddressFamily, bind_unix, listen, socket, SocketAddrUnix, SocketType};
|
||||||
use rustix::thread::gettid;
|
use rustix::thread::gettid;
|
||||||
|
|
||||||
@@ -50,10 +50,6 @@ impl<T> std::ops::Deref for LateInit<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn random_string() -> String {
|
|
||||||
Alphanumeric.sample_string(&mut rand::thread_rng(), 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_socket_create_context(context: &str) -> Result<()> {
|
pub fn set_socket_create_context(context: &str) -> Result<()> {
|
||||||
let path = "/proc/thread-self/attr/sockcreate";
|
let path = "/proc/thread-self/attr/sockcreate";
|
||||||
match fs::write(path, context) {
|
match fs::write(path, context) {
|
||||||
@@ -66,15 +62,16 @@ pub fn set_socket_create_context(context: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_native_bridge() -> String {
|
pub fn chcon(path: &str, context: &str) -> Result<()> {
|
||||||
std::env::var("NATIVE_BRIDGE").unwrap_or_default()
|
Command::new("chcon").arg(context).arg(path).status()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_raw(level: i32, tag: &str, message: &str) -> Result<()> {
|
pub fn log_raw(level: i32, tag: &str, message: &str) -> Result<()> {
|
||||||
let tag = std::ffi::CString::new(tag)?;
|
let tag = std::ffi::CString::new(tag)?;
|
||||||
let message = std::ffi::CString::new(message)?;
|
let message = std::ffi::CString::new(message)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
__android_log_print(level as i32, tag.as_ptr(), message.as_ptr());
|
__android_log_print(level, tag.as_ptr(), message.as_ptr());
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -89,10 +86,11 @@ pub fn get_property(name: &str) -> Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_property(name: &str, value: &str) -> Result<()> {
|
pub fn set_property(name: &str, value: &str) -> Result<()> {
|
||||||
Command::new("resetprop")
|
let name = std::ffi::CString::new(name)?;
|
||||||
.arg(name)
|
let value = std::ffi::CString::new(value)?;
|
||||||
.arg(value)
|
unsafe {
|
||||||
.spawn()?.wait()?;
|
__system_property_set(name.as_ptr(), value.as_ptr());
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,15 +153,18 @@ impl UnixStreamExt for UnixStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abstract_namespace_socket(name: &str) -> Result<UnixListener> {
|
pub fn unix_listener_from_path(path: &str) -> Result<UnixListener> {
|
||||||
let addr = SocketAddrUnix::new_abstract_name(name.as_bytes())?;
|
let _ = fs::remove_file(path);
|
||||||
|
let addr = SocketAddrUnix::new(path)?;
|
||||||
let socket = socket(AddressFamily::UNIX, SocketType::STREAM, None)?;
|
let socket = socket(AddressFamily::UNIX, SocketType::STREAM, None)?;
|
||||||
bind_unix(&socket, &addr)?;
|
bind_unix(&socket, &addr)?;
|
||||||
listen(&socket, 2)?;
|
listen(&socket, 2)?;
|
||||||
|
chcon(path, "u:object_r:magisk_file:s0")?;
|
||||||
Ok(UnixListener::from(socket))
|
Ok(UnixListener::from(socket))
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32;
|
||||||
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
fn __system_property_get(name: *const c_char, value: *mut c_char) -> u32;
|
||||||
|
fn __system_property_set(name: *const c_char, value: *const c_char) -> u32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,46 @@
|
|||||||
use crate::{constants, magic, root_impl, utils};
|
use crate::{constants, root_impl, utils};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::os::unix::net::UnixListener;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use binder::IBinder;
|
use binder::IBinder;
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use log::info;
|
||||||
use rustix::mount::mount_bind;
|
use rustix::mount::mount_bind;
|
||||||
use rustix::process::{getgid, getuid, kill_process, Pid, Signal};
|
use rustix::process::{getgid, getuid, kill_process, Pid, Signal};
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
use crate::utils::LateInit;
|
use crate::utils::LateInit;
|
||||||
|
|
||||||
static LOCK: LateInit<UnixListener> = LateInit::new();
|
|
||||||
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
static PROP_SECTIONS: LateInit<[String; 2]> = LateInit::new();
|
||||||
|
|
||||||
pub async fn entry() -> Result<()> {
|
pub async fn main() -> Result<()> {
|
||||||
log::info!("Start zygisksu watchdog");
|
let result = run().await;
|
||||||
|
set_prop_hint(constants::STATUS_CRASHED)?;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run() -> Result<()> {
|
||||||
|
info!("Start zygisksu watchdog");
|
||||||
check_permission()?;
|
check_permission()?;
|
||||||
ensure_single_instance()?;
|
|
||||||
mount_prop().await?;
|
mount_prop().await?;
|
||||||
if check_and_set_hint()? == false {
|
if check_and_set_hint()? == false {
|
||||||
log::warn!("Requirements not met, exiting");
|
log::warn!("Requirements not met, exiting");
|
||||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let end = spawn_daemon().await;
|
spawn_daemon().await?;
|
||||||
set_prop_hint(constants::STATUS_CRASHED)?;
|
Ok(())
|
||||||
end
|
}
|
||||||
|
|
||||||
|
fn spawn_fuse() -> Result<()> {
|
||||||
|
Command::new("bin/zygisk-fuse").spawn()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_permission() -> Result<()> {
|
fn check_permission() -> Result<()> {
|
||||||
log::info!("Check permission");
|
info!("Check permission");
|
||||||
let uid = getuid();
|
let uid = getuid();
|
||||||
if !uid.is_root() {
|
if !uid.is_root() {
|
||||||
bail!("UID is not 0");
|
bail!("UID is not 0");
|
||||||
@@ -53,16 +60,6 @@ fn check_permission() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_single_instance() -> Result<()> {
|
|
||||||
log::info!("Ensure single instance");
|
|
||||||
let name = format!("zygiskwd{}", magic::MAGIC.as_str());
|
|
||||||
match utils::abstract_namespace_socket(&name) {
|
|
||||||
Ok(socket) => LOCK.init(socket),
|
|
||||||
Err(e) => bail!("Failed to acquire lock: {e}. Maybe another instance is running?")
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn mount_prop() -> Result<()> {
|
async fn mount_prop() -> Result<()> {
|
||||||
let module_prop = if let root_impl::RootImpl::Magisk = root_impl::get_impl() {
|
let module_prop = if let root_impl::RootImpl::Magisk = root_impl::get_impl() {
|
||||||
let magisk_path = Command::new("magisk").arg("--path").output().await?;
|
let magisk_path = Command::new("magisk").arg("--path").output().await?;
|
||||||
@@ -74,7 +71,7 @@ async fn mount_prop() -> Result<()> {
|
|||||||
} else {
|
} else {
|
||||||
constants::PATH_MODULE_PROP.to_string()
|
constants::PATH_MODULE_PROP.to_string()
|
||||||
};
|
};
|
||||||
log::info!("Mount {module_prop}");
|
info!("Mount {module_prop}");
|
||||||
let module_prop_file = fs::File::open(&module_prop)?;
|
let module_prop_file = fs::File::open(&module_prop)?;
|
||||||
let mut section = 0;
|
let mut section = 0;
|
||||||
let mut sections: [String; 2] = [String::new(), String::new()];
|
let mut sections: [String; 2] = [String::new(), String::new()];
|
||||||
@@ -93,15 +90,13 @@ async fn mount_prop() -> Result<()> {
|
|||||||
}
|
}
|
||||||
PROP_SECTIONS.init(sections);
|
PROP_SECTIONS.init(sections);
|
||||||
|
|
||||||
fs::create_dir(magic::PATH_TMP_DIR.as_str())?;
|
fs::File::create(constants::PATH_PROP_OVERLAY)?;
|
||||||
fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
mount_bind(constants::PATH_PROP_OVERLAY, &module_prop)?;
|
||||||
|
|
||||||
mount_bind(magic::PATH_TMP_PROP.as_str(), &module_prop)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_prop_hint(hint: &str) -> Result<()> {
|
fn set_prop_hint(hint: &str) -> Result<()> {
|
||||||
let mut file = fs::File::create(magic::PATH_TMP_PROP.as_str())?;
|
let mut file = fs::File::create(constants::PATH_PROP_OVERLAY)?;
|
||||||
file.write_all(PROP_SECTIONS[0].as_bytes())?;
|
file.write_all(PROP_SECTIONS[0].as_bytes())?;
|
||||||
file.write_all(b"[")?;
|
file.write_all(b"[")?;
|
||||||
file.write_all(hint.as_bytes())?;
|
file.write_all(hint.as_bytes())?;
|
||||||
@@ -131,8 +126,8 @@ async fn spawn_daemon() -> Result<()> {
|
|||||||
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
|
let mut futures = FuturesUnordered::<Pin<Box<dyn Future<Output=Result<()>>>>>::new();
|
||||||
let mut child_ids = vec![];
|
let mut child_ids = vec![];
|
||||||
|
|
||||||
let daemon32 = Command::new(constants::PATH_ZYGISKD32).arg("daemon").spawn();
|
let daemon32 = Command::new(constants::PATH_CP32_BIN).spawn();
|
||||||
let daemon64 = Command::new(constants::PATH_ZYGISKD64).arg("daemon").spawn();
|
let daemon64 = Command::new(constants::PATH_CP64_BIN).spawn();
|
||||||
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
|
async fn spawn_daemon(mut daemon: Child) -> Result<()> {
|
||||||
let result = daemon.wait().await?;
|
let result = daemon.wait().await?;
|
||||||
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
log::error!("Daemon process {} died: {}", daemon.id().unwrap(), result);
|
||||||
@@ -158,8 +153,7 @@ async fn spawn_daemon() -> Result<()> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
log::info!("System server ready, restore native bridge");
|
info!("System server ready");
|
||||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, &utils::get_native_bridge())?;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if binder.ping_binder().is_err() { break; }
|
if binder.ping_binder().is_err() { break; }
|
||||||
@@ -185,7 +179,6 @@ async fn spawn_daemon() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log::error!("Restarting zygote...");
|
log::error!("Restarting zygote...");
|
||||||
utils::set_property(constants::PROP_NATIVE_BRIDGE, constants::ZYGISK_LOADER)?;
|
|
||||||
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
|
utils::set_property(constants::PROP_CTL_RESTART, "zygote")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
use crate::constants::{DaemonSocketAction, ProcessFlags};
|
||||||
use crate::utils::UnixStreamExt;
|
use crate::utils::UnixStreamExt;
|
||||||
use crate::{constants, dl, lp_select, magic, root_impl, utils};
|
use crate::{constants, dl, lp_select, root_impl, utils};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use passfd::FdPassingExt;
|
use passfd::FdPassingExt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -14,7 +14,7 @@ use std::os::unix::{
|
|||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use rustix::fs::fstat;
|
use rustix::fs::fstat;
|
||||||
use rustix::process::Signal;
|
use rustix::process::{set_parent_process_death_signal, Signal};
|
||||||
|
|
||||||
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
type ZygiskCompanionEntryFn = unsafe extern "C" fn(i32);
|
||||||
|
|
||||||
@@ -25,12 +25,11 @@ struct Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
native_bridge: String,
|
|
||||||
modules: Vec<Module>,
|
modules: Vec<Module>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entry() -> Result<()> {
|
pub fn main() -> Result<()> {
|
||||||
rustix::process::set_parent_process_death_signal(Some(Signal::Kill))?;
|
set_parent_process_death_signal(Some(Signal::Kill))?;
|
||||||
|
|
||||||
let arch = get_arch()?;
|
let arch = get_arch()?;
|
||||||
log::debug!("Daemon architecture: {arch}");
|
log::debug!("Daemon architecture: {arch}");
|
||||||
@@ -39,7 +38,6 @@ pub fn entry() -> Result<()> {
|
|||||||
let modules = load_modules(arch)?;
|
let modules = load_modules(arch)?;
|
||||||
|
|
||||||
let context = Context {
|
let context = Context {
|
||||||
native_bridge: utils::get_native_bridge(),
|
|
||||||
modules,
|
modules,
|
||||||
};
|
};
|
||||||
let context = Arc::new(context);
|
let context = Arc::new(context);
|
||||||
@@ -131,10 +129,8 @@ fn create_library_fd(so_path: &PathBuf) -> Result<OwnedFd> {
|
|||||||
|
|
||||||
fn create_daemon_socket() -> Result<UnixListener> {
|
fn create_daemon_socket() -> Result<UnixListener> {
|
||||||
utils::set_socket_create_context("u:r:zygote:s0")?;
|
utils::set_socket_create_context("u:r:zygote:s0")?;
|
||||||
let prefix = lp_select!("zygiskd32", "zygiskd64");
|
log::debug!("Daemon socket: {}", constants::PATH_CP_SOCKET);
|
||||||
let name = format!("{}{}", prefix, magic::MAGIC.as_str());
|
let listener = utils::unix_listener_from_path(constants::PATH_CP_SOCKET)?;
|
||||||
let listener = utils::abstract_namespace_socket(&name)?;
|
|
||||||
log::debug!("Daemon socket: {name}");
|
|
||||||
Ok(listener)
|
Ok(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +166,6 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()>
|
|||||||
utils::log_raw(level as i32, &tag, &message)?;
|
utils::log_raw(level as i32, &tag, &message)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DaemonSocketAction::ReadNativeBridge => {
|
|
||||||
stream.write_string(&context.native_bridge)?;
|
|
||||||
}
|
|
||||||
DaemonSocketAction::GetProcessFlags => {
|
DaemonSocketAction::GetProcessFlags => {
|
||||||
let uid = stream.read_u32()? as i32;
|
let uid = stream.read_u32()? as i32;
|
||||||
let mut flags = ProcessFlags::empty();
|
let mut flags = ProcessFlags::empty();
|
||||||
|
|||||||
Reference in New Issue
Block a user