You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
improve: port ptracer to C
This commit ports the C++ to C99 from zygisk-ptracer code, allowing a ~3x size reduce in its binary.
This commit is contained in:
@@ -26,6 +26,8 @@ val ccachePath by lazy {
|
||||
}
|
||||
|
||||
val defaultCFlags = arrayOf(
|
||||
"-D_GNU_SOURCE",
|
||||
|
||||
"-Wall", "-Wextra",
|
||||
"-fno-rtti", "-fno-exceptions",
|
||||
"-fno-stack-protector", "-fomit-frame-pointer",
|
||||
|
||||
@@ -10,7 +10,7 @@ add_definitions(-DZKSU_VERSION=\"${ZKSU_VERSION}\")
|
||||
aux_source_directory(common COMMON_SRC_LIST)
|
||||
add_library(common STATIC ${COMMON_SRC_LIST})
|
||||
target_include_directories(common PRIVATE include)
|
||||
target_link_libraries(common cxx::cxx log)
|
||||
target_link_libraries(common log)
|
||||
|
||||
aux_source_directory(injector INJECTOR_SRC_LIST)
|
||||
add_library(zygisk SHARED ${INJECTOR_SRC_LIST})
|
||||
@@ -20,6 +20,5 @@ target_link_libraries(zygisk cxx::cxx log common lsplt_static phmap)
|
||||
aux_source_directory(ptracer PTRACER_SRC_LIST)
|
||||
add_executable(libzygisk_ptrace.so ${PTRACER_SRC_LIST})
|
||||
target_include_directories(libzygisk_ptrace.so PRIVATE include)
|
||||
target_link_libraries(libzygisk_ptrace.so cxx::cxx log common)
|
||||
|
||||
target_link_libraries(libzygisk_ptrace.so log common)
|
||||
add_subdirectory(external)
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
// #include <unistd.h>
|
||||
// #include <sys/types.h>
|
||||
// #include <sys/stat.h>
|
||||
// #include <dirent.h>
|
||||
// #include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
@@ -15,19 +10,6 @@
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
char daemon_path[PATH_MAX];
|
||||
|
||||
void rezygiskd_init(const char *path) {
|
||||
snprintf(daemon_path, sizeof(daemon_path), "%s/%s", path, SOCKET_FILE_NAME);
|
||||
}
|
||||
|
||||
void rezygiskd_get_path(char *buf, size_t buf_size) {
|
||||
size_t fileless_daemon_path = strlen(daemon_path) - strlen("/") - strlen(SOCKET_FILE_NAME);
|
||||
|
||||
strncpy(buf, daemon_path, buf_size > fileless_daemon_path ? fileless_daemon_path : buf_size);
|
||||
buf[fileless_daemon_path] = '\0';
|
||||
}
|
||||
|
||||
int rezygiskd_connect(uint8_t retry) {
|
||||
retry++;
|
||||
|
||||
@@ -49,7 +31,7 @@ int rezygiskd_connect(uint8_t retry) {
|
||||
Sources:
|
||||
- https://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/un.h.html
|
||||
*/
|
||||
strcpy(addr.sun_path, daemon_path);
|
||||
strcpy(addr.sun_path, TMP_PATH "/" SOCKET_FILE_NAME);
|
||||
socklen_t socklen = sizeof(addr);
|
||||
|
||||
while (--retry) {
|
||||
@@ -343,15 +325,18 @@ bool rezygiskd_update_mns(enum mount_namespace_state nms_state, char *buf, size_
|
||||
write_uint8_t(fd, (uint8_t)nms_state);
|
||||
|
||||
uint32_t target_pid = 0;
|
||||
read_uint32_t(fd, &target_pid);
|
||||
if (target_pid == 0) {
|
||||
if (read_uint32_t(fd, &target_pid) < 0) {
|
||||
PLOGE("Failed to read target pid");
|
||||
|
||||
close(fd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int target_fd = read_fd(fd);
|
||||
if (target_fd == -1) {
|
||||
uint32_t target_fd = 0;
|
||||
if (read_uint32_t(fd, &target_fd) < 0) {
|
||||
PLOGE("Failed to read target fd");
|
||||
|
||||
close(fd);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* INFO: This file is written in C99 */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
@@ -51,9 +54,11 @@ enum mount_namespace_state {
|
||||
Module
|
||||
};
|
||||
|
||||
void rezygiskd_init(const char *path);
|
||||
#define TMP_PATH "/data/adb/rezygisk"
|
||||
|
||||
void rezygiskd_get_path(char *buf, size_t buf_size);
|
||||
static inline const char *rezygiskd_get_path() {
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
int rezygiskd_connect(uint8_t retry);
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <cstdint>
|
||||
|
||||
template<unsigned>
|
||||
struct NativeBridgeCallbacks;
|
||||
|
||||
template<>
|
||||
struct NativeBridgeCallbacks<__ANDROID_API_Q__> {
|
||||
[[maybe_unused]] uint32_t version;
|
||||
[[maybe_unused]] void *initialize;
|
||||
[[maybe_unused]] void *loadLibrary;
|
||||
[[maybe_unused]] void *getTrampoline;
|
||||
[[maybe_unused]] void *isSupported;
|
||||
[[maybe_unused]] void *getAppEnv;
|
||||
[[maybe_unused]] void *isCompatibleWith;
|
||||
[[maybe_unused]] void *getSignalHandler;
|
||||
[[maybe_unused]] void *unloadLibrary;
|
||||
[[maybe_unused]] void *getError;
|
||||
[[maybe_unused]] void *isPathSupported;
|
||||
[[maybe_unused]] void *initAnonymousNamespace;
|
||||
[[maybe_unused]] void *createNamespace;
|
||||
[[maybe_unused]] void *linkNamespaces;
|
||||
[[maybe_unused]] void *loadLibraryExt;
|
||||
[[maybe_unused]] void *getVendorNamespace;
|
||||
[[maybe_unused]] void *getExportedNamespace;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct NativeBridgeCallbacks<__ANDROID_API_R__> : NativeBridgeCallbacks<__ANDROID_API_Q__> {
|
||||
[[maybe_unused]] void *preZygoteFork;
|
||||
};
|
||||
@@ -10,9 +10,9 @@ size_t block_size = 0;
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void* addr, size_t size, const char* path) {
|
||||
LOGD("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
|
||||
start_addr = addr;
|
||||
block_size = size;
|
||||
rezygiskd_init(path);
|
||||
|
||||
if (!rezygiskd_ping()) {
|
||||
LOGE("Zygisk daemon is not running");
|
||||
|
||||
@@ -181,8 +181,6 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
|
||||
update_mnt_ns(Rooted, false);
|
||||
} else if (!(g_ctx->flags[DO_REVERT_UNMOUNT])) {
|
||||
update_mnt_ns(Module, false);
|
||||
} else {
|
||||
LOGI("Process [%s] is on denylist, skipping unmount", g_ctx->process);
|
||||
}
|
||||
|
||||
old_unshare(CLONE_NEWNS);
|
||||
@@ -636,6 +634,7 @@ void ZygiskContext::run_modules_pre() {
|
||||
|
||||
for (auto &m : modules) {
|
||||
m.onLoad(env);
|
||||
|
||||
if (flags[APP_SPECIALIZE]) m.preAppSpecialize(args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.preServerSpecialize(args.server);
|
||||
}
|
||||
@@ -674,6 +673,12 @@ void ZygiskContext::app_specialize_pre() {
|
||||
if ((info_flags & (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) == (PROCESS_IS_MANAGER | PROCESS_ROOT_IS_MAGISK)) {
|
||||
LOGD("Manager process detected. Notifying that Zygisk has been enabled.");
|
||||
|
||||
/* INFO: This environment variable is related to Magisk Zygisk/Manager. It
|
||||
it used by Magisk's Zygisk to communicate to Magisk Manager whether
|
||||
Zygisk is working or not.
|
||||
|
||||
To allow Zygisk modules to both work properly and for the manager to
|
||||
identify Zygisk, being it not built-in, as working, we also set it. */
|
||||
setenv("ZYGISK_ENABLED", "1", 1);
|
||||
} else {
|
||||
run_modules_pre();
|
||||
@@ -818,7 +823,7 @@ void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps)
|
||||
|
||||
if (load > 0 || unload > 0) solist_reset_counters(load, unload);
|
||||
|
||||
LOGI("Dropping solist record for %s", path);
|
||||
LOGD("Dropping solist record for %s", path);
|
||||
|
||||
bool path_found = solist_drop_so_path(path);
|
||||
if (!path_found || !spoof_maps) return;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* INFO: This file is written in C99 */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
#define LOG_TAG "zygisk-ptrace64"
|
||||
#else
|
||||
#define LOG_TAG "zygisk-ptrace32"
|
||||
#endif
|
||||
|
||||
#include "monitor.h"
|
||||
#include "utils.hpp"
|
||||
#include "utils.h"
|
||||
#include "daemon.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
rezygiskd_init("/data/adb/rezygisk");
|
||||
|
||||
printf("The ReZygisk Tracer %s\n\n", ZKSU_VERSION);
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "monitor") == 0) {
|
||||
init_monitor();
|
||||
|
||||
printf("[ReZygisk]: Started monitoring...\n");
|
||||
|
||||
return 0;
|
||||
} else if (argc >= 3 && strcmp(argv[1], "trace") == 0) {
|
||||
if (argc >= 4 && strcmp(argv[3], "--restart") == 0) rezygiskd_zygote_restart();
|
||||
@@ -25,11 +28,9 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[ReZygisk]: Tracing %ld...\n", pid);
|
||||
|
||||
return 0;
|
||||
} else if (argc >= 2 && strcmp(argv[1], "ctl") == 0) {
|
||||
enum Command command;
|
||||
enum rezygiskd_command command;
|
||||
|
||||
if (strcmp(argv[2], "start") == 0) command = START;
|
||||
else if (strcmp(argv[2], "stop") == 0) command = STOP;
|
||||
892
loader/src/ptracer/monitor.c
Normal file
892
loader/src/ptracer/monitor.c
Normal file
@@ -0,0 +1,892 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <err.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "utils.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "monitor.h"
|
||||
|
||||
#define PROP_PATH TMP_PATH "/module.prop"
|
||||
#define SOCKET_NAME "init_monitor"
|
||||
|
||||
#define STOPPED_WITH(sig, event) WIFSTOPPED(sigchld_status) && (sigchld_status >> 8 == ((sig) | (event << 8)))
|
||||
|
||||
static bool update_status(const char *message);
|
||||
|
||||
char monitor_stop_reason[32];
|
||||
|
||||
enum ptracer_tracing_state {
|
||||
TRACING,
|
||||
STOPPING,
|
||||
STOPPED,
|
||||
EXITING
|
||||
};
|
||||
|
||||
enum ptracer_tracing_state tracing_state = TRACING;
|
||||
|
||||
struct rezygiskd_status {
|
||||
bool supported;
|
||||
bool zygote_injected;
|
||||
bool daemon_running;
|
||||
pid_t daemon_pid;
|
||||
char *daemon_info;
|
||||
char *daemon_error_info;
|
||||
};
|
||||
|
||||
struct rezygiskd_status status64 = {
|
||||
.daemon_pid = -1
|
||||
};
|
||||
struct rezygiskd_status status32 = {
|
||||
.daemon_pid = -1
|
||||
};
|
||||
|
||||
int monitor_epoll_fd;
|
||||
bool monitor_events_running = true;
|
||||
|
||||
bool monitor_events_init() {
|
||||
monitor_epoll_fd = epoll_create(1);
|
||||
if (monitor_epoll_fd == -1) {
|
||||
PLOGE("epoll_create");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct monitor_event_cbs {
|
||||
void (*callback)();
|
||||
void (*stop_callback)();
|
||||
};
|
||||
|
||||
bool monitor_events_register_event(struct monitor_event_cbs *event_cbs, int fd, uint32_t events) {
|
||||
struct epoll_event ev = {
|
||||
.data.ptr = event_cbs,
|
||||
.events = events
|
||||
};
|
||||
|
||||
if (epoll_ctl(monitor_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
PLOGE("epoll_ctl");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool monitor_events_unregister_event(int fd) {
|
||||
if (epoll_ctl(monitor_epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
|
||||
PLOGE("epoll_ctl");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void monitor_events_stop() {
|
||||
monitor_events_running = false;
|
||||
};
|
||||
|
||||
void monitor_events_loop() {
|
||||
struct epoll_event events[2];
|
||||
while (monitor_events_running) {
|
||||
int nfds = epoll_wait(monitor_epoll_fd, events, 2, -1);
|
||||
if (nfds == -1) {
|
||||
if (errno != EINTR) PLOGE("epoll_wait");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr;
|
||||
event_cbs->callback();
|
||||
|
||||
if (!monitor_events_running) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor_epoll_fd >= 0) close(monitor_epoll_fd);
|
||||
monitor_epoll_fd = -1;
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(events) / sizeof(events[0])); i++) {
|
||||
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr;
|
||||
event_cbs->stop_callback();
|
||||
}
|
||||
}
|
||||
|
||||
int monitor_sock_fd;
|
||||
|
||||
bool rezygiskd_listener_init() {
|
||||
monitor_sock_fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||
if (monitor_sock_fd == -1) {
|
||||
PLOGE("socket create");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = { 0 }
|
||||
};
|
||||
|
||||
size_t sun_path_len = sprintf(addr.sun_path, "%s/%s", rezygiskd_get_path(), SOCKET_NAME);
|
||||
|
||||
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||
if (bind(monitor_sock_fd, (struct sockaddr *)&addr, socklen) == -1) {
|
||||
PLOGE("bind socket");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rezygiskd_listener_callback() {
|
||||
struct [[gnu::packed]] MsgHead {
|
||||
enum rezygiskd_command cmd;
|
||||
int length;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
while (1) {
|
||||
struct MsgHead *msg = (struct MsgHead *)malloc(sizeof(struct MsgHead));
|
||||
|
||||
ssize_t real_size;
|
||||
ssize_t nread = recv(monitor_sock_fd, msg, sizeof(struct MsgHead), MSG_PEEK);
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("read socket");
|
||||
}
|
||||
|
||||
if ((size_t)nread < sizeof(enum rezygiskd_command)) {
|
||||
LOGE("read %zu < %zu", nread, sizeof(enum rezygiskd_command));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg->cmd >= DAEMON64_SET_INFO && msg->cmd != SYSTEM_SERVER_STARTED) {
|
||||
if (nread != sizeof(msg)) {
|
||||
LOGE("cmd %d size %zu != %zu", msg->cmd, nread, sizeof(struct MsgHead));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
real_size = sizeof(struct MsgHead) + msg->length;
|
||||
} else {
|
||||
if (nread != sizeof(enum rezygiskd_command)) {
|
||||
LOGE("cmd %d size %zu != %zu", msg->cmd, nread, sizeof(enum rezygiskd_command));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
real_size = sizeof(enum rezygiskd_command);
|
||||
}
|
||||
|
||||
msg = (struct MsgHead *)realloc(msg, real_size);
|
||||
nread = recv(monitor_sock_fd, msg, real_size, 0);
|
||||
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("recv");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nread != real_size) {
|
||||
LOGE("real size %zu != %zu", real_size, nread);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (msg->cmd) {
|
||||
case START: {
|
||||
if (tracing_state == STOPPING) tracing_state = TRACING;
|
||||
else if (tracing_state == STOPPED) {
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
|
||||
LOGI("start tracing init");
|
||||
|
||||
tracing_state = TRACING;
|
||||
}
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (tracing_state == TRACING) {
|
||||
LOGI("stop tracing requested");
|
||||
|
||||
tracing_state = STOPPING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
|
||||
update_status(NULL);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EXIT: {
|
||||
LOGI("prepare for exit ...");
|
||||
|
||||
tracing_state = EXITING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
|
||||
update_status(NULL);
|
||||
monitor_events_stop();
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE64_INJECTED: {
|
||||
status64.zygote_injected = true;
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE32_INJECTED: {
|
||||
status32.zygote_injected = true;
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_INFO: {
|
||||
LOGD("received daemon64 info %s", msg->data);
|
||||
|
||||
/* Will only happen if somehow the daemon restarts */
|
||||
if (status64.daemon_info) {
|
||||
free(status64.daemon_info);
|
||||
status64.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_info = (char *)malloc(msg->length);
|
||||
if (!status64.daemon_info) {
|
||||
PLOGE("malloc daemon64 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_info, msg->data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON32_SET_INFO: {
|
||||
LOGD("received daemon32 info %s", msg->data);
|
||||
|
||||
if (status32.daemon_info) {
|
||||
free(status32.daemon_info);
|
||||
status32.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_info = (char *)malloc(msg->length);
|
||||
if (!status32.daemon_info) {
|
||||
PLOGE("malloc daemon32 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_info, msg->data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_ERROR_INFO: {
|
||||
LOGD("received daemon64 error info %s", msg->data);
|
||||
|
||||
status64.daemon_running = false;
|
||||
|
||||
if (status64.daemon_error_info) {
|
||||
free(status64.daemon_error_info);
|
||||
status64.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_error_info = (char *)malloc(msg->length);
|
||||
if (!status64.daemon_error_info) {
|
||||
PLOGE("malloc daemon64 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_error_info, msg->data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON32_SET_ERROR_INFO: {
|
||||
LOGD("received daemon32 error info %s", msg->data);
|
||||
|
||||
status32.daemon_running = false;
|
||||
|
||||
if (status32.daemon_error_info) {
|
||||
free(status32.daemon_error_info);
|
||||
status32.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_error_info = (char *)malloc(msg->length);
|
||||
if (!status32.daemon_error_info) {
|
||||
PLOGE("malloc daemon32 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_error_info, msg->data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
case SYSTEM_SERVER_STARTED: {
|
||||
LOGD("system server started, mounting prop");
|
||||
|
||||
if (mount(PROP_PATH, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) {
|
||||
PLOGE("failed to mount prop");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void rezygiskd_listener_stop() {
|
||||
if (monitor_sock_fd >= 0) close(monitor_sock_fd);
|
||||
monitor_sock_fd = -1;
|
||||
}
|
||||
|
||||
#define MAX_RETRY_COUNT 5
|
||||
|
||||
#define CREATE_ZYGOTE_START_COUNTER(abi) \
|
||||
struct timespec last_zygote##abi = { \
|
||||
.tv_sec = 0, \
|
||||
.tv_nsec = 0 \
|
||||
}; \
|
||||
\
|
||||
int count_zygote ## abi = 0; \
|
||||
bool should_stop_inject ## abi() { \
|
||||
struct timespec now = {}; \
|
||||
clock_gettime(CLOCK_MONOTONIC, &now); \
|
||||
if (now.tv_sec - last_zygote ## abi.tv_sec < 30) \
|
||||
count_zygote ## abi++; \
|
||||
else \
|
||||
count_zygote ## abi = 0; \
|
||||
\
|
||||
last_zygote##abi = now; \
|
||||
\
|
||||
return count_zygote##abi >= MAX_RETRY_COUNT; \
|
||||
}
|
||||
|
||||
CREATE_ZYGOTE_START_COUNTER(64)
|
||||
CREATE_ZYGOTE_START_COUNTER(32)
|
||||
|
||||
static bool ensure_daemon_created(bool is_64bit) {
|
||||
struct rezygiskd_status *status = is_64bit ? &status64 : &status32;
|
||||
if (is_64bit) {
|
||||
LOGD("new zygote started.");
|
||||
|
||||
umount2("/data/adb/modules/zygisksu/module.prop", MNT_DETACH);
|
||||
}
|
||||
|
||||
status->zygote_injected = false;
|
||||
|
||||
if (status->daemon_pid == -1) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
PLOGE("create daemon%s", is_64bit ? "64" : "32");
|
||||
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
char daemon_name[PATH_MAX] = "./bin/zygiskd";
|
||||
strcat(daemon_name, is_64bit ? "64" : "32");
|
||||
|
||||
execl(daemon_name, daemon_name, NULL);
|
||||
|
||||
PLOGE("exec daemon %s failed", daemon_name);
|
||||
|
||||
exit(1);
|
||||
} else {
|
||||
status->supported = true;
|
||||
status->daemon_pid = pid;
|
||||
status->daemon_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return status->daemon_running;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_DAEMON_EXIT(abi) \
|
||||
if (status##abi.supported && pid == status##abi.daemon_pid) { \
|
||||
char status_str[64]; \
|
||||
parse_status(sigchld_status, status_str, sizeof(status_str)); \
|
||||
\
|
||||
LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \
|
||||
status##abi.daemon_running = false; \
|
||||
\
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
status##abi.daemon_error_info = (char *)malloc(strlen(status_str) + 1); \
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
LOGE("malloc daemon" #abi " error info failed"); \
|
||||
\
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str) + 1); \
|
||||
} \
|
||||
\
|
||||
update_status(NULL); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define PRE_INJECT(abi, is_64) \
|
||||
if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \
|
||||
tracer = "./bin/zygisk-ptrace" # abi; \
|
||||
\
|
||||
if (should_stop_inject ## abi()) { \
|
||||
LOGW("zygote" # abi " restart too much times, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "zygote crashed", sizeof("zygote crashed")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
if (!ensure_daemon_created(is_64)) { \
|
||||
LOGW("daemon" #abi " not running, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "daemon not running", sizeof("daemon not running")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
int sigchld_signal_fd;
|
||||
struct signalfd_siginfo sigchld_fdsi;
|
||||
int sigchld_status;
|
||||
|
||||
pid_t *sigchld_process;
|
||||
size_t sigchld_process_count = 0;
|
||||
|
||||
bool sigchld_listener_init() {
|
||||
sigchld_process = NULL;
|
||||
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
|
||||
PLOGE("set sigprocmask");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sigchld_signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (sigchld_signal_fd == -1) {
|
||||
PLOGE("create signalfd");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void sigchld_listener_callback() {
|
||||
while (1) {
|
||||
ssize_t s = read(sigchld_signal_fd, &sigchld_fdsi, sizeof(sigchld_fdsi));
|
||||
if (s == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("read signalfd");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s != sizeof(sigchld_fdsi)) {
|
||||
LOGW("read %zu != %zu", s, sizeof(sigchld_fdsi));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sigchld_fdsi.ssi_signo != SIGCHLD) {
|
||||
LOGW("no sigchld received");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int pid;
|
||||
while ((pid = waitpid(-1, &sigchld_status, __WALL | WNOHANG)) != 0) {
|
||||
if (pid == -1) {
|
||||
if (tracing_state == STOPPED && errno == ECHILD) break;
|
||||
PLOGE("waitpid");
|
||||
}
|
||||
|
||||
if (pid == 1) {
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_FORK)) {
|
||||
long child_pid;
|
||||
|
||||
ptrace(PTRACE_GETEVENTMSG, pid, 0, &child_pid);
|
||||
|
||||
LOGV("forked %ld", child_pid);
|
||||
} else if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP) && tracing_state == STOPPING) {
|
||||
if (ptrace(PTRACE_DETACH, 1, 0, 0) == -1) PLOGE("failed to detach init");
|
||||
|
||||
tracing_state = STOPPED;
|
||||
|
||||
LOGI("stop tracing init");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(sigchld_status)) {
|
||||
if (WPTEVENT(sigchld_status) == 0) {
|
||||
if (WSTOPSIG(sigchld_status) != SIGSTOP && WSTOPSIG(sigchld_status) != SIGTSTP && WSTOPSIG(sigchld_status) != SIGTTIN && WSTOPSIG(sigchld_status) != SIGTTOU) {
|
||||
LOGW("inject signal sent to init: %s %d", sigabbrev_np(WSTOPSIG(sigchld_status)), WSTOPSIG(sigchld_status));
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(sigchld_status));
|
||||
|
||||
continue;
|
||||
} else {
|
||||
LOGW("suppress stopping signal sent to init: %s %d", sigabbrev_np(WSTOPSIG(sigchld_status)), WSTOPSIG(sigchld_status));
|
||||
}
|
||||
}
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK_DAEMON_EXIT(64)
|
||||
CHECK_DAEMON_EXIT(32)
|
||||
|
||||
pid_t state = 0;
|
||||
for (size_t i = 0; i < sigchld_process_count; i++) {
|
||||
if (sigchld_process[i] != pid) continue;
|
||||
|
||||
state = sigchld_process[i];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
LOGV("new process %d attached", pid);
|
||||
|
||||
for (size_t i = 0; i < sigchld_process_count; i++) {
|
||||
if (sigchld_process[i] != 0) continue;
|
||||
|
||||
sigchld_process[i] = pid;
|
||||
|
||||
goto ptrace_process;
|
||||
}
|
||||
|
||||
sigchld_process = (pid_t *)realloc(sigchld_process, sizeof(pid_t) * (sigchld_process_count + 1));
|
||||
if (sigchld_process == NULL) {
|
||||
PLOGE("realloc sigchld_process");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
sigchld_process[sigchld_process_count] = pid;
|
||||
sigchld_process_count++;
|
||||
|
||||
ptrace_process:
|
||||
|
||||
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC);
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) {
|
||||
char program[PATH_MAX];
|
||||
if (get_program(pid, program, sizeof(program)) == -1) {
|
||||
LOGW("failed to get program %d", pid);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGV("%d program %s", pid, program);
|
||||
const char* tracer = NULL;
|
||||
|
||||
do {
|
||||
if (tracing_state != TRACING) {
|
||||
LOGW("stop injecting %d because not tracing", pid);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PRE_INJECT(64, true)
|
||||
PRE_INJECT(32, false)
|
||||
|
||||
if (tracer != NULL) {
|
||||
LOGD("stopping %d", pid);
|
||||
|
||||
kill(pid, SIGSTOP);
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
waitpid(pid, &sigchld_status, __WALL);
|
||||
|
||||
if (STOPPED_WITH(SIGSTOP, 0)) {
|
||||
LOGD("detaching %d", pid);
|
||||
|
||||
ptrace(PTRACE_DETACH, pid, 0, SIGSTOP);
|
||||
sigchld_status = 0;
|
||||
int p = fork_dont_care();
|
||||
|
||||
if (p == 0) {
|
||||
char pid_str[32];
|
||||
sprintf(pid_str, "%d", pid);
|
||||
|
||||
execl(tracer, basename(tracer), "trace", pid_str, "--restart", NULL);
|
||||
|
||||
PLOGE("failed to exec, kill");
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
exit(1);
|
||||
} else if (p == -1) {
|
||||
PLOGE("failed to fork, kill");
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
update_status(NULL);
|
||||
} else {
|
||||
char status_str[64];
|
||||
parse_status(sigchld_status, status_str, sizeof(status_str));
|
||||
|
||||
LOGW("process %d received unknown sigchld_status %s", pid, status_str);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sigchld_process_count; i++) {
|
||||
if (sigchld_process[i] != pid) continue;
|
||||
|
||||
sigchld_process[i] = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(sigchld_status)) {
|
||||
LOGV("detach process %d", pid);
|
||||
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sigchld_listener_stop() {
|
||||
if (sigchld_signal_fd >= 0) close(sigchld_signal_fd);
|
||||
sigchld_signal_fd = -1;
|
||||
|
||||
if (sigchld_process != NULL) free(sigchld_process);
|
||||
sigchld_process = NULL;
|
||||
sigchld_process_count = 0;
|
||||
}
|
||||
|
||||
static char pre_section[1024];
|
||||
static char post_section[1024];
|
||||
|
||||
#define WRITE_STATUS_ABI(suffix) \
|
||||
if (status ## suffix.supported) { \
|
||||
strcat(status_text, " zygote" # suffix ": "); \
|
||||
if (tracing_state != TRACING) strcat(status_text, "❓ unknown, "); \
|
||||
else if (status ## suffix.zygote_injected) strcat(status_text, "😋 injected, "); \
|
||||
else strcat(status_text, "❌ not injected, "); \
|
||||
\
|
||||
strcat(status_text, "daemon" # suffix ": "); \
|
||||
if (status ## suffix.daemon_running) { \
|
||||
strcat(status_text, "😋 running "); \
|
||||
\
|
||||
if (status ## suffix.daemon_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
strcat(status_text, status ## suffix.daemon_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} \
|
||||
} else { \
|
||||
strcat(status_text, "❌ crashed "); \
|
||||
\
|
||||
if (status ## suffix.daemon_error_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
strcat(status_text, status ## suffix.daemon_error_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
static bool update_status(const char *message) {
|
||||
FILE *prop = fopen(PROP_PATH, "w");
|
||||
if (prop == NULL) {
|
||||
PLOGE("failed to open prop");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message) {
|
||||
fprintf(prop, "%s[%s] %s", pre_section, message, post_section);
|
||||
fclose(prop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char status_text[1024] = "monitor: ";
|
||||
|
||||
switch (tracing_state) {
|
||||
case TRACING: {
|
||||
strcat(status_text, "😋 tracing");
|
||||
|
||||
break;
|
||||
}
|
||||
case STOPPING: [[fallthrough]];
|
||||
case STOPPED: {
|
||||
strcat(status_text, "❌ stopped");
|
||||
|
||||
break;
|
||||
}
|
||||
case EXITING: {
|
||||
strcat(status_text, "❌ exited");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracing_state != TRACING && monitor_stop_reason[0] != '\0') {
|
||||
strcat(status_text, " (");
|
||||
strcat(status_text, monitor_stop_reason);
|
||||
strcat(status_text, ")");
|
||||
}
|
||||
strcat(status_text, ",");
|
||||
|
||||
WRITE_STATUS_ABI(64)
|
||||
WRITE_STATUS_ABI(32)
|
||||
|
||||
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
|
||||
|
||||
fclose(prop);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prepare_environment() {
|
||||
/* INFO: We need to create the file first, otherwise the mount will fail */
|
||||
close(open(PROP_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0644));
|
||||
|
||||
FILE *orig_prop = fopen("/data/adb/modules/zygisksu/module.prop", "r");
|
||||
if (orig_prop == NULL) {
|
||||
PLOGE("failed to open orig prop");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool after_description = false;
|
||||
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), orig_prop) != NULL) {
|
||||
if (strncmp(line, "description=", strlen("description=")) == 0) {
|
||||
strcat(pre_section, "description=");
|
||||
strcat(post_section, line + strlen("description="));
|
||||
after_description = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (after_description) strcat(post_section, line);
|
||||
else strcat(pre_section, line);
|
||||
}
|
||||
|
||||
fclose(orig_prop);
|
||||
|
||||
/* INFO: This environment variable is related to Magisk Zygisk/Manager. It
|
||||
it used by Magisk's Zygisk to communicate to Magisk Manager whether
|
||||
Zygisk is working or not.
|
||||
|
||||
Because of that behavior, we can knowledge built-in Zygisk is being
|
||||
used and stop the continuation of initialization of ReZygisk.*/
|
||||
if (getenv("ZYGISK_ENABLED")) {
|
||||
update_status("❌ Disable Magisk's built-in Zygisk");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return update_status(NULL);
|
||||
}
|
||||
|
||||
void init_monitor() {
|
||||
LOGI("ReZygisk %s", ZKSU_VERSION);
|
||||
|
||||
if (!prepare_environment()) exit(1);
|
||||
|
||||
monitor_events_init();
|
||||
|
||||
rezygiskd_listener_init();
|
||||
|
||||
struct monitor_event_cbs listener_cbs = {
|
||||
.callback = rezygiskd_listener_callback,
|
||||
.stop_callback = rezygiskd_listener_stop
|
||||
};
|
||||
monitor_events_register_event(&listener_cbs, monitor_sock_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
sigchld_listener_init();
|
||||
|
||||
struct monitor_event_cbs sigchld_cbs = {
|
||||
.callback = sigchld_listener_callback,
|
||||
.stop_callback = sigchld_listener_stop
|
||||
};
|
||||
monitor_events_register_event(&sigchld_cbs, sigchld_signal_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
monitor_events_loop();
|
||||
|
||||
if (status64.daemon_info) free(status64.daemon_info);
|
||||
if (status64.daemon_error_info) free(status64.daemon_error_info);
|
||||
if (status32.daemon_info) free(status32.daemon_info);
|
||||
if (status32.daemon_error_info) free(status32.daemon_error_info);
|
||||
|
||||
LOGI("exit");
|
||||
}
|
||||
|
||||
int send_control_command(enum rezygiskd_command cmd) {
|
||||
int sockfd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (sockfd == -1) return -1;
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = { 0 }
|
||||
};
|
||||
|
||||
size_t sun_path_len = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", rezygiskd_get_path(), SOCKET_NAME);
|
||||
|
||||
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||
|
||||
ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (struct sockaddr *)&addr, socklen);
|
||||
|
||||
close(sockfd);
|
||||
|
||||
return nsend != sizeof(cmd) ? -1 : 0;
|
||||
}
|
||||
@@ -1,838 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
#include <unistd.h>
|
||||
#include <set>
|
||||
#include <sys/signalfd.h>
|
||||
#include <err.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/mount.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "monitor.h"
|
||||
#include "utils.hpp"
|
||||
#include "misc.h"
|
||||
|
||||
#define STOPPED_WITH(sig, event) WIFSTOPPED(status) && (status >> 8 == ((sig) | (event << 8)))
|
||||
|
||||
static void updateStatus();
|
||||
|
||||
char monitor_stop_reason[32];
|
||||
|
||||
constexpr char SOCKET_NAME[] = "init_monitor";
|
||||
|
||||
struct EventLoop;
|
||||
|
||||
struct EventHandler {
|
||||
virtual int GetFd() = 0;
|
||||
virtual void HandleEvent(EventLoop& loop, uint32_t event) = 0;
|
||||
};
|
||||
|
||||
struct EventLoop {
|
||||
private:
|
||||
int epoll_fd_;
|
||||
bool running = false;
|
||||
public:
|
||||
bool Init() {
|
||||
epoll_fd_ = epoll_create(1);
|
||||
if (epoll_fd_ == -1) {
|
||||
PLOGE("failed to create");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
void Loop() {
|
||||
running = true;
|
||||
|
||||
constexpr auto MAX_EVENTS = 2;
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
|
||||
while (running) {
|
||||
int nfds = epoll_wait(epoll_fd_, events, MAX_EVENTS, -1);
|
||||
if (nfds == -1) {
|
||||
if (errno != EINTR) PLOGE("epoll_wait");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
reinterpret_cast<EventHandler *>(events[i].data.ptr)->HandleEvent(*this,
|
||||
events[i].events);
|
||||
if (!running) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RegisterHandler(EventHandler &handler, uint32_t events) {
|
||||
struct epoll_event ev{};
|
||||
ev.events = events;
|
||||
ev.data.ptr = &handler;
|
||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, handler.GetFd(), &ev) == -1) {
|
||||
PLOGE("failed to add event handler");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnregisterHandler(EventHandler &handler) {
|
||||
if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, handler.GetFd(), NULL) == -1) {
|
||||
PLOGE("failed to del event handler");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
~EventLoop() {
|
||||
if (epoll_fd_ >= 0) close(epoll_fd_);
|
||||
}
|
||||
};
|
||||
|
||||
enum TracingState {
|
||||
TRACING = 1,
|
||||
STOPPING,
|
||||
STOPPED,
|
||||
EXITING
|
||||
};
|
||||
|
||||
TracingState tracing_state = TRACING;
|
||||
static char prop_path[PATH_MAX];
|
||||
|
||||
struct Status {
|
||||
bool supported = false;
|
||||
bool zygote_injected = false;
|
||||
bool daemon_running = false;
|
||||
pid_t daemon_pid = -1;
|
||||
char *daemon_info = NULL;
|
||||
char *daemon_error_info = NULL;
|
||||
};
|
||||
|
||||
Status status64;
|
||||
Status status32;
|
||||
|
||||
struct SocketHandler : public EventHandler {
|
||||
int sock_fd_;
|
||||
|
||||
bool Init() {
|
||||
sock_fd_ = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||
if (sock_fd_ == -1) {
|
||||
PLOGE("socket create");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = { 0 }
|
||||
};
|
||||
|
||||
char tmp_path[PATH_MAX];
|
||||
rezygiskd_get_path(tmp_path, sizeof(tmp_path));
|
||||
|
||||
size_t sun_path_len = sprintf(addr.sun_path, "%s/%s", tmp_path, SOCKET_NAME);
|
||||
|
||||
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||
if (bind(sock_fd_, (struct sockaddr *)&addr, socklen) == -1) {
|
||||
PLOGE("bind socket");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetFd() override {
|
||||
return sock_fd_;
|
||||
}
|
||||
|
||||
void HandleEvent(EventLoop &loop, uint32_t) override {
|
||||
struct [[gnu::packed]] MsgHead {
|
||||
enum Command cmd;
|
||||
int length;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
while (1) {
|
||||
struct MsgHead *msg = (struct MsgHead *)malloc(sizeof(struct MsgHead));
|
||||
|
||||
ssize_t real_size;
|
||||
ssize_t nread = recv(sock_fd_, msg, sizeof(struct MsgHead), MSG_PEEK);
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("read socket");
|
||||
}
|
||||
|
||||
if ((size_t)nread < sizeof(Command)) {
|
||||
LOGE("read %zu < %zu", nread, sizeof(Command));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg->cmd >= Command::DAEMON64_SET_INFO && msg->cmd != Command::SYSTEM_SERVER_STARTED) {
|
||||
if (nread != sizeof(msg)) {
|
||||
LOGE("cmd %d size %zu != %zu", msg->cmd, nread, sizeof(MsgHead));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
real_size = sizeof(MsgHead) + msg->length;
|
||||
} else {
|
||||
if (nread != sizeof(Command)) {
|
||||
LOGE("cmd %d size %zu != %zu", msg->cmd, nread, sizeof(Command));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
real_size = sizeof(Command);
|
||||
}
|
||||
|
||||
msg = (struct MsgHead *)realloc(msg, real_size);
|
||||
nread = recv(sock_fd_, msg, real_size, 0);
|
||||
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("recv");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nread != real_size) {
|
||||
LOGE("real size %zu != %zu", real_size, nread);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (msg->cmd) {
|
||||
case START: {
|
||||
if (tracing_state == STOPPING) tracing_state = TRACING;
|
||||
else if (tracing_state == STOPPED) {
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
|
||||
LOGI("start tracing init");
|
||||
|
||||
tracing_state = TRACING;
|
||||
}
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (tracing_state == TRACING) {
|
||||
LOGI("stop tracing requested");
|
||||
|
||||
tracing_state = STOPPING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0);
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case EXIT: {
|
||||
LOGI("prepare for exit ...");
|
||||
|
||||
tracing_state = EXITING;
|
||||
strcpy(monitor_stop_reason, "user requested");
|
||||
|
||||
updateStatus();
|
||||
loop.Stop();
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE64_INJECTED: {
|
||||
status64.zygote_injected = true;
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case ZYGOTE32_INJECTED: {
|
||||
status32.zygote_injected = true;
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_INFO: {
|
||||
LOGD("received daemon64 info %s", msg->data);
|
||||
|
||||
/* Will only happen if somehow the daemon restarts */
|
||||
if (status64.daemon_info) {
|
||||
free(status64.daemon_info);
|
||||
status64.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_info = (char *)malloc(msg->length);
|
||||
if (!status64.daemon_info) {
|
||||
PLOGE("malloc daemon64 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_info, msg->data);
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON32_SET_INFO: {
|
||||
LOGD("received daemon32 info %s", msg->data);
|
||||
|
||||
if (status32.daemon_info) {
|
||||
free(status32.daemon_info);
|
||||
status32.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_info = (char *)malloc(msg->length);
|
||||
if (!status32.daemon_info) {
|
||||
PLOGE("malloc daemon32 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_info, msg->data);
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON64_SET_ERROR_INFO: {
|
||||
LOGD("received daemon64 error info %s", msg->data);
|
||||
|
||||
status64.daemon_running = false;
|
||||
|
||||
if (status64.daemon_error_info) {
|
||||
free(status64.daemon_error_info);
|
||||
status64.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_error_info = (char *)malloc(msg->length);
|
||||
if (!status64.daemon_error_info) {
|
||||
PLOGE("malloc daemon64 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_error_info, msg->data);
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case DAEMON32_SET_ERROR_INFO: {
|
||||
LOGD("received daemon32 error info %s", msg->data);
|
||||
|
||||
status32.daemon_running = false;
|
||||
|
||||
if (status32.daemon_error_info) {
|
||||
free(status32.daemon_error_info);
|
||||
status32.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_error_info = (char *)malloc(msg->length);
|
||||
if (!status32.daemon_error_info) {
|
||||
PLOGE("malloc daemon32 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_error_info, msg->data);
|
||||
|
||||
updateStatus();
|
||||
|
||||
break;
|
||||
}
|
||||
case SYSTEM_SERVER_STARTED: {
|
||||
LOGD("system server started, mounting prop");
|
||||
|
||||
if (mount(prop_path, "/data/adb/modules/zygisksu/module.prop", NULL, MS_BIND, NULL) == -1) {
|
||||
PLOGE("failed to mount prop");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
~SocketHandler() {
|
||||
if (sock_fd_ >= 0) close(sock_fd_);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr int MAX_RETRY_COUNT = 5;
|
||||
|
||||
#define CREATE_ZYGOTE_START_COUNTER(abi) \
|
||||
struct timespec last_zygote##abi = { \
|
||||
.tv_sec = 0, \
|
||||
.tv_nsec = 0 \
|
||||
}; \
|
||||
\
|
||||
int count_zygote ## abi = 0; \
|
||||
bool should_stop_inject ## abi() { \
|
||||
struct timespec now = {}; \
|
||||
clock_gettime(CLOCK_MONOTONIC, &now); \
|
||||
if (now.tv_sec - last_zygote ## abi.tv_sec < 30) \
|
||||
count_zygote ## abi++; \
|
||||
else \
|
||||
count_zygote ## abi = 0; \
|
||||
\
|
||||
last_zygote##abi = now; \
|
||||
\
|
||||
return count_zygote##abi >= MAX_RETRY_COUNT; \
|
||||
}
|
||||
|
||||
CREATE_ZYGOTE_START_COUNTER(64)
|
||||
CREATE_ZYGOTE_START_COUNTER(32)
|
||||
|
||||
static bool ensure_daemon_created(bool is_64bit) {
|
||||
Status *status = is_64bit ? &status64 : &status32;
|
||||
if (is_64bit) {
|
||||
LOGD("new zygote started.");
|
||||
|
||||
umount2("/data/adb/modules/zygisksu/module.prop", MNT_DETACH);
|
||||
}
|
||||
|
||||
status->zygote_injected = false;
|
||||
|
||||
if (status->daemon_pid == -1) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
PLOGE("create daemon%s", is_64bit ? "64" : "32");
|
||||
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
char daemon_name[PATH_MAX] = "./bin/zygiskd";
|
||||
strcat(daemon_name, is_64bit ? "64" : "32");
|
||||
|
||||
execl(daemon_name, daemon_name, NULL);
|
||||
|
||||
PLOGE("exec daemon %s failed", daemon_name);
|
||||
|
||||
exit(1);
|
||||
} else {
|
||||
status->supported = true;
|
||||
status->daemon_pid = pid;
|
||||
status->daemon_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return status->daemon_running;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK_DAEMON_EXIT(abi) \
|
||||
if (status##abi.supported && pid == status64.daemon_pid) { \
|
||||
char status_str[64]; \
|
||||
parse_status(status, status_str, sizeof(status_str)); \
|
||||
\
|
||||
LOGW("daemon" #abi " pid %d exited: %s", pid, status_str); \
|
||||
status##abi.daemon_running = false; \
|
||||
\
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
status##abi.daemon_error_info = (char *)malloc(strlen(status_str) + 1); \
|
||||
if (!status##abi.daemon_error_info) { \
|
||||
LOGE("malloc daemon" #abi " error info failed"); \
|
||||
\
|
||||
return; \
|
||||
} \
|
||||
\
|
||||
memcpy(status##abi.daemon_error_info, status_str, strlen(status_str) + 1); \
|
||||
} \
|
||||
\
|
||||
updateStatus(); \
|
||||
continue; \
|
||||
}
|
||||
|
||||
#define PRE_INJECT(abi, is_64) \
|
||||
if (strcmp(program, "/system/bin/app_process" # abi) == 0) { \
|
||||
tracer = "./bin/zygisk-ptrace" # abi; \
|
||||
\
|
||||
if (should_stop_inject ## abi()) { \
|
||||
LOGW("zygote" # abi " restart too much times, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "zygote crashed", sizeof("zygote crashed")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
if (!ensure_daemon_created(is_64)) { \
|
||||
LOGW("daemon" #abi " not running, stop injecting"); \
|
||||
\
|
||||
tracing_state = STOPPING; \
|
||||
memcpy(monitor_stop_reason, "daemon not running", sizeof("daemon not running")); \
|
||||
ptrace(PTRACE_INTERRUPT, 1, 0, 0); \
|
||||
\
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
struct SigChldHandler : public EventHandler {
|
||||
private:
|
||||
int signal_fd_;
|
||||
struct signalfd_siginfo fdsi;
|
||||
int status;
|
||||
std::set<pid_t> process;
|
||||
|
||||
public:
|
||||
bool Init() {
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
|
||||
PLOGE("set sigprocmask");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
signal_fd_ = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (signal_fd_ == -1) {
|
||||
PLOGE("create signalfd");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetFd() override {
|
||||
return signal_fd_;
|
||||
}
|
||||
|
||||
void HandleEvent(EventLoop &, uint32_t) override {
|
||||
while (1) {
|
||||
ssize_t s = read(signal_fd_, &fdsi, sizeof(fdsi));
|
||||
if (s == -1) {
|
||||
if (errno == EAGAIN) break;
|
||||
|
||||
PLOGE("read signalfd");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s != sizeof(fdsi)) {
|
||||
LOGW("read %zu != %zu", s, sizeof(fdsi));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fdsi.ssi_signo != SIGCHLD) {
|
||||
LOGW("no sigchld received");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int pid;
|
||||
while ((pid = waitpid(-1, &status, __WALL | WNOHANG)) != 0) {
|
||||
if (pid == -1) {
|
||||
if (tracing_state == STOPPED && errno == ECHILD) break;
|
||||
PLOGE("waitpid");
|
||||
}
|
||||
|
||||
if (pid == 1) {
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_FORK)) {
|
||||
long child_pid;
|
||||
|
||||
ptrace(PTRACE_GETEVENTMSG, pid, 0, &child_pid);
|
||||
|
||||
LOGV("forked %ld", child_pid);
|
||||
} else if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_STOP) && tracing_state == STOPPING) {
|
||||
if (ptrace(PTRACE_DETACH, 1, 0, 0) == -1) PLOGE("failed to detach init");
|
||||
|
||||
tracing_state = STOPPED;
|
||||
|
||||
LOGI("stop tracing init");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WIFSTOPPED(status)) {
|
||||
if (WPTEVENT(status) == 0) {
|
||||
if (WSTOPSIG(status) != SIGSTOP && WSTOPSIG(status) != SIGTSTP && WSTOPSIG(status) != SIGTTIN && WSTOPSIG(status) != SIGTTOU) {
|
||||
LOGW("inject signal sent to init: %s %d", sigabbrev_np(WSTOPSIG(status)), WSTOPSIG(status));
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
|
||||
|
||||
continue;
|
||||
} else {
|
||||
LOGW("suppress stopping signal sent to init: %s %d", sigabbrev_np(WSTOPSIG(status)), WSTOPSIG(status));
|
||||
}
|
||||
}
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CHECK_DAEMON_EXIT(64)
|
||||
CHECK_DAEMON_EXIT(32)
|
||||
|
||||
auto state = process.find(pid);
|
||||
|
||||
if (state == process.end()) {
|
||||
LOGV("new process %d attached", pid);
|
||||
|
||||
process.emplace(pid);
|
||||
|
||||
ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC);
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
if (STOPPED_WITH(SIGTRAP, PTRACE_EVENT_EXEC)) {
|
||||
char program[PATH_MAX];
|
||||
if (get_program(pid, program, sizeof(program)) == -1) {
|
||||
LOGW("failed to get program %d", pid);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGV("%d program %s", pid, program);
|
||||
const char* tracer = NULL;
|
||||
|
||||
do {
|
||||
if (tracing_state != TRACING) {
|
||||
LOGW("stop injecting %d because not tracing", pid);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
PRE_INJECT(64, true)
|
||||
PRE_INJECT(32, false)
|
||||
|
||||
if (tracer != NULL) {
|
||||
LOGD("stopping %d", pid);
|
||||
|
||||
kill(pid, SIGSTOP);
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
waitpid(pid, &status, __WALL);
|
||||
|
||||
if (STOPPED_WITH(SIGSTOP, 0)) {
|
||||
LOGD("detaching %d", pid);
|
||||
|
||||
ptrace(PTRACE_DETACH, pid, 0, SIGSTOP);
|
||||
status = 0;
|
||||
auto p = fork_dont_care();
|
||||
|
||||
if (p == 0) {
|
||||
char pid_str[32];
|
||||
sprintf(pid_str, "%d", pid);
|
||||
|
||||
execl(tracer, basename(tracer), "trace", pid_str, "--restart", NULL);
|
||||
|
||||
PLOGE("failed to exec, kill");
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
exit(1);
|
||||
} else if (p == -1) {
|
||||
PLOGE("failed to fork, kill");
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
updateStatus();
|
||||
} else {
|
||||
char status_str[64];
|
||||
parse_status(status, status_str, sizeof(status_str));
|
||||
|
||||
LOGW("process %d received unknown status %s", pid, status_str);
|
||||
}
|
||||
|
||||
process.erase(state);
|
||||
if (WIFSTOPPED(status)) {
|
||||
LOGV("detach process %d", pid);
|
||||
|
||||
ptrace(PTRACE_DETACH, pid, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~SigChldHandler() {
|
||||
if (signal_fd_ >= 0) close(signal_fd_);
|
||||
}
|
||||
};
|
||||
|
||||
static char pre_section[1024];
|
||||
static char post_section[1024];
|
||||
|
||||
#define WRITE_STATUS_ABI(suffix) \
|
||||
if (status ## suffix.supported) { \
|
||||
strcat(status_text, " zygote" # suffix ": "); \
|
||||
if (tracing_state != TRACING) strcat(status_text, "❓ unknown, "); \
|
||||
else if (status ## suffix.zygote_injected) strcat(status_text, "😋 injected, "); \
|
||||
else strcat(status_text, "❌ not injected, "); \
|
||||
\
|
||||
strcat(status_text, "daemon" # suffix ": "); \
|
||||
if (status ## suffix.daemon_running) { \
|
||||
strcat(status_text, "😋 running "); \
|
||||
\
|
||||
if (status ## suffix.daemon_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
strcat(status_text, status ## suffix.daemon_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} \
|
||||
} else { \
|
||||
strcat(status_text, "❌ crashed "); \
|
||||
\
|
||||
if (status ## suffix.daemon_error_info != NULL) { \
|
||||
strcat(status_text, "("); \
|
||||
strcat(status_text, status ## suffix.daemon_error_info); \
|
||||
strcat(status_text, ")"); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
static void updateStatus() {
|
||||
FILE *prop = fopen(prop_path, "w");
|
||||
char status_text[1024] = "monitor: ";
|
||||
|
||||
switch (tracing_state) {
|
||||
case TRACING: {
|
||||
strcat(status_text, "😋 tracing");
|
||||
|
||||
break;
|
||||
}
|
||||
case STOPPING: [[fallthrough]];
|
||||
case STOPPED: {
|
||||
strcat(status_text, "❌ stopped");
|
||||
|
||||
break;
|
||||
}
|
||||
case EXITING: {
|
||||
strcat(status_text, "❌ exited");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tracing_state != TRACING && monitor_stop_reason[0] != '\0') {
|
||||
strcat(status_text, " (");
|
||||
strcat(status_text, monitor_stop_reason);
|
||||
strcat(status_text, ")");
|
||||
}
|
||||
strcat(status_text, ",");
|
||||
|
||||
WRITE_STATUS_ABI(64)
|
||||
WRITE_STATUS_ABI(32)
|
||||
|
||||
fprintf(prop, "%s[%s] %s", pre_section, status_text, post_section);
|
||||
|
||||
fclose(prop);
|
||||
}
|
||||
|
||||
static bool prepare_environment() {
|
||||
char tmp_path[PATH_MAX];
|
||||
rezygiskd_get_path(tmp_path, sizeof(tmp_path));
|
||||
|
||||
strcat(prop_path, tmp_path);
|
||||
strcat(prop_path, "/module.prop");
|
||||
|
||||
close(open(prop_path, O_WRONLY | O_CREAT | O_TRUNC, 0644));
|
||||
|
||||
FILE *orig_prop = fopen("./module.prop", "r");
|
||||
if (orig_prop == NULL) {
|
||||
PLOGE("failed to open orig prop");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool after_description = false;
|
||||
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), orig_prop) != NULL) {
|
||||
if (strncmp(line, "description=", strlen("description=")) == 0) {
|
||||
strcat(pre_section, "description=");
|
||||
strcat(post_section, line + strlen("description="));
|
||||
after_description = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (after_description) strcat(post_section, line);
|
||||
else strcat(pre_section, line);
|
||||
}
|
||||
|
||||
fclose(orig_prop);
|
||||
|
||||
/* TODO: See if ZYGISK_ENABLED flag is already set,
|
||||
if so, set a status saying to disable built-in Zygisk. */
|
||||
updateStatus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_monitor() {
|
||||
LOGI("ReZygisk %s", ZKSU_VERSION);
|
||||
|
||||
if (!prepare_environment()) exit(1);
|
||||
|
||||
SocketHandler socketHandler{};
|
||||
socketHandler.Init();
|
||||
SigChldHandler ptraceHandler{};
|
||||
ptraceHandler.Init();
|
||||
EventLoop looper;
|
||||
|
||||
looper.Init();
|
||||
looper.RegisterHandler(socketHandler, EPOLLIN | EPOLLET);
|
||||
looper.RegisterHandler(ptraceHandler, EPOLLIN | EPOLLET);
|
||||
looper.Loop();
|
||||
|
||||
if (status64.daemon_info) free(status64.daemon_info);
|
||||
if (status64.daemon_error_info) free(status64.daemon_error_info);
|
||||
if (status32.daemon_info) free(status32.daemon_info);
|
||||
if (status32.daemon_error_info) free(status32.daemon_error_info);
|
||||
|
||||
LOGI("exit");
|
||||
}
|
||||
|
||||
int send_control_command(enum Command cmd) {
|
||||
int sockfd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (sockfd == -1) return -1;
|
||||
|
||||
struct sockaddr_un addr = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = { 0 }
|
||||
};
|
||||
|
||||
char tmp_path[PATH_MAX];
|
||||
rezygiskd_get_path(tmp_path, sizeof(tmp_path));
|
||||
|
||||
size_t sun_path_len = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", tmp_path, SOCKET_NAME);
|
||||
|
||||
socklen_t socklen = sizeof(sa_family_t) + sun_path_len;
|
||||
|
||||
ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (sockaddr *)&addr, socklen);
|
||||
|
||||
/* TODO: Should we close even when it fails? */
|
||||
close(sockfd);
|
||||
|
||||
return nsend != sizeof(cmd) ? -1 : 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef MAIN_HPP
|
||||
#define MAIN_HPP
|
||||
#ifndef MONITOR_H
|
||||
#define MONITOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -7,7 +7,7 @@ void init_monitor();
|
||||
|
||||
bool trace_zygote(int pid);
|
||||
|
||||
enum Command {
|
||||
enum rezygiskd_command {
|
||||
START = 1,
|
||||
STOP = 2,
|
||||
EXIT = 3,
|
||||
@@ -22,6 +22,6 @@ enum Command {
|
||||
SYSTEM_SERVER_STARTED = 10
|
||||
};
|
||||
|
||||
int send_control_command(enum Command cmd);
|
||||
int send_control_command(enum rezygiskd_command cmd);
|
||||
|
||||
#endif /* MAIN_HPP */
|
||||
#endif /* MONITOR_H */
|
||||
@@ -1,20 +1,18 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dlfcn.h>
|
||||
#include <signal.h>
|
||||
#include <sys/system_properties.h>
|
||||
#include <string>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGI("injecting %s to zygote %d", lib_path, pid);
|
||||
@@ -25,16 +23,26 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/private/KernelArgumentBlock.h;l=30;drc=6d1ee77ee32220e4202c3066f7e1f69572967ad8
|
||||
*/
|
||||
|
||||
struct user_regs_struct regs {},
|
||||
backup {};
|
||||
struct user_regs_struct regs = { 0 };
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::vector<MapInfo> map = MapInfo::Scan(std::to_string(pid));
|
||||
if (!get_regs(pid, regs)) return false;
|
||||
char pid_maps[PATH_MAX];
|
||||
snprintf(pid_maps, sizeof(pid_maps), "/proc/%d/maps", pid);
|
||||
|
||||
struct maps *map = parse_maps(pid_maps);
|
||||
if (map == NULL) {
|
||||
LOGE("failed to parse remote maps");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!get_regs(pid, ®s)) return false;
|
||||
|
||||
uintptr_t arg = (uintptr_t)regs.REG_SP;
|
||||
|
||||
LOGV("kernel argument %" PRIxPTR " %s", arg, get_addr_mem_region(map, arg).c_str());
|
||||
char addr_mem_region[1024];
|
||||
get_addr_mem_region(map, arg, addr_mem_region, sizeof(addr_mem_region));
|
||||
|
||||
LOGV("kernel argument %" PRIxPTR " %s", arg, addr_mem_region);
|
||||
|
||||
int argc;
|
||||
char **argv = (char **)((uintptr_t *)arg + 1);
|
||||
@@ -43,12 +51,10 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
read_proc(pid, arg, &argc, sizeof(argc));
|
||||
LOGV("argc %d", argc);
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
auto envp = argv + argc + 1;
|
||||
char **envp = argv + argc + 1;
|
||||
LOGV("envp %p", (void *)envp);
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
auto p = envp;
|
||||
char **p = envp;
|
||||
while (1) {
|
||||
uintptr_t *buf;
|
||||
read_proc(pid, (uintptr_t)p, &buf, sizeof(buf));
|
||||
@@ -63,7 +69,9 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
p++;
|
||||
|
||||
ElfW(auxv_t) *auxv = (ElfW(auxv_t) *)p;
|
||||
LOGV("auxv %p %s", auxv, get_addr_mem_region(map, (uintptr_t) auxv).c_str());
|
||||
|
||||
get_addr_mem_region(map, (uintptr_t)auxv, addr_mem_region, sizeof(addr_mem_region));
|
||||
LOGV("auxv %p %s", auxv, addr_mem_region);
|
||||
|
||||
ElfW(auxv_t) *v = auxv;
|
||||
uintptr_t entry_addr = 0;
|
||||
@@ -78,8 +86,9 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
entry_addr = (uintptr_t)buf.a_un.a_val;
|
||||
addr_of_entry_addr = (uintptr_t)v + offsetof(ElfW(auxv_t), a_un);
|
||||
|
||||
get_addr_mem_region(map, entry_addr, addr_mem_region, sizeof(addr_mem_region));
|
||||
LOGV("entry address %" PRIxPTR " %s (entry=%" PRIxPTR ", entry_addr=%" PRIxPTR ")", entry_addr,
|
||||
get_addr_mem_region(map, entry_addr).c_str(), (uintptr_t)v, addr_of_entry_addr);
|
||||
addr_mem_region, (uintptr_t)v, addr_of_entry_addr);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -113,7 +122,7 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
int status;
|
||||
wait_for_trace(pid, &status, __WALL);
|
||||
if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGSEGV) {
|
||||
if (!get_regs(pid, regs)) return false;
|
||||
if (!get_regs(pid, ®s)) return false;
|
||||
|
||||
if (((int)regs.REG_IP & ~1) != ((int)break_addr & ~1)) {
|
||||
LOGE("stopped at unknown addr %p", (void *) regs.REG_IP);
|
||||
@@ -128,13 +137,25 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
if (!write_proc(pid, (uintptr_t) addr_of_entry_addr, &entry_addr, sizeof(entry_addr))) return false;
|
||||
|
||||
/* backup registers */
|
||||
struct user_regs_struct backup;
|
||||
memcpy(&backup, ®s, sizeof(regs));
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
map = MapInfo::Scan(std::to_string(pid));
|
||||
free_maps(map);
|
||||
|
||||
map = parse_maps(pid_maps);
|
||||
if (!map) {
|
||||
LOGE("failed to parse remote maps");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct maps *local_map = parse_maps("/proc/self/maps");
|
||||
if (!local_map) {
|
||||
LOGE("failed to parse local maps");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::vector<MapInfo> local_map = MapInfo::Scan();
|
||||
void *libc_return_addr = find_module_return_addr(map, "libc.so");
|
||||
LOGD("libc return addr %p", libc_return_addr);
|
||||
|
||||
@@ -142,17 +163,19 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
void *dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen");
|
||||
if (dlopen_addr == NULL) return false;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::vector<long> args;
|
||||
long *args = (long *)malloc(3 * sizeof(long));
|
||||
if (args == NULL) {
|
||||
LOGE("malloc args");
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
uintptr_t str = push_string(pid, regs, lib_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
args.clear();
|
||||
args.push_back((long) str);
|
||||
args.push_back((long) RTLD_NOW);
|
||||
uintptr_t str = push_string(pid, ®s, lib_path);
|
||||
|
||||
uintptr_t remote_handle = remote_call(pid, regs, (uintptr_t)dlopen_addr, (uintptr_t)libc_return_addr, args);
|
||||
args[0] = (long) str;
|
||||
args[1] = (long) RTLD_NOW;
|
||||
|
||||
uintptr_t remote_handle = remote_call(pid, ®s, (uintptr_t)dlopen_addr, (uintptr_t)libc_return_addr, args, 2);
|
||||
LOGD("remote handle %p", (void *)remote_handle);
|
||||
if (remote_handle == 0) {
|
||||
LOGE("handle is null");
|
||||
@@ -162,37 +185,47 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
if (dlerror_addr == NULL) {
|
||||
LOGE("find dlerror");
|
||||
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
args.clear();
|
||||
|
||||
uintptr_t dlerror_str_addr = remote_call(pid, regs, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args);
|
||||
uintptr_t dlerror_str_addr = remote_call(pid, ®s, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args, 0);
|
||||
LOGD("dlerror str %p", (void *)dlerror_str_addr);
|
||||
if (dlerror_str_addr == 0) return false;
|
||||
if (dlerror_str_addr == 0) {
|
||||
LOGE("dlerror str is null");
|
||||
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen");
|
||||
if (strlen_addr == NULL) {
|
||||
LOGE("find strlen");
|
||||
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
args.clear();
|
||||
args.push_back(dlerror_str_addr);
|
||||
args[0] = (long) dlerror_str_addr;
|
||||
|
||||
uintptr_t dlerror_len = remote_call(pid, regs, (uintptr_t)strlen_addr, (uintptr_t)libc_return_addr, args);
|
||||
uintptr_t dlerror_len = remote_call(pid, ®s, (uintptr_t)strlen_addr, (uintptr_t)libc_return_addr, args, 1);
|
||||
if (dlerror_len <= 0) {
|
||||
LOGE("dlerror len <= 0");
|
||||
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NOTICE: C++ -> C */
|
||||
char *err = (char *)malloc((dlerror_len + 1) * sizeof(char));
|
||||
if (err == NULL) {
|
||||
LOGE("malloc err");
|
||||
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -201,6 +234,7 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
LOGE("dlerror info %s", err);
|
||||
|
||||
free(err);
|
||||
free(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -209,12 +243,13 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
void *dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym");
|
||||
if (dlsym_addr == NULL) return false;
|
||||
|
||||
args.clear();
|
||||
str = push_string(pid, regs, "entry");
|
||||
args.push_back(remote_handle);
|
||||
args.push_back((long) str);
|
||||
free_maps(local_map);
|
||||
|
||||
uintptr_t injector_entry = remote_call(pid, regs, (uintptr_t)dlsym_addr, (uintptr_t)libc_return_addr, args);
|
||||
str = push_string(pid, ®s, "entry");
|
||||
args[0] = remote_handle;
|
||||
args[1] = (long) str;
|
||||
|
||||
uintptr_t injector_entry = remote_call(pid, ®s, (uintptr_t)dlsym_addr, (uintptr_t)libc_return_addr, args, 2);
|
||||
LOGD("injector entry %p", (void *)injector_entry);
|
||||
if (injector_entry == 0) {
|
||||
LOGE("injector entry is null");
|
||||
@@ -223,38 +258,41 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
}
|
||||
|
||||
/* record the address range of libzygisk.so */
|
||||
map = MapInfo::Scan(std::to_string(pid));
|
||||
void *start_addr = nullptr;
|
||||
map = parse_maps(pid_maps);
|
||||
|
||||
void *start_addr = NULL;
|
||||
size_t block_size = 0;
|
||||
for (auto &info : map) {
|
||||
if (strstr(info.path.c_str(), "libzygisk.so")) {
|
||||
void *addr = (void *)info.start;
|
||||
if (start_addr == nullptr) start_addr = addr;
|
||||
size_t size = info.end - info.start;
|
||||
|
||||
for (size_t i = 0; i < map->size; i++) {
|
||||
if (!strstr(map->maps[i].path, "libzygisk.so")) continue;
|
||||
|
||||
if (start_addr == NULL) start_addr = (void *)map->maps[i].start;
|
||||
|
||||
size_t size = map->maps[i].end - map->maps[i].start;
|
||||
block_size += size;
|
||||
LOGD("found block %s: [%p-%p] with size %zu", info.path.c_str(), addr, (void *)info.end, size);
|
||||
}
|
||||
|
||||
LOGD("found block %s: [%p-%p] with size %zu", map->maps[i].path, (void *)map->maps[i].start,
|
||||
(void *)map->maps[i].end, size);
|
||||
}
|
||||
|
||||
free_maps(map);
|
||||
|
||||
/* call injector entry(start_addr, block_size, path) */
|
||||
args.clear();
|
||||
args.push_back((uintptr_t) start_addr);
|
||||
args.push_back(block_size);
|
||||
args[0] = (uintptr_t)start_addr;
|
||||
args[1] = block_size;
|
||||
str = push_string(pid, ®s, rezygiskd_get_path());
|
||||
args[2] = (uintptr_t)str;
|
||||
|
||||
char tmp_path[PATH_MAX];
|
||||
rezygiskd_get_path(tmp_path, sizeof(tmp_path));
|
||||
remote_call(pid, ®s, injector_entry, (uintptr_t)libc_return_addr, args, 3);
|
||||
|
||||
str = push_string(pid, regs, tmp_path);
|
||||
args.push_back((long) str);
|
||||
|
||||
remote_call(pid, regs, injector_entry, (uintptr_t)libc_return_addr, args);
|
||||
free(args);
|
||||
|
||||
/* reset pc to entry */
|
||||
backup.REG_IP = (long) entry_addr;
|
||||
LOGD("invoke entry");
|
||||
|
||||
/* restore registers */
|
||||
if (!set_regs(pid, backup)) return false;
|
||||
if (!set_regs(pid, &backup)) return false;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
@@ -290,11 +328,8 @@ bool trace_zygote(int pid) {
|
||||
WAIT_OR_DIE
|
||||
|
||||
if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) {
|
||||
/* WARNING: C++ keyword */
|
||||
char lib_path[PATH_MAX];
|
||||
rezygiskd_get_path(lib_path, sizeof(lib_path));
|
||||
|
||||
strcat(lib_path,"/lib" LP_SELECT("", "64") "/libzygisk.so");
|
||||
snprintf(lib_path, sizeof(lib_path), "%s/lib" LP_SELECT("", "64") "/libzygisk.so", rezygiskd_get_path());
|
||||
|
||||
if (!inject_on_main(pid, lib_path)) {
|
||||
LOGE("failed to inject");
|
||||
578
loader/src/ptracer/utils.c
Normal file
578
loader/src/ptracer/utils.c
Normal file
@@ -0,0 +1,578 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/uio.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sched.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
bool switch_mnt_ns(int pid, int *fd) {
|
||||
int nsfd, old_nsfd = -1;
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (pid == 0) {
|
||||
if (fd != NULL) {
|
||||
nsfd = *fd;
|
||||
*fd = -1;
|
||||
} else return false;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/self/fd/%d", nsfd);
|
||||
} else {
|
||||
if (fd != NULL) {
|
||||
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
|
||||
if (old_nsfd == -1) {
|
||||
PLOGE("get old nsfd");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
*fd = old_nsfd;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
|
||||
nsfd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (nsfd == -1) {
|
||||
PLOGE("open nsfd %s", path);
|
||||
|
||||
close(old_nsfd);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (setns(nsfd, CLONE_NEWNS) == -1) {
|
||||
PLOGE("set ns to %s", path);
|
||||
|
||||
close(nsfd);
|
||||
close(old_nsfd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
close(nsfd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct maps *parse_maps(const char *filename) {
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (!fp) {
|
||||
LOGE("Failed to open %s", filename);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct maps *maps = (struct maps *)malloc(sizeof(struct maps));
|
||||
if (!maps) {
|
||||
LOGE("Failed to allocate memory for maps");
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char line[4096 * 2];
|
||||
size_t i = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
/* INFO: Remove line ending at the end */
|
||||
line[strlen(line) - 1] = '\0';
|
||||
|
||||
uintptr_t addr_start;
|
||||
uintptr_t addr_end;
|
||||
uintptr_t addr_offset;
|
||||
ino_t inode;
|
||||
unsigned int dev_major;
|
||||
unsigned int dev_minor;
|
||||
char permissions[5] = "";
|
||||
int path_offset;
|
||||
|
||||
sscanf(line,
|
||||
"%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s",
|
||||
&addr_start, &addr_end, permissions, &addr_offset, &dev_major, &dev_minor,
|
||||
&inode, &path_offset);
|
||||
|
||||
while (isspace(line[path_offset])) {
|
||||
path_offset++;
|
||||
}
|
||||
|
||||
maps->maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
|
||||
if (!maps->maps) {
|
||||
LOGE("Failed to allocate memory for maps->maps");
|
||||
|
||||
maps->size = i;
|
||||
|
||||
fclose(fp);
|
||||
free_maps(maps);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
maps->maps[i].start = addr_start;
|
||||
maps->maps[i].end = addr_end;
|
||||
maps->maps[i].offset = addr_offset;
|
||||
|
||||
maps->maps[i].perms = 0;
|
||||
if (permissions[0] == 'r') maps->maps[i].perms |= PROT_READ;
|
||||
if (permissions[1] == 'w') maps->maps[i].perms |= PROT_WRITE;
|
||||
if (permissions[2] == 'x') maps->maps[i].perms |= PROT_EXEC;
|
||||
|
||||
maps->maps[i].is_private = permissions[3] == 'p';
|
||||
maps->maps[i].dev = makedev(dev_major, dev_minor);
|
||||
maps->maps[i].inode = inode;
|
||||
maps->maps[i].path = strdup(line + path_offset);
|
||||
if (!maps->maps[i].path) {
|
||||
LOGE("Failed to allocate memory for maps->maps[%zu].path", i);
|
||||
|
||||
maps->size = i;
|
||||
|
||||
fclose(fp);
|
||||
free_maps(maps);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
maps->size = i;
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
void free_maps(struct maps *maps) {
|
||||
if (!maps) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < maps->size; i++) {
|
||||
free((void *)maps->maps[i].path);
|
||||
}
|
||||
|
||||
free(maps->maps);
|
||||
free(maps);
|
||||
}
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len) {
|
||||
LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len);
|
||||
|
||||
struct iovec local = {
|
||||
.iov_base = (void *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
struct iovec remote = {
|
||||
.iov_base = (void *)remote_addr,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
ssize_t l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) PLOGE("process_vm_writev");
|
||||
else if ((size_t)l != len) LOGW("not fully written: %zu, excepted %zu", l, len);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len) {
|
||||
struct iovec local = {
|
||||
.iov_base = (void *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
struct iovec remote = {
|
||||
.iov_base = (void *)remote_addr,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
ssize_t l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) PLOGE("process_vm_readv");
|
||||
else if ((size_t)l != len) LOGW("not fully read: %zu, excepted %zu", l, len);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool get_regs(int pid, struct user_regs_struct *regs) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
if (ptrace(PTRACE_GETREGS, pid, 0, regs) == -1) {
|
||||
PLOGE("getregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#elif defined(__aarch64__) || defined(__arm__)
|
||||
struct iovec iov = {
|
||||
.iov_base = regs,
|
||||
.iov_len = sizeof(struct user_regs_struct),
|
||||
};
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||
PLOGE("getregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_regs(int pid, struct user_regs_struct *regs) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
if (ptrace(PTRACE_SETREGS, pid, 0, regs) == -1) {
|
||||
PLOGE("setregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#elif defined(__aarch64__) || defined(__arm__)
|
||||
struct iovec iov = {
|
||||
.iov_base = regs,
|
||||
.iov_len = sizeof(struct user_regs_struct),
|
||||
};
|
||||
|
||||
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||
PLOGE("setregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_addr_mem_region(struct maps *info, uintptr_t addr, char *buf, size_t buf_size) {
|
||||
for (size_t i = 0; i < info->size; i++) {
|
||||
/* TODO: Early "leave" */
|
||||
if (info->maps[i].start <= addr && info->maps[i].end > addr) {
|
||||
snprintf(buf, buf_size, "%s %s%s%s",
|
||||
info->maps[i].path,
|
||||
info->maps[i].perms & PROT_READ ? "r" : "-",
|
||||
info->maps[i].perms & PROT_WRITE ? "w" : "-",
|
||||
info->maps[i].perms & PROT_EXEC ? "x" : "-");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(buf, buf_size, "<unknown>");
|
||||
}
|
||||
|
||||
/* INFO: strrchr but without modifying the string */
|
||||
const char *position_after(const char *str, const char needle) {
|
||||
const char *positioned = str + strlen(str);
|
||||
|
||||
int i = strlen(str);
|
||||
while (i != 0) {
|
||||
i--;
|
||||
if (str[i] == needle) {
|
||||
positioned = str + i + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return positioned;
|
||||
}
|
||||
|
||||
void *find_module_return_addr(struct maps *map, const char *suffix) {
|
||||
for (size_t i = 0; i < map->size; i++) {
|
||||
/* TODO: Make it NULL in 1 length path */
|
||||
if (map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *file_name = position_after(map->maps[i].path, '/');
|
||||
if (!file_name) continue;
|
||||
|
||||
if (strlen(file_name) < strlen(suffix) || (map->maps[i].perms & PROT_EXEC) != 0 || strncmp(file_name, suffix, strlen(suffix)) != 0) continue;
|
||||
|
||||
return (void *)map->maps[i].start;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *find_module_base(struct maps *map, const char *suffix) {
|
||||
for (size_t i = 0; i < map->size; i++) {
|
||||
/* TODO: Make it NULL in 1 length path */
|
||||
if (map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *file_name = position_after(map->maps[i].path, '/');
|
||||
if (!file_name) continue;
|
||||
|
||||
if (strlen(file_name) < strlen(suffix) || map->maps[i].offset != 0 || strncmp(file_name, suffix, strlen(suffix)) != 0) continue;
|
||||
|
||||
return (void *)map->maps[i].start;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *find_func_addr(struct maps *local_info, struct maps *remote_info, const char *module, const char *func) {
|
||||
void *lib = dlopen(module, RTLD_NOW);
|
||||
if (lib == NULL) {
|
||||
LOGE("failed to open lib %s: %s", module, dlerror());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *sym = (uint8_t *)dlsym(lib, func);
|
||||
if (sym == NULL) {
|
||||
LOGE("failed to find sym %s in %s: %s", func, module, dlerror());
|
||||
|
||||
dlclose(lib);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGD("sym %s: %p", func, sym);
|
||||
|
||||
dlclose(lib);
|
||||
|
||||
uint8_t *local_base = (uint8_t *)find_module_base(local_info, module);
|
||||
if (local_base == NULL) {
|
||||
LOGE("failed to find local base for module %s", module);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *remote_base = (uint8_t *)find_module_base(remote_info, module);
|
||||
if (remote_base == NULL) {
|
||||
LOGE("failed to find remote base for module %s", module);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGD("found local base %p remote base %p", local_base, remote_base);
|
||||
|
||||
uint8_t *addr = (sym - local_base) + remote_base;
|
||||
LOGD("addr %p", addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void align_stack(struct user_regs_struct *regs, long preserve) {
|
||||
/* INFO: ~0xf is a negative value, and REG_SP is unsigned,
|
||||
so we must cast REG_SP to signed type before subtracting
|
||||
then cast back to unsigned type.
|
||||
*/
|
||||
regs->REG_SP = (uintptr_t)((intptr_t)(regs->REG_SP - preserve) & ~0xf);
|
||||
}
|
||||
|
||||
uintptr_t push_string(int pid, struct user_regs_struct *regs, const char *str) {
|
||||
size_t len = strlen(str) + 1;
|
||||
|
||||
regs->REG_SP -= len;
|
||||
|
||||
align_stack(regs, 0);
|
||||
|
||||
uintptr_t addr = (uintptr_t)regs->REG_SP;
|
||||
if (!write_proc(pid, addr, str, len)) LOGE("failed to write string %s", str);
|
||||
|
||||
LOGD("pushed string %" PRIxPTR, addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct *regs, uintptr_t func_addr, uintptr_t return_addr, long *args, size_t args_size) {
|
||||
align_stack(regs, 0);
|
||||
|
||||
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args_size);
|
||||
|
||||
for (size_t i = 0; i < args_size; i++) {
|
||||
LOGV("arg %p", (void *)args[i]);
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
if (args_size >= 1) regs->rdi = args[0];
|
||||
if (args_size >= 2) regs->rsi = args[1];
|
||||
if (args_size >= 3) regs->rdx = args[2];
|
||||
if (args_size >= 4) regs->rcx = args[3];
|
||||
if (args_size >= 5) regs->r8 = args[4];
|
||||
if (args_size >= 6) regs->r9 = args[5];
|
||||
if (args_size > 6) {
|
||||
long remain = (args_size - 6L) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs->REG_SP, args, remain)) LOGE("failed to push arguments");
|
||||
}
|
||||
|
||||
regs->REG_SP -= sizeof(long);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs->REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr");
|
||||
|
||||
regs->REG_IP = func_addr;
|
||||
#elif defined(__i386__)
|
||||
if (args_size > 0) {
|
||||
long remain = (args_size) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs->REG_SP, args, remain)) LOGE("failed to push arguments");
|
||||
}
|
||||
|
||||
regs->REG_SP -= sizeof(long);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs->REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr");
|
||||
|
||||
regs->REG_IP = func_addr;
|
||||
#elif defined(__aarch64__)
|
||||
for (size_t i = 0; i < args_size && i < 8; i++) {
|
||||
regs->regs[i] = args[i];
|
||||
}
|
||||
|
||||
if (args_size > 8) {
|
||||
long remain = (args_size - 8) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
write_proc(pid, (uintptr_t)regs->REG_SP, args, remain);
|
||||
}
|
||||
|
||||
regs->regs[30] = return_addr;
|
||||
regs->REG_IP = func_addr;
|
||||
#elif defined(__arm__)
|
||||
for (size_t i = 0; i < args_size && i < 4; i++) {
|
||||
regs->uregs[i] = args[i];
|
||||
}
|
||||
|
||||
if (args_size > 4) {
|
||||
long remain = (args_size - 4) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
write_proc(pid, (uintptr_t)regs->REG_SP, args, remain);
|
||||
}
|
||||
|
||||
regs->uregs[14] = return_addr;
|
||||
regs->REG_IP = func_addr;
|
||||
|
||||
unsigned long CPSR_T_MASK = 1lu << 5;
|
||||
|
||||
if ((regs->REG_IP & 1) != 0) {
|
||||
regs->REG_IP = regs->REG_IP & ~1;
|
||||
regs->uregs[16] = regs->uregs[16] | CPSR_T_MASK;
|
||||
} else {
|
||||
regs->uregs[16] = regs->uregs[16] & ~CPSR_T_MASK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!set_regs(pid, regs)) {
|
||||
LOGE("failed to set regs");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
int status;
|
||||
wait_for_trace(pid, &status, __WALL);
|
||||
if (!get_regs(pid, regs)) {
|
||||
LOGE("failed to get regs after call");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (WSTOPSIG(status) == SIGSEGV) {
|
||||
if ((uintptr_t)regs->REG_IP != return_addr) {
|
||||
LOGE("wrong return addr %p", (void *) regs->REG_IP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return regs->REG_RET;
|
||||
} else {
|
||||
char status_str[64];
|
||||
parse_status(status, status_str, sizeof(status_str));
|
||||
|
||||
LOGE("stopped by other reason %s at addr %p", status_str, (void *)regs->REG_IP);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fork_dont_care() {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid < 0) PLOGE("fork 1");
|
||||
else if (pid == 0) {
|
||||
pid = fork();
|
||||
if (pid < 0) PLOGE("fork 2");
|
||||
else if (pid > 0) exit(0);
|
||||
} else {
|
||||
int status;
|
||||
waitpid(pid, &status, __WALL);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void wait_for_trace(int pid, int *status, int flags) {
|
||||
while (1) {
|
||||
pid_t result = waitpid(pid, status, flags);
|
||||
if (result == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
|
||||
PLOGE("wait %d failed", pid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(*status)) {
|
||||
char status_str[64];
|
||||
parse_status(*status, status_str, sizeof(status_str));
|
||||
|
||||
LOGE("process %d not stopped for trace: %s, exit", pid, status_str);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_status(int status, char *buf, size_t len) {
|
||||
snprintf(buf, len, "0x%x ", status);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "stopped by ");
|
||||
|
||||
int stop_sig = WSTOPSIG(status);
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "signal=%s(%d),", sigabbrev_np(stop_sig), stop_sig);
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status));
|
||||
} else {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
int get_program(int pid, char *buf, size_t size) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
|
||||
|
||||
ssize_t sz = readlink(path, buf, size);
|
||||
|
||||
if (sz == -1) {
|
||||
PLOGE("readlink /proc/%d/exe", pid);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[sz] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,528 +0,0 @@
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <vector>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <sys/ptrace.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <dlfcn.h>
|
||||
#include <signal.h>
|
||||
#include <sstream>
|
||||
#include <ios>
|
||||
#include <cstring>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sched.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "logging.h"
|
||||
|
||||
bool switch_mnt_ns(int pid, int *fd) {
|
||||
int nsfd, old_nsfd = -1;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
char path[PATH_MAX];
|
||||
if (pid == 0) {
|
||||
if (fd != NULL) {
|
||||
nsfd = *fd;
|
||||
*fd = -1;
|
||||
} else return false;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/self/fd/%d", nsfd);
|
||||
} else {
|
||||
if (fd != NULL) {
|
||||
old_nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
|
||||
if (old_nsfd == -1) {
|
||||
PLOGE("get old nsfd");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
*fd = old_nsfd;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
|
||||
|
||||
nsfd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (nsfd == -1) {
|
||||
PLOGE("open nsfd %s", path);
|
||||
|
||||
close(old_nsfd);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (setns(nsfd, CLONE_NEWNS) == -1) {
|
||||
PLOGE("set ns to %s", path);
|
||||
|
||||
close(nsfd);
|
||||
close(old_nsfd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
close(nsfd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::vector<MapInfo> MapInfo::Scan(const std::string &pid) {
|
||||
constexpr static auto kPermLength = 5;
|
||||
constexpr static auto kMapEntry = 7;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::vector<MapInfo> info;
|
||||
char file_name[NAME_MAX];
|
||||
snprintf(file_name, sizeof(file_name), "/proc/%s/maps", pid.c_str());
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
auto maps = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file_name, "r"), &fclose};
|
||||
if (maps) {
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
while ((read = getline(&line, &len, maps.get())) > 0) {
|
||||
line[read - 1] = '\0';
|
||||
|
||||
uintptr_t start = 0;
|
||||
uintptr_t end = 0;
|
||||
uintptr_t off = 0;
|
||||
ino_t inode = 0;
|
||||
unsigned int dev_major = 0;
|
||||
unsigned int dev_minor = 0;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::array<char, kPermLength> perm {'\0'};
|
||||
|
||||
int path_off;
|
||||
if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start,
|
||||
&end, perm.data(), &off, &dev_major, &dev_minor, &inode,
|
||||
&path_off) != kMapEntry) continue;
|
||||
|
||||
while (path_off < read && isspace(line[path_off])) path_off++;
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
MapInfo &ref = info.emplace_back(MapInfo{
|
||||
start,
|
||||
end,
|
||||
0,
|
||||
perm[3] == 'p',
|
||||
off,
|
||||
static_cast<dev_t>(makedev(dev_major, dev_minor)),
|
||||
inode,
|
||||
line + path_off
|
||||
});
|
||||
|
||||
if (perm[0] == 'r') ref.perms |= PROT_READ;
|
||||
if (perm[1] == 'w') ref.perms |= PROT_WRITE;
|
||||
if (perm[2] == 'x') ref.perms |= PROT_EXEC;
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len) {
|
||||
LOGV("write to remote addr %" PRIxPTR " size %zu", remote_addr, len);
|
||||
|
||||
struct iovec local = {
|
||||
.iov_base = (void *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
struct iovec remote = {
|
||||
.iov_base = (void *)remote_addr,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
ssize_t l = process_vm_writev(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) PLOGE("process_vm_writev");
|
||||
else if ((size_t)l != len) LOGW("not fully written: %zu, excepted %zu", l, len);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len) {
|
||||
struct iovec local = {
|
||||
.iov_base = (void *)buf,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
struct iovec remote = {
|
||||
.iov_base = (void *)remote_addr,
|
||||
.iov_len = len
|
||||
};
|
||||
|
||||
ssize_t l = process_vm_readv(pid, &local, 1, &remote, 1, 0);
|
||||
if (l == -1) PLOGE("process_vm_readv");
|
||||
else if ((size_t)l != len) LOGW("not fully read: %zu, excepted %zu", l, len);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool get_regs(int pid, struct user_regs_struct ®s) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) {
|
||||
PLOGE("getregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#elif defined(__aarch64__) || defined(__arm__)
|
||||
struct iovec iov = {
|
||||
.iov_base = ®s,
|
||||
.iov_len = sizeof(struct user_regs_struct),
|
||||
};
|
||||
|
||||
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||
PLOGE("getregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_regs(int pid, struct user_regs_struct ®s) {
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) {
|
||||
PLOGE("setregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#elif defined(__aarch64__) || defined(__arm__)
|
||||
struct iovec iov = {
|
||||
.iov_base = ®s,
|
||||
.iov_len = sizeof(struct user_regs_struct),
|
||||
};
|
||||
|
||||
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov) == -1) {
|
||||
PLOGE("setregs");
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr) {
|
||||
/* WARNING: C++ keyword */
|
||||
for (auto &map: info) {
|
||||
if (map.start <= addr && map.end > addr) {
|
||||
/* WARNING: C++ keyword */
|
||||
auto s = std::string(map.path);
|
||||
|
||||
s += ' ';
|
||||
s += map.perms & PROT_READ ? 'r' : '-';
|
||||
s += map.perms & PROT_WRITE ? 'w' : '-';
|
||||
s += map.perms & PROT_EXEC ? 'x' : '-';
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
void *find_module_return_addr(std::vector<MapInfo> &info, std::string_view suffix) {
|
||||
/* WARNING: C++ keyword */
|
||||
for (auto &map: info) {
|
||||
/* WARNING: C++ keyword */
|
||||
if ((map.perms & PROT_EXEC) == 0 && map.path.ends_with(suffix)) return (void *)map.start;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix) {
|
||||
/* WARNING: C++ keyword */
|
||||
for (auto &map: info) {
|
||||
/* WARNING: C++ keyword */
|
||||
if (map.offset == 0 && map.path.ends_with(suffix)) return (void *)map.start;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
void *find_func_addr(std::vector<MapInfo> &local_info, std::vector<MapInfo> &remote_info, std::string_view module, std::string_view func) {
|
||||
void *lib = dlopen(module.data(), RTLD_NOW);
|
||||
if (lib == NULL) {
|
||||
LOGE("failed to open lib %s: %s", module.data(), dlerror());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *sym = (uint8_t *)dlsym(lib, func.data());
|
||||
if (sym == NULL) {
|
||||
LOGE("failed to find sym %s in %s: %s", func.data(), module.data(), dlerror());
|
||||
|
||||
dlclose(lib);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGD("sym %s: %p", func.data(), sym);
|
||||
|
||||
dlclose(lib);
|
||||
|
||||
uint8_t *local_base = (uint8_t *)find_module_base(local_info, module);
|
||||
if (local_base == NULL) {
|
||||
LOGE("failed to find local base for module %s", module.data());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *remote_base = (uint8_t *)find_module_base(remote_info, module);
|
||||
if (remote_base == NULL) {
|
||||
LOGE("failed to find remote base for module %s", module.data());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOGD("found local base %p remote base %p", local_base, remote_base);
|
||||
|
||||
uint8_t *addr = (sym - local_base) + remote_base;
|
||||
LOGD("addr %p", addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
void align_stack(struct user_regs_struct ®s, long preserve) {
|
||||
/* INFO: ~0xf is a negative value, and REG_SP is unsigned,
|
||||
so we must cast REG_SP to signed type before subtracting
|
||||
then cast back to unsigned type.
|
||||
*/
|
||||
regs.REG_SP = (uintptr_t)((intptr_t)(regs.REG_SP - preserve) & ~0xf);
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str) {
|
||||
size_t len = strlen(str) + 1;
|
||||
|
||||
regs.REG_SP -= len;
|
||||
|
||||
align_stack(regs);
|
||||
|
||||
uintptr_t addr = (uintptr_t)regs.REG_SP;
|
||||
if (!write_proc(pid, addr, str, len)) LOGE("failed to write string %s", str);
|
||||
|
||||
LOGD("pushed string %" PRIxPTR, addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr, std::vector<long> &args) {
|
||||
align_stack(regs);
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
LOGV("calling remote function %" PRIxPTR " args %zu", func_addr, args.size());
|
||||
|
||||
/* WARNING: C++ keyword */
|
||||
for (auto &a: args) {
|
||||
LOGV("arg %p", (void *) a);
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
if (args.size() >= 1) regs.rdi = args[0];
|
||||
if (args.size() >= 2) regs.rsi = args[1];
|
||||
if (args.size() >= 3) regs.rdx = args[2];
|
||||
if (args.size() >= 4) regs.rcx = args[3];
|
||||
if (args.size() >= 5) regs.r8 = args[4];
|
||||
if (args.size() >= 6) regs.r9 = args[5];
|
||||
if (args.size() > 6) {
|
||||
long remain = (args.size() - 6L) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments");
|
||||
}
|
||||
|
||||
regs.REG_SP -= sizeof(long);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr");
|
||||
|
||||
regs.REG_IP = func_addr;
|
||||
#elif defined(__i386__)
|
||||
if (args.size() > 0) {
|
||||
long remain = (args.size()) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, args.data(), remain)) LOGE("failed to push arguments");
|
||||
}
|
||||
|
||||
regs.REG_SP -= sizeof(long);
|
||||
|
||||
if (!write_proc(pid, (uintptr_t) regs.REG_SP, &return_addr, sizeof(return_addr))) LOGE("failed to write return addr");
|
||||
|
||||
regs.REG_IP = func_addr;
|
||||
#elif defined(__aarch64__)
|
||||
for (size_t i = 0; i < args.size() && i < 8; i++) {
|
||||
regs.regs[i] = args[i];
|
||||
}
|
||||
|
||||
if (args.size() > 8) {
|
||||
long remain = (args.size() - 8) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain);
|
||||
}
|
||||
|
||||
regs.regs[30] = return_addr;
|
||||
regs.REG_IP = func_addr;
|
||||
#elif defined(__arm__)
|
||||
for (size_t i = 0; i < args.size() && i < 4; i++) {
|
||||
regs.uregs[i] = args[i];
|
||||
}
|
||||
|
||||
if (args.size() > 4) {
|
||||
long remain = (args.size() - 4) * sizeof(long);
|
||||
align_stack(regs, remain);
|
||||
|
||||
write_proc(pid, (uintptr_t)regs.REG_SP, args.data(), remain);
|
||||
}
|
||||
|
||||
regs.uregs[14] = return_addr;
|
||||
regs.REG_IP = func_addr;
|
||||
|
||||
constexpr auto CPSR_T_MASK = 1lu << 5;
|
||||
|
||||
if ((regs.REG_IP & 1) != 0) {
|
||||
regs.REG_IP = regs.REG_IP & ~1;
|
||||
regs.uregs[16] = regs.uregs[16] | CPSR_T_MASK;
|
||||
} else {
|
||||
regs.uregs[16] = regs.uregs[16] & ~CPSR_T_MASK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!set_regs(pid, regs)) {
|
||||
LOGE("failed to set regs");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptrace(PTRACE_CONT, pid, 0, 0);
|
||||
|
||||
int status;
|
||||
wait_for_trace(pid, &status, __WALL);
|
||||
if (!get_regs(pid, regs)) {
|
||||
LOGE("failed to get regs after call");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (WSTOPSIG(status) == SIGSEGV) {
|
||||
if ((uintptr_t)regs.REG_IP != return_addr) {
|
||||
LOGE("wrong return addr %p", (void *) regs.REG_IP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return regs.REG_RET;
|
||||
} else {
|
||||
char status_str[64];
|
||||
parse_status(status, status_str, sizeof(status_str));
|
||||
|
||||
LOGE("stopped by other reason %s at addr %p", status_str, (void *)regs.REG_IP);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fork_dont_care() {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid < 0) PLOGE("fork 1");
|
||||
else if (pid == 0) {
|
||||
pid = fork();
|
||||
if (pid < 0) PLOGE("fork 2");
|
||||
else if (pid > 0) exit(0);
|
||||
} else {
|
||||
int status;
|
||||
waitpid(pid, &status, __WALL);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void wait_for_trace(int pid, int *status, int flags) {
|
||||
while (1) {
|
||||
pid_t result = waitpid(pid, status, flags);
|
||||
if (result == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
|
||||
PLOGE("wait %d failed", pid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!WIFSTOPPED(*status)) {
|
||||
char status_str[64];
|
||||
parse_status(*status, status_str, sizeof(status_str));
|
||||
|
||||
LOGE("process %d not stopped for trace: %s, exit", pid, status_str);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void parse_status(int status, char *buf, size_t len) {
|
||||
snprintf(buf, len, "0x%x ", status);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "exited with %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "signaled with %s(%d)", sigabbrev_np(WTERMSIG(status)), WTERMSIG(status));
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "stopped by ");
|
||||
|
||||
int stop_sig = WSTOPSIG(status);
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "signal=%s(%d),", sigabbrev_np(stop_sig), stop_sig);
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "event=%s", parse_ptrace_event(status));
|
||||
} else {
|
||||
snprintf(buf + strlen(buf), len - strlen(buf), "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
int get_program(int pid, char *buf, size_t size) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
|
||||
|
||||
ssize_t sz = readlink(path, buf, size);
|
||||
|
||||
if (sz == -1) {
|
||||
PLOGE("readlink /proc/%d/exe", pid);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[sz] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
109
loader/src/ptracer/utils.h
Normal file
109
loader/src/ptracer/utils.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
struct map {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
uint8_t perms;
|
||||
bool is_private;
|
||||
uintptr_t offset;
|
||||
dev_t dev;
|
||||
ino_t inode;
|
||||
const char *path;
|
||||
};
|
||||
|
||||
struct maps {
|
||||
struct map *maps;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct maps *parse_maps(const char *filename);
|
||||
|
||||
void free_maps(struct maps *maps);
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define REG_SP rsp
|
||||
#define REG_IP rip
|
||||
#define REG_RET rax
|
||||
#elif defined(__i386__)
|
||||
#define REG_SP esp
|
||||
#define REG_IP eip
|
||||
#define REG_RET eax
|
||||
#elif defined(__aarch64__)
|
||||
#define REG_SP sp
|
||||
#define REG_IP pc
|
||||
#define REG_RET regs[0]
|
||||
#elif defined(__arm__)
|
||||
#define REG_SP uregs[13]
|
||||
#define REG_IP uregs[15]
|
||||
#define REG_RET uregs[0]
|
||||
#define user_regs_struct user_regs
|
||||
#endif
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len);
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len);
|
||||
|
||||
bool get_regs(int pid, struct user_regs_struct *regs);
|
||||
|
||||
bool set_regs(int pid, struct user_regs_struct *regs);
|
||||
|
||||
void get_addr_mem_region(struct maps *map, uintptr_t addr, char *buf, size_t buf_size);
|
||||
|
||||
void *find_module_return_addr(struct maps *map, const char *suffix);
|
||||
|
||||
void *find_func_addr(struct maps *local_info, struct maps *remote_info, const char *module, const char *func);
|
||||
|
||||
void align_stack(struct user_regs_struct *regs, long preserve);
|
||||
|
||||
uintptr_t push_string(int pid, struct user_regs_struct *regs, const char *str);
|
||||
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct *regs, uintptr_t func_addr, uintptr_t return_addr, long *args, size_t args_size);
|
||||
|
||||
int fork_dont_care();
|
||||
|
||||
void wait_for_trace(int pid, int* status, int flags);
|
||||
|
||||
void parse_status(int status, char *buf, size_t len);
|
||||
|
||||
#define WPTEVENT(x) (x >> 16)
|
||||
|
||||
#define CASE_CONST_RETURN(x) case x: return #x;
|
||||
|
||||
static inline const char *parse_ptrace_event(int status) {
|
||||
status = status >> 16;
|
||||
|
||||
switch (status) {
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_FORK)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_VFORK)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_CLONE)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_EXEC)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_VFORK_DONE)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_EXIT)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_SECCOMP)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_STOP)
|
||||
default:
|
||||
return "(no event)";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *sigabbrev_np(int sig) {
|
||||
if (sig > 0 && sig < NSIG) return sys_signame[sig];
|
||||
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
int get_program(int pid, char *buf, size_t size);
|
||||
|
||||
/* INFO: pid = 0, fd != nullptr -> set to fd
|
||||
pid != 0, fd != nullptr -> set to pid ns, give orig ns in fd
|
||||
*/
|
||||
bool switch_mnt_ns(int pid, int *fd);
|
||||
|
||||
#endif /* UTILS_H */
|
||||
@@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <sys/ptrace.h>
|
||||
#include <map>
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
#ifdef __LP64__
|
||||
#define LOG_TAG "zygisk-ptrace64"
|
||||
#else
|
||||
#define LOG_TAG "zygisk-ptrace32"
|
||||
#endif
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
struct MapInfo {
|
||||
/// \brief The start address of the memory region.
|
||||
uintptr_t start;
|
||||
/// \brief The end address of the memory region.
|
||||
uintptr_t end;
|
||||
/// \brief The permissions of the memory region. This is a bit mask of the following values:
|
||||
/// - PROT_READ
|
||||
/// - PROT_WRITE
|
||||
/// - PROT_EXEC
|
||||
uint8_t perms;
|
||||
/// \brief Whether the memory region is private.
|
||||
bool is_private;
|
||||
/// \brief The offset of the memory region.
|
||||
uintptr_t offset;
|
||||
/// \brief The device number of the memory region.
|
||||
/// Major can be obtained by #major()
|
||||
/// Minor can be obtained by #minor()
|
||||
dev_t dev;
|
||||
/// \brief The inode number of the memory region.
|
||||
ino_t inode;
|
||||
/// \brief The path of the memory region.
|
||||
std::string path;
|
||||
|
||||
/// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries.
|
||||
/// This is useful to find out the inode of the library to hook.
|
||||
/// \return A list of \ref MapInfo entries.
|
||||
static std::vector<MapInfo> Scan(const std::string& pid = "self");
|
||||
};
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define REG_SP rsp
|
||||
#define REG_IP rip
|
||||
#define REG_RET rax
|
||||
#elif defined(__i386__)
|
||||
#define REG_SP esp
|
||||
#define REG_IP eip
|
||||
#define REG_RET eax
|
||||
#elif defined(__aarch64__)
|
||||
#define REG_SP sp
|
||||
#define REG_IP pc
|
||||
#define REG_RET regs[0]
|
||||
#elif defined(__arm__)
|
||||
#define REG_SP uregs[13]
|
||||
#define REG_IP uregs[15]
|
||||
#define REG_RET uregs[0]
|
||||
#define user_regs_struct user_regs
|
||||
#endif
|
||||
|
||||
ssize_t write_proc(int pid, uintptr_t remote_addr, const void *buf, size_t len);
|
||||
|
||||
ssize_t read_proc(int pid, uintptr_t remote_addr, void *buf, size_t len);
|
||||
|
||||
bool get_regs(int pid, struct user_regs_struct ®s);
|
||||
|
||||
bool set_regs(int pid, struct user_regs_struct ®s);
|
||||
|
||||
std::string get_addr_mem_region(std::vector<MapInfo> &info, uintptr_t addr);
|
||||
|
||||
void *find_module_base(std::vector<MapInfo> &info, std::string_view suffix);
|
||||
|
||||
void *find_func_addr(
|
||||
std::vector<MapInfo> &local_info,
|
||||
std::vector<MapInfo> &remote_info,
|
||||
std::string_view module,
|
||||
std::string_view func);
|
||||
|
||||
void align_stack(struct user_regs_struct ®s, long preserve = 0);
|
||||
|
||||
uintptr_t push_string(int pid, struct user_regs_struct ®s, const char *str);
|
||||
|
||||
uintptr_t remote_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr, uintptr_t return_addr,
|
||||
std::vector<long> &args);
|
||||
|
||||
int fork_dont_care();
|
||||
|
||||
void wait_for_trace(int pid, int* status, int flags);
|
||||
|
||||
void parse_status(int status, char *buf, size_t len);
|
||||
|
||||
#define WPTEVENT(x) (x >> 16)
|
||||
|
||||
#define CASE_CONST_RETURN(x) case x: return #x;
|
||||
|
||||
inline const char* parse_ptrace_event(int status) {
|
||||
status = status >> 16;
|
||||
switch (status) {
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_FORK)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_VFORK)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_CLONE)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_EXEC)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_VFORK_DONE)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_EXIT)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_SECCOMP)
|
||||
CASE_CONST_RETURN(PTRACE_EVENT_STOP)
|
||||
default:
|
||||
return "(no event)";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* sigabbrev_np(int sig) {
|
||||
if (sig > 0 && sig < NSIG) return sys_signame[sig];
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
int get_program(int pid, char *buf, size_t size);
|
||||
void *find_module_return_addr(std::vector<MapInfo> &info, std::string_view suffix);
|
||||
|
||||
// pid = 0, fd != nullptr -> set to fd
|
||||
// pid != 0, fd != nullptr -> set to pid ns, give orig ns in fd
|
||||
bool switch_mnt_ns(int pid, int *fd);
|
||||
@@ -58,34 +58,6 @@ static enum Architecture get_arch(void) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int create_library_fd(const char *restrict so_path) {
|
||||
int so_fd = open(so_path, O_RDONLY);
|
||||
if (so_fd == -1) {
|
||||
LOGE("Failed opening so file: %s\n", strerror(errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t so_size = lseek(so_fd, 0, SEEK_END);
|
||||
if (so_size == -1) {
|
||||
LOGE("Failed getting so file size: %s\n", strerror(errno));
|
||||
|
||||
close(so_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek(so_fd, 0, SEEK_SET) == -1) {
|
||||
LOGE("Failed seeking so file: %s\n", strerror(errno));
|
||||
|
||||
close(so_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return so_fd;
|
||||
}
|
||||
|
||||
/* WARNING: Dynamic memory based */
|
||||
static void load_modules(enum Architecture arch, struct Context *restrict context) {
|
||||
context->len = 0;
|
||||
@@ -138,7 +110,7 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
|
||||
errno = 0;
|
||||
} else continue;
|
||||
|
||||
int lib_fd = create_library_fd(so_path);
|
||||
int lib_fd = open(so_path, O_RDONLY | O_CLOEXEC);
|
||||
if (lib_fd == -1) {
|
||||
LOGE("Failed loading module `%s`\n", name);
|
||||
|
||||
@@ -556,12 +528,6 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (write_string(client_fd, context.modules[i].name) == -1) {
|
||||
LOGE("Failed writing module name.\n");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user