From 814476ea7a461d1e0c7bd2bf3e7b1b3408127903 Mon Sep 17 00:00:00 2001 From: Nullptr Date: Wed, 8 Feb 2023 14:49:32 +0800 Subject: [PATCH] Log to zygiskd --- loader/src/common/daemon.cpp | 11 +++++- loader/src/common/logging.cpp | 36 +++++++++++++++++++ loader/src/common/socket_utils.cpp | 4 +++ loader/src/include/daemon.h | 3 ++ loader/src/include/logging.h | 21 +++++++---- loader/src/include/socket_utils.h | 3 ++ loader/src/injector/entry.cpp | 5 ++- loader/src/injector/hook.cpp | 20 ++++++++--- loader/src/loader/loader.cpp | 56 +++++++++++++++++------------- module/build.gradle.kts | 1 - module/src/customize.sh | 27 ++++++++++---- zygiskd/src/constants.rs | 1 + zygiskd/src/utils.rs | 15 ++++++++ zygiskd/src/zygisk.rs | 28 +++++++++++---- 14 files changed, 180 insertions(+), 51 deletions(-) create mode 100644 loader/src/common/logging.cpp diff --git a/loader/src/common/daemon.cpp b/loader/src/common/daemon.cpp index 832f1c9..82a69e3 100644 --- a/loader/src/common/daemon.cpp +++ b/loader/src/common/daemon.cpp @@ -29,7 +29,6 @@ namespace zygiskd { } bool PingHeartbeat() { - LOGD("Daemon socket: %s", kZygiskSocket.data()); UniqueFd fd = Connect(5); if (fd == -1) { PLOGE("Connect to zygiskd"); @@ -39,6 +38,16 @@ namespace zygiskd { return true; } + int RequestLogcatFd() { + int fd = Connect(1); + if (fd == -1) { + PLOGE("RequestLogcatFd"); + return -1; + } + socket_utils::write_u8(fd, (uint8_t) SocketAction::RequestLogcatFd); + return fd; + } + std::string ReadNativeBridge() { UniqueFd fd = Connect(1); if (fd == -1) { diff --git a/loader/src/common/logging.cpp b/loader/src/common/logging.cpp new file mode 100644 index 0000000..887025f --- /dev/null +++ b/loader/src/common/logging.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include "logging.h" +#include "socket_utils.h" + +namespace logging { + static int logfd = -1; + + void setfd(int fd) { + close(logfd); + logfd = fd; + } + + int getfd() { + return logfd; + } + + void log(int prio, const char* tag, const char* fmt, ...) { + if (logfd == -1) { + va_list ap; + va_start(ap, fmt); + __android_log_vprint(prio, tag, fmt, ap); + va_end(ap); + } else { + char buf[BUFSIZ]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + socket_utils::write_u8(logfd, prio); + socket_utils::write_string(logfd, tag); + socket_utils::write_string(logfd, buf); + } + } +} diff --git a/loader/src/common/socket_utils.cpp b/loader/src/common/socket_utils.cpp index d58f915..a889d90 100644 --- a/loader/src/common/socket_utils.cpp +++ b/loader/src/common/socket_utils.cpp @@ -110,6 +110,10 @@ namespace socket_utils { return write_exact(fd, val); } + bool write_string(int fd, std::string_view str) { + return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size()); + } + int recv_fd(int sockfd) { char cmsgbuf[CMSG_SPACE(sizeof(int))]; diff --git a/loader/src/include/daemon.h b/loader/src/include/daemon.h index 3f88677..65b0a81 100644 --- a/loader/src/include/daemon.h +++ b/loader/src/include/daemon.h @@ -52,6 +52,7 @@ namespace zygiskd { enum class SocketAction { PingHeartBeat, + RequestLogcatFd, ReadNativeBridge, ReadModules, RequestCompanionSocket, @@ -60,6 +61,8 @@ namespace zygiskd { bool PingHeartbeat(); + int RequestLogcatFd(); + std::string ReadNativeBridge(); std::vector ReadModules(); diff --git a/loader/src/include/logging.h b/loader/src/include/logging.h index 187c1e8..e0e8046 100644 --- a/loader/src/include/logging.h +++ b/loader/src/include/logging.h @@ -20,15 +20,24 @@ #define LOGE(...) #else #ifndef NDEBUG -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) -#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define LOGD(...) logging::log(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define LOGV(...) logging::log(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #else #define LOGD(...) #define LOGV(...) #endif -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) -#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__) +#define LOGI(...) logging::log(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) logging::log(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#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); + + int getfd(); + + [[gnu::format(printf, 3, 4)]] + void log(int prio, const char* tag, const char* fmt, ...); +} diff --git a/loader/src/include/socket_utils.h b/loader/src/include/socket_utils.h index 3a2bcbc..76f692e 100644 --- a/loader/src/include/socket_utils.h +++ b/loader/src/include/socket_utils.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "logging.h" @@ -21,4 +22,6 @@ namespace socket_utils { int recv_fd(int fd); bool write_usize(int fd, size_t val); + + bool write_string(int fd, std::string_view str); } diff --git a/loader/src/injector/entry.cpp b/loader/src/injector/entry.cpp index 6c83c5f..3da0e2e 100644 --- a/loader/src/injector/entry.cpp +++ b/loader/src/injector/entry.cpp @@ -1,3 +1,4 @@ +#include "daemon.h" #include "logging.h" #include "zygisk.hpp" #include "module.hpp" @@ -17,8 +18,10 @@ static void zygisk_cleanup_wait() { extern "C" [[gnu::visibility("default")]] void entry(void *handle) { - LOGD("Load injector successfully"); + logging::setfd(zygiskd::RequestLogcatFd()); self_handle = handle; + + LOGD("Load injector successfully"); hook_functions(); } diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 7024036..a16781a 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -204,7 +204,17 @@ DCL_HOOK_FUNC(int, unshare, int flags) { return res; } -/* Zygisksu changed: No android_log_close hook */ +// Close logd_fd if necessary to prevent crashing +// For more info, check comments in zygisk_log_write +DCL_HOOK_FUNC(void, android_log_close) { + if (g_ctx == nullptr) { + // Happens during un-managed fork like nativeForkApp, nativeForkUsap + logging::setfd(-1); + } else if (!g_ctx->flags[SKIP_FD_SANITIZATION]) { + logging::setfd(-1); + } + old_android_log_close(); +} // Last point before process secontext changes DCL_HOOK_FUNC(int, selinux_android_setcontext, @@ -595,7 +605,7 @@ void HookContext::app_specialize_post() { // Cleanups env->ReleaseStringUTFChars(args.app->nice_name, process); g_ctx = nullptr; - /* Zygisksu changed: No android_log_close */ + logging::setfd(-1); } void HookContext::unload_zygisk() { @@ -668,7 +678,9 @@ void HookContext::nativeForkAndSpecialize_pre() { flags[APP_FORK_AND_SPECIALIZE] = true; /* Zygisksu changed: No args.app->fds_to_ignore check since we are Android 10+ */ - flags[SKIP_FD_SANITIZATION] = true; + if (logging::getfd() != -1) { + exempted_fds.push_back(logging::getfd()); + } fork_pre(); if (pid == 0) { @@ -729,7 +741,7 @@ void hook_functions() { PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, unshare); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, jniRegisterNativeMethods); PLT_HOOK_REGISTER(android_runtime_dev, android_runtime_inode, selinux_android_setcontext); - /* Zygisksu changed: No android_log_close hook */ + PLT_HOOK_REGISTER_SYM(android_runtime_dev, android_runtime_inode, "__android_log_close", android_log_close); hook_commit(); // Remove unhooked methods diff --git a/loader/src/loader/loader.cpp b/loader/src/loader/loader.cpp index f3cfca7..1e4d5c2 100644 --- a/loader/src/loader/loader.cpp +++ b/loader/src/loader/loader.cpp @@ -42,9 +42,10 @@ void Constructor() { std::string native_bridge; do { - LOGD("Ping heartbeat"); if (!zygiskd::PingHeartbeat()) break; + logging::setfd(zygiskd::RequestLogcatFd()); + LOGI("Read native bridge"); native_bridge = zygiskd::ReadNativeBridge(); @@ -63,32 +64,37 @@ void Constructor() { reinterpret_cast(entry)(handle); } while (false); - if (native_bridge.empty() || native_bridge == "0") return; - LOGI("Load original native bridge: %s", native_bridge.data()); - sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW); - if (sOriginalBridge == nullptr) { - LOGE("dlopen failed: %s", dlerror()); - return; - } + do { + if (native_bridge.empty() || native_bridge == "0") break; - auto* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf"); - if (original_native_bridge_itf == nullptr) { - LOGE("dlsym failed: %s", dlerror()); - return; - } + LOGI("Load original native bridge: %s", native_bridge.data()); + sOriginalBridge = dlopen(native_bridge.data(), RTLD_NOW); + if (sOriginalBridge == nullptr) { + LOGE("dlopen failed: %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* original_native_bridge_itf = dlsym(sOriginalBridge, "NativeBridgeItf"); + if (original_native_bridge_itf == nullptr) { + LOGE("dlsym failed: %s", dlerror()); + break; + } - 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__>); - } + long sdk = 0; + char value[PROP_VALUE_MAX + 1]; + if (__system_property_get("ro.build.version.sdk", value) > 0) { + sdk = strtol(value, nullptr, 10); + } - memcpy(NativeBridgeItf, original_native_bridge_itf, callbacks_size); + 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); } diff --git a/module/build.gradle.kts b/module/build.gradle.kts index 4f3d50f..67652dd 100644 --- a/module/build.gradle.kts +++ b/module/build.gradle.kts @@ -49,7 +49,6 @@ androidComponents.onVariants { variant -> from("$projectDir/src") { include("customize.sh", "daemon.sh") val tokens = mapOf( - "ZYGISK_API" to (verCode / 1000).toString(), "DEBUG" to if (buildTypeLowered == "debug") "true" else "false" ) filter("tokens" to tokens) diff --git a/module/src/customize.sh b/module/src/customize.sh index 9c1f479..c071ed6 100644 --- a/module/src/customize.sh +++ b/module/src/customize.sh @@ -1,8 +1,6 @@ # shellcheck disable=SC2034 SKIPUNZIP=1 -ZYGISK_API="@ZYGISK_API@" - if [ $BOOTMODE ] && [ "$KSU" == "true" ]; then ui_print "- Installing from KernelSU app" else @@ -13,10 +11,16 @@ else fi VERSION=$(grep_prop version "${TMPDIR}/module.prop") -ui_print "- Installing Zygisksu $VERSION (ZYGISK API $ZYGISK_API)" +ui_print "- Installing Zygisksu $VERSION" # check KernelSU -# ui_print "- KernelSU version: $KSU_VER ($KSU_VER_CODE)" +ui_print "- KernelSU version: $KSU_VER ($KSU_VER_CODE)" +if [ "$KSU_VER_CODE" -lt 10200 ]; then + ui_print "*********************************************************" + ui_print "! KernelSU version is too old!" + ui_print "! Please update KernelSU to latest version" + abort "*********************************************************" +fi # check android if [ "$API" -lt 29 ]; then @@ -39,11 +43,20 @@ if [ ! -f "$TMPDIR/verify.sh" ]; then ui_print "*********************************************************" ui_print "! Unable to extract verify.sh!" ui_print "! This zip may be corrupted, please try downloading again" - abort "*********************************************************" + abort "*********************************************************" fi . "$TMPDIR/verify.sh" -extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip" -extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip" +extract "$ZIPFILE" 'customize.sh' "$TMPDIR/.vunzip" +extract "$ZIPFILE" 'verify.sh' "$TMPDIR/.vunzip" +extract "$ZIPFILE" 'sepolicy.rule' "$TMPDIR" + +ui_print "- Checking SELinux patches" +if ! /data/adb/ksud sepolicy check "$TMPDIR/sepolicy.rule"; then + ui_print "*********************************************************" + ui_print "! Unable to apply SELinux patches!" + ui_print "! Your kernel may not support SELinux patch fully" + abort "*********************************************************" +fi ui_print "- Extracting module files" extract "$ZIPFILE" 'daemon.sh' "$MODPATH" diff --git a/zygiskd/src/constants.rs b/zygiskd/src/constants.rs index 1096ca5..49a4b11 100644 --- a/zygiskd/src/constants.rs +++ b/zygiskd/src/constants.rs @@ -15,6 +15,7 @@ pub const PATH_DAEMON_LOCK: &str = concatcp!(PATH_ZYGISKSU_DIR, "/zygiskd.lock") #[repr(u8)] pub enum DaemonSocketAction { PingHeartbeat, + RequestLogcatFd, ReadNativeBridge, ReadModules, RequestCompanionSocket, diff --git a/zygiskd/src/utils.rs b/zygiskd/src/utils.rs index 574ef22..19a4475 100644 --- a/zygiskd/src/utils.rs +++ b/zygiskd/src/utils.rs @@ -39,8 +39,10 @@ pub trait UnixStreamExt { fn read_u8(&mut self) -> Result; fn read_u32(&mut self) -> Result; fn read_usize(&mut self) -> Result; + fn read_string(&mut self) -> Result; fn write_u8(&mut self, value: u8) -> Result<()>; fn write_usize(&mut self, value: usize) -> Result<()>; + fn write_string(&mut self, value: &str) -> Result<()>; } impl UnixStreamExt for UnixStream { @@ -62,6 +64,13 @@ impl UnixStreamExt for UnixStream { Ok(usize::from_ne_bytes(buf)) } + fn read_string(&mut self) -> Result { + let len = self.read_usize()?; + let mut buf = vec![0u8; len]; + self.read_exact(&mut buf)?; + Ok(String::from_utf8(buf)?) + } + fn write_u8(&mut self, value: u8) -> Result<()> { self.write_all(&value.to_ne_bytes())?; Ok(()) @@ -71,6 +80,12 @@ impl UnixStreamExt for UnixStream { self.write_all(&value.to_ne_bytes())?; Ok(()) } + + fn write_string(&mut self, value: &str) -> Result<()> { + self.write_usize(value.len())?; + self.write_all(value.as_bytes())?; + Ok(()) + } } // TODO: Replace with SockAddrExt::from_abstract_name when it's stable diff --git a/zygiskd/src/zygisk.rs b/zygiskd/src/zygisk.rs index 8e2d5c2..e47c7c7 100644 --- a/zygiskd/src/zygisk.rs +++ b/zygiskd/src/zygisk.rs @@ -8,10 +8,9 @@ use nix::{ unistd::getppid, }; use passfd::FdPassingExt; -use std::io::Write; use std::sync::Arc; use std::thread; -use std::ffi::c_void; +use std::ffi::{c_char, c_void}; use std::fs; use std::os::unix::{ net::{UnixListener, UnixStream}, @@ -187,15 +186,28 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()> DaemonSocketAction::PingHeartbeat => { restore_native_bridge()?; } + DaemonSocketAction::RequestLogcatFd => { + loop { + let level = match stream.read_u8() { + Ok(level) => level, + Err(_) => break, + }; + let tag = stream.read_string()?; + let tag = std::ffi::CString::new(tag)?; + let message = stream.read_string()?; + let message = std::ffi::CString::new(message)?; + unsafe { + __android_log_print(level as i32, tag.as_ptr(), message.as_ptr()); + } + } + } DaemonSocketAction::ReadNativeBridge => { - stream.write_usize(context.native_bridge.len())?; - stream.write_all(context.native_bridge.as_bytes())?; + stream.write_string(&context.native_bridge)?; } DaemonSocketAction::ReadModules => { stream.write_usize(context.modules.len())?; for module in context.modules.iter() { - stream.write_usize(module.name.len())?; - stream.write_all(module.name.as_bytes())?; + stream.write_string(&module.name)?; stream.send_fd(module.memfd.as_raw_fd())?; } } @@ -225,3 +237,7 @@ fn handle_daemon_action(mut stream: UnixStream, context: &Context) -> Result<()> } Ok(()) } + +extern "C" { + fn __android_log_print(prio: i32, tag: *const c_char, fmt: *const c_char, ...) -> i32; +}