Commit 171d68ca authored by topjohnwu's avatar topjohnwu

Connect to magiskd log daemon

parent bade4f2c
...@@ -10,6 +10,7 @@ void unlock_blocks(); ...@@ -10,6 +10,7 @@ void unlock_blocks();
void reboot(); void reboot();
void start_log_daemon(); void start_log_daemon();
void setup_logfile(bool reset); void setup_logfile(bool reset);
void magisk_logging();
// Module stuffs // Module stuffs
void handle_modules(); void handle_modules();
......
...@@ -17,7 +17,7 @@ struct log_meta { ...@@ -17,7 +17,7 @@ struct log_meta {
int tid; int tid;
}; };
static atomic<int> logd_fd = -1; atomic<int> logd_fd = -1;
void setup_logfile(bool reset) { void setup_logfile(bool reset) {
if (logd_fd < 0) if (logd_fd < 0)
...@@ -121,7 +121,7 @@ static void logfile_writer(int pipefd) { ...@@ -121,7 +121,7 @@ static void logfile_writer(int pipefd) {
} }
} }
static int magisk_log(int prio, const char *fmt, va_list ap) { int magisk_log(int prio, const char *fmt, va_list ap) {
char buf[MAX_MSG_LEN + 1]; char buf[MAX_MSG_LEN + 1];
int len = vsnprintf(buf, sizeof(buf), fmt, ap); int len = vsnprintf(buf, sizeof(buf), fmt, ap);
...@@ -172,6 +172,15 @@ void magisk_logging() { ...@@ -172,6 +172,15 @@ void magisk_logging() {
log_cb.ex = nop_ex; log_cb.ex = nop_ex;
} }
#define alog(prio) [](auto fmt, auto ap){ return __android_log_vprint(ANDROID_LOG_##prio, "Magisk", fmt, ap); }
void android_logging() {
log_cb.d = alog(DEBUG);
log_cb.i = alog(INFO);
log_cb.w = alog(WARN);
log_cb.e = alog(ERROR);
log_cb.ex = nop_ex;
}
void start_log_daemon() { void start_log_daemon() {
int fds[2]; int fds[2];
if (pipe2(fds, O_CLOEXEC) == 0) { if (pipe2(fds, O_CLOEXEC) == 0) {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <pthread.h> #include <pthread.h>
#include <string> #include <string>
#include <limits> #include <limits>
#include <atomic>
#include <socket.hpp> #include <socket.hpp>
...@@ -48,7 +49,9 @@ enum : int { ...@@ -48,7 +49,9 @@ enum : int {
int connect_daemon(bool create = false); int connect_daemon(bool create = false);
void magisk_logging(); extern std::atomic<int> logd_fd;
int magisk_log(int prio, const char *fmt, va_list ap);
void android_logging();
// Daemon handlers // Daemon handlers
void post_fs_data(int client); void post_fs_data(int client);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define linkat compat_linkat #define linkat compat_linkat
#define inotify_init1 compat_inotify_init1 #define inotify_init1 compat_inotify_init1
#define faccessat compat_faccessat #define faccessat compat_faccessat
#define sigtimedwait compat_sigtimedwait
ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream); ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream);
ssize_t compat_getdelim(char **lineptr, size_t *n, int delim, FILE *stream); ssize_t compat_getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
...@@ -66,3 +67,12 @@ static inline int compat_inotify_init1(int flags) { ...@@ -66,3 +67,12 @@ static inline int compat_inotify_init1(int flags) {
static inline int compat_faccessat(int dirfd, const char *pathname, int mode, int flags) { static inline int compat_faccessat(int dirfd, const char *pathname, int mode, int flags) {
return syscall(__NR_faccessat, dirfd, pathname, mode, flags); return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
} }
static inline int compat_sigtimedwait(const sigset_t* set, siginfo_t* info, const timespec* timeout) {
union {
sigset_t set;
sigset_t set64;
} s{};
s.set = *set;
return syscall(__NR_rt_sigtimedwait, &s.set64, info, timeout, sizeof(sigset64_t));
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <android/log.h>
#include <utils.hpp> #include <utils.hpp>
#include <daemon.hpp> #include <daemon.hpp>
...@@ -16,6 +17,17 @@ using namespace std; ...@@ -16,6 +17,17 @@ using namespace std;
static void *self_handle = nullptr; static void *self_handle = nullptr;
static atomic<int> active_threads = -1; static atomic<int> active_threads = -1;
static int zygisk_log(int prio, const char *fmt, va_list ap);
#define zlog(prio) [](auto fmt, auto ap){ return zygisk_log(ANDROID_LOG_##prio, fmt, ap); }
static void zygisk_logging() {
log_cb.d = zlog(DEBUG);
log_cb.i = zlog(INFO);
log_cb.w = zlog(WARN);
log_cb.e = zlog(ERROR);
log_cb.ex = nop_ex;
}
void self_unload() { void self_unload() {
LOGD("zygisk: Request to self unload\n"); LOGD("zygisk: Request to self unload\n");
// If deny failed, do not unload or else it will cause SIGSEGV // If deny failed, do not unload or else it will cause SIGSEGV
...@@ -81,7 +93,7 @@ static void inject_cleanup_wait() { ...@@ -81,7 +93,7 @@ static void inject_cleanup_wait() {
__attribute__((constructor)) __attribute__((constructor))
static void inject_init() { static void inject_init() {
if (char *env = getenv(INJECT_ENV_2)) { if (char *env = getenv(INJECT_ENV_2)) {
magisk_logging(); zygisk_logging();
LOGD("zygisk: inject 2nd stage\n"); LOGD("zygisk: inject 2nd stage\n");
active_threads = 1; active_threads = 1;
unsetenv(INJECT_ENV_2); unsetenv(INJECT_ENV_2);
...@@ -100,7 +112,7 @@ static void inject_init() { ...@@ -100,7 +112,7 @@ static void inject_init() {
active_threads++; active_threads++;
new_daemon_thread(&unload_first_stage, env); new_daemon_thread(&unload_first_stage, env);
} else if (getenv(INJECT_ENV_1)) { } else if (getenv(INJECT_ENV_1)) {
magisk_logging(); android_logging();
LOGD("zygisk: inject 1st stage\n"); LOGD("zygisk: inject 1st stage\n");
char *ld = getenv("LD_PRELOAD"); char *ld = getenv("LD_PRELOAD");
...@@ -128,7 +140,7 @@ static void inject_init() { ...@@ -128,7 +140,7 @@ static void inject_init() {
// Start code for magiskd IPC // Start code for magiskd IPC
int app_process_main(int argc, char *argv[]) { int app_process_main(int argc, char *argv[]) {
magisk_logging(); android_logging();
if (int fd = connect_daemon(); fd >= 0) { if (int fd = connect_daemon(); fd >= 0) {
write_int(fd, ZYGISK_REQUEST); write_int(fd, ZYGISK_REQUEST);
...@@ -157,6 +169,42 @@ int app_process_main(int argc, char *argv[]) { ...@@ -157,6 +169,42 @@ int app_process_main(int argc, char *argv[]) {
return 1; return 1;
} }
static int zygisk_log(int prio, const char *fmt, va_list ap) {
// If we don't have log pipe set, ask magiskd for it
// This could happen multiple times in zygote because it was closed to prevent crashing
if (logd_fd < 0) {
// Change logging temporarily to prevent infinite recursion and stack overflow
android_logging();
if (int fd = connect_daemon(); fd >= 0) {
write_int(fd, ZYGISK_REQUEST);
write_int(fd, ZYGISK_GET_LOG_PIPE);
if (read_int(fd) == 0) {
logd_fd = recv_fd(fd);
}
close(fd);
}
zygisk_logging();
}
sigset_t mask;
sigset_t orig_mask;
bool sig = false;
// Make sure SIGPIPE won't crash zygote
if (logd_fd >= 0) {
sig = true;
sigemptyset(&mask);
sigaddset(&mask, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &mask, &orig_mask);
}
int ret = magisk_log(prio, fmt, ap);
if (sig) {
timespec ts{};
sigtimedwait(&mask, nullptr, &ts);
pthread_sigmask(SIG_SETMASK, &orig_mask, nullptr);
}
return ret;
}
bool remote_check_denylist(int uid, const char *process) { bool remote_check_denylist(int uid, const char *process) {
if (int fd = connect_daemon(); fd >= 0) { if (int fd = connect_daemon(); fd >= 0) {
write_int(fd, ZYGISK_REQUEST); write_int(fd, ZYGISK_REQUEST);
...@@ -226,6 +274,16 @@ static void do_unmount(int client, ucred *cred) { ...@@ -226,6 +274,16 @@ static void do_unmount(int client, ucred *cred) {
} }
} }
static void send_log_pipe(int fd) {
// There is race condition here, but we can't really do much about it...
if (logd_fd) {
write_int(fd, 0);
send_fd(fd, logd_fd);
} else {
write_int(fd, 1);
}
}
void zygisk_handler(int client, ucred *cred) { void zygisk_handler(int client, ucred *cred) {
int code = read_int(client); int code = read_int(client);
switch (code) { switch (code) {
...@@ -238,6 +296,9 @@ void zygisk_handler(int client, ucred *cred) { ...@@ -238,6 +296,9 @@ void zygisk_handler(int client, ucred *cred) {
case ZYGISK_UNMOUNT: case ZYGISK_UNMOUNT:
do_unmount(client, cred); do_unmount(client, cred);
break; break;
case ZYGISK_GET_LOG_PIPE:
send_log_pipe(client);
break;
} }
close(client); close(client);
} }
...@@ -24,10 +24,12 @@ namespace { ...@@ -24,10 +24,12 @@ namespace {
enum { enum {
DENY_FLAG, DENY_FLAG,
FORK_AND_SPECIALIZE, FORK_AND_SPECIALIZE,
APP_SPECIALIZE,
SERVER_SPECIALIZE,
FLAG_MAX FLAG_MAX
}; };
#define DCL_JNI_FUNC(name) \ #define DCL_PRE_POST(name) \
void name##_pre(); \ void name##_pre(); \
void name##_post(); void name##_post();
...@@ -44,15 +46,17 @@ struct HookContext { ...@@ -44,15 +46,17 @@ struct HookContext {
HookContext() : pid(-1) {} HookContext() : pid(-1) {}
void pre_fork(); static void close_fds();
static void post_fork();
DCL_JNI_FUNC(nativeForkAndSpecialize) DCL_PRE_POST(fork)
DCL_JNI_FUNC(nativeSpecializeAppProcess) DCL_PRE_POST(run_modules)
DCL_JNI_FUNC(nativeForkSystemServer)
DCL_PRE_POST(nativeForkAndSpecialize)
DCL_PRE_POST(nativeSpecializeAppProcess)
DCL_PRE_POST(nativeForkSystemServer)
}; };
#undef DCL_JNI_FUNC #undef DCL_PRE_POST
struct StringCmp { struct StringCmp {
using is_transparent = void; using is_transparent = void;
...@@ -127,15 +131,15 @@ DCL_HOOK_FUNC(int, jniRegisterNativeMethods, ...@@ -127,15 +131,15 @@ DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods); return old_jniRegisterNativeMethods(env, className, newMethods.get() ?: methods, numMethods);
} }
// Skip actual fork and return cached result if applicable
DCL_HOOK_FUNC(int, fork) { DCL_HOOK_FUNC(int, fork) {
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork(); return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
} }
// This is the latest point where we can still connect to the magiskd main socket
DCL_HOOK_FUNC(int, selinux_android_setcontext, DCL_HOOK_FUNC(int, selinux_android_setcontext,
uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) { uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) {
if (g_ctx && g_ctx->flags[DENY_FLAG]) { if (g_ctx && g_ctx->flags[DENY_FLAG]) {
// Request magiskd to cleanup our mount namespace before switching secontext
// This is the latest point where we can still connect to the magiskd main socket
if (remote_request_unmount() == 0) { if (remote_request_unmount() == 0) {
LOGD("zygisk: mount namespace cleaned up\n"); LOGD("zygisk: mount namespace cleaned up\n");
} }
...@@ -143,6 +147,16 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext, ...@@ -143,6 +147,16 @@ DCL_HOOK_FUNC(int, selinux_android_setcontext,
return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname); return old_selinux_android_setcontext(uid, isSystemServer, seinfo, pkgname);
} }
// A place to clean things up before calling into zygote::ForkCommon/SpecializeCommon
DCL_HOOK_FUNC(void, android_log_close) {
HookContext::close_fds();
if (g_ctx && g_ctx->pid <= 0) {
// In child process, no longer be able to access to magiskd
android_logging();
}
old_android_log_close();
}
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// The original android::AppRuntime virtual table // The original android::AppRuntime virtual table
...@@ -208,8 +222,18 @@ DCL_HOOK_FUNC(void, setArgv0, void *self, const char *argv0, bool setProcName) { ...@@ -208,8 +222,18 @@ DCL_HOOK_FUNC(void, setArgv0, void *self, const char *argv0, bool setProcName) {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
void HookContext::run_modules_pre() { /* TODO */ }
void HookContext::run_modules_post() { /* TODO */ }
void HookContext::close_fds() {
close(logd_fd.exchange(-1));
}
// -----------------------------------------------------------------
void HookContext::nativeSpecializeAppProcess_pre() { void HookContext::nativeSpecializeAppProcess_pre() {
g_ctx = this; g_ctx = this;
flags[APP_SPECIALIZE] = true;
process = env->GetStringUTFChars(args->nice_name, nullptr); process = env->GetStringUTFChars(args->nice_name, nullptr);
if (flags[FORK_AND_SPECIALIZE]) { if (flags[FORK_AND_SPECIALIZE]) {
VLOG("zygisk: pre forkAndSpecialize [%s]\n", process); VLOG("zygisk: pre forkAndSpecialize [%s]\n", process);
...@@ -221,6 +245,8 @@ void HookContext::nativeSpecializeAppProcess_pre() { ...@@ -221,6 +245,8 @@ void HookContext::nativeSpecializeAppProcess_pre() {
if (args->mount_external != 0 && remote_check_denylist(args->uid, process)) { if (args->mount_external != 0 && remote_check_denylist(args->uid, process)) {
flags[DENY_FLAG] = true; flags[DENY_FLAG] = true;
LOGI("zygisk: [%s] is on the denylist\n", process); LOGI("zygisk: [%s] is on the denylist\n", process);
} else {
run_modules_pre();
} }
} }
...@@ -232,24 +258,49 @@ void HookContext::nativeSpecializeAppProcess_post() { ...@@ -232,24 +258,49 @@ void HookContext::nativeSpecializeAppProcess_post() {
} }
env->ReleaseStringUTFChars(args->nice_name, process); env->ReleaseStringUTFChars(args->nice_name, process);
if (flags[DENY_FLAG]) if (flags[DENY_FLAG]) {
self_unload(); self_unload();
} else {
run_modules_post();
}
g_ctx = nullptr; g_ctx = nullptr;
} }
void HookContext::nativeForkSystemServer_pre() { void HookContext::nativeForkSystemServer_pre() {
pre_fork(); fork_pre();
if (pid) return; flags[SERVER_SPECIALIZE] = true;
if (pid == 0) {
VLOG("zygisk: pre forkSystemServer\n"); VLOG("zygisk: pre forkSystemServer\n");
run_modules_pre();
close_fds();
}
} }
void HookContext::nativeForkSystemServer_post() { void HookContext::nativeForkSystemServer_post() {
run_finally f([]{ post_fork(); }); if (pid == 0) {
if (pid) return; android_logging();
VLOG("zygisk: post forkSystemServer\n"); VLOG("zygisk: post forkSystemServer\n");
run_modules_post();
}
fork_post();
} }
// ----------------------------------------------------------------- void HookContext::nativeForkAndSpecialize_pre() {
fork_pre();
flags[FORK_AND_SPECIALIZE] = true;
if (pid == 0) {
nativeSpecializeAppProcess_pre();
close_fds();
}
}
void HookContext::nativeForkAndSpecialize_post() {
if (pid == 0) {
android_logging();
nativeSpecializeAppProcess_post();
}
fork_post();
}
int sigmask(int how, int signum) { int sigmask(int how, int signum) {
sigset_t set; sigset_t set;
...@@ -260,31 +311,18 @@ int sigmask(int how, int signum) { ...@@ -260,31 +311,18 @@ int sigmask(int how, int signum) {
// Do our own fork before loading any 3rd party code // Do our own fork before loading any 3rd party code
// First block SIGCHLD, unblock after original fork is done // First block SIGCHLD, unblock after original fork is done
void HookContext::pre_fork() { void HookContext::fork_pre() {
g_ctx = this; g_ctx = this;
sigmask(SIG_BLOCK, SIGCHLD); sigmask(SIG_BLOCK, SIGCHLD);
pid = old_fork(); pid = old_fork();
} }
// Unblock SIGCHLD in case the original method didn't // Unblock SIGCHLD in case the original method didn't
void HookContext::post_fork() { void HookContext::fork_post() {
sigmask(SIG_UNBLOCK, SIGCHLD); sigmask(SIG_UNBLOCK, SIGCHLD);
g_ctx = nullptr; g_ctx = nullptr;
} }
void HookContext::nativeForkAndSpecialize_pre() {
pre_fork();
flags[FORK_AND_SPECIALIZE] = true;
if (pid == 0)
nativeSpecializeAppProcess_pre();
}
void HookContext::nativeForkAndSpecialize_post() {
if (pid == 0)
nativeSpecializeAppProcess_post();
post_fork();
}
} // namespace } // namespace
static bool hook_refresh() { static bool hook_refresh() {
...@@ -329,6 +367,7 @@ void hook_functions() { ...@@ -329,6 +367,7 @@ void hook_functions() {
XHOOK_REGISTER(ANDROID_RUNTIME, fork); XHOOK_REGISTER(ANDROID_RUNTIME, fork);
XHOOK_REGISTER(ANDROID_RUNTIME, selinux_android_setcontext); XHOOK_REGISTER(ANDROID_RUNTIME, selinux_android_setcontext);
XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods); XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods);
XHOOK_REGISTER_SYM(ANDROID_RUNTIME, "__android_log_close", android_log_close);
hook_refresh(); hook_refresh();
// Remove unhooked methods // Remove unhooked methods
......
...@@ -10,6 +10,7 @@ enum : int { ...@@ -10,6 +10,7 @@ enum : int {
ZYGISK_SETUP, ZYGISK_SETUP,
ZYGISK_CHECK_DENYLIST, ZYGISK_CHECK_DENYLIST,
ZYGISK_UNMOUNT, ZYGISK_UNMOUNT,
ZYGISK_GET_LOG_PIPE,
}; };
// Unmap all pages matching the name // Unmap all pages matching the name
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment