diff --git a/loader/src/common/socket_utils.c b/loader/src/common/socket_utils.c index ebca7ff..4ff72f7 100644 --- a/loader/src/common/socket_utils.c +++ b/loader/src/common/socket_utils.c @@ -8,6 +8,50 @@ #include "socket_utils.h" +ssize_t write_loop(int fd, const void *buf, size_t count) { + ssize_t written = 0; + while (written < (ssize_t)count) { + ssize_t ret = write(fd, (const char *)buf + written, count - written); + if (ret == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + + PLOGE("write"); + } + + if (ret == 0) { + LOGE("write: 0 bytes written"); + + return -1; + } + + written += ret; + } + + return written; +} + +ssize_t read_loop(int fd, void *buf, size_t count) { + ssize_t read_bytes = 0; + while (read_bytes < (ssize_t)count) { + ssize_t ret = read(fd, (char *)buf + read_bytes, count - read_bytes); + if (ret == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + + PLOGE("read"); + } + + if (ret == 0) { + LOGE("read: 0 bytes read"); + + return -1; + } + + read_bytes += ret; + } + + return read_bytes; +} + /* TODO: Standardize how to log errors */ int read_fd(int fd) { char cmsgbuf[CMSG_SPACE(sizeof(int))]; @@ -47,14 +91,14 @@ int read_fd(int fd) { ssize_t write_string(int fd, const char *str) { size_t str_len = strlen(str); - ssize_t write_bytes = TEMP_FAILURE_RETRY(write(fd, &str_len, sizeof(size_t))); + ssize_t write_bytes = write_loop(fd, &str_len, sizeof(size_t)); if (write_bytes != (ssize_t)sizeof(size_t)) { LOGE("Failed to write string length: Not all bytes were written (%zd != %zu).\n", write_bytes, sizeof(size_t)); return -1; } - write_bytes = TEMP_FAILURE_RETRY(write(fd, str, str_len)); + write_bytes = write_loop(fd, str, str_len); if (write_bytes != (ssize_t)str_len) { LOGE("Failed to write string: Promised bytes doesn't exist (%zd != %zu).\n", write_bytes, str_len); @@ -66,7 +110,7 @@ ssize_t write_string(int fd, const char *str) { char *read_string(int fd) { size_t str_len = 0; - ssize_t read_bytes = TEMP_FAILURE_RETRY(read(fd, &str_len, sizeof(size_t))); + ssize_t read_bytes = read_loop(fd, &str_len, sizeof(size_t)); if (read_bytes != (ssize_t)sizeof(size_t)) { LOGE("Failed to read string length: Not all bytes were read (%zd != %zu).\n", read_bytes, sizeof(size_t)); @@ -80,7 +124,7 @@ char *read_string(int fd) { return NULL; } - read_bytes = TEMP_FAILURE_RETRY(read(fd, buf, str_len)); + read_bytes = read_loop(fd, buf, str_len); if (read_bytes != (ssize_t)str_len) { LOGE("Failed to read string: Promised bytes doesn't exist (%zd != %zu).\n", read_bytes, str_len); @@ -94,14 +138,14 @@ char *read_string(int fd) { return buf; } -#define write_func(type) \ - ssize_t write_## type(int fd, type val) { \ - return TEMP_FAILURE_RETRY(write(fd, &val, sizeof(type))); \ +#define write_func(type) \ + ssize_t write_## type(int fd, type val) { \ + return write_loop(fd, &val, sizeof(type)); \ } -#define read_func(type) \ - ssize_t read_## type(int fd, type *val) { \ - return TEMP_FAILURE_RETRY(read(fd, val, sizeof(type))); \ +#define read_func(type) \ + ssize_t read_## type(int fd, type *val) { \ + return read_loop(fd, val, sizeof(type)); \ } write_func(uint8_t) diff --git a/loader/src/include/socket_utils.h b/loader/src/include/socket_utils.h index 8891335..bb5cae1 100644 --- a/loader/src/include/socket_utils.h +++ b/loader/src/include/socket_utils.h @@ -3,6 +3,10 @@ #include +ssize_t write_loop(int fd, const void *buf, size_t count); + +ssize_t read_loop(int fd, void *buf, size_t count); + int read_fd(int fd); ssize_t write_string(int fd, const char *str); diff --git a/loader/src/ptracer/monitor.c b/loader/src/ptracer/monitor.c index 54e43ea..796aa1f 100644 --- a/loader/src/ptracer/monitor.c +++ b/loader/src/ptracer/monitor.c @@ -17,6 +17,7 @@ #include "utils.h" #include "daemon.h" #include "misc.h" +#include "socket_utils.h" #include "monitor.h" @@ -29,6 +30,15 @@ static bool update_status(const char *message); char monitor_stop_reason[32]; +struct environment_information { + char *root_impl; + char **modules; + uint32_t modules_len; +}; + +static struct environment_information environment_information64; +static struct environment_information environment_information32; + enum ptracer_tracing_state { TRACING, STOPPING, @@ -158,57 +168,26 @@ bool rezygiskd_listener_init() { return true; } -struct __attribute__((__packed__)) MsgHead { - unsigned int cmd; - int length; -}; - void rezygiskd_listener_callback() { while (1) { - struct MsgHead msg = { 0 }; + uint8_t cmd; + ssize_t nread = TEMP_FAILURE_RETRY(read(monitor_sock_fd, &cmd, sizeof(cmd))); + if (nread == -1) { + PLOGE("read socket"); - size_t nread; - - again: - nread = read(monitor_sock_fd, &msg, sizeof(msg)); - if ((int)nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) goto again; - - PLOGE("read socket"); - - continue; - } - - char *msg_data = NULL; - - if (msg.length != 0) { - msg_data = malloc(msg.length); - if (!msg_data) { - LOGE("malloc msg data failed"); - - continue; - } - - again_msg_data: - nread = read(monitor_sock_fd, msg_data, msg.length); - if ((int)nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) goto again_msg_data; - - PLOGE("read socket"); - - free(msg_data); - - continue; - } + continue; } - switch (msg.cmd) { + switch (cmd) { case START: { - if (tracing_state == STOPPING) tracing_state = TRACING; - else if (tracing_state == STOPPED) { - ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); + if (tracing_state == STOPPING) { + LOGI("Continue tracing init"); - LOGI("start tracing init"); + tracing_state = TRACING; + } else if (tracing_state == STOPPED) { + LOGI("Start tracing init"); + + ptrace(PTRACE_SEIZE, 1, 0, PTRACE_O_TRACEFORK); tracing_state = TRACING; } @@ -219,7 +198,7 @@ void rezygiskd_listener_callback() { } case STOP: { if (tracing_state == TRACING) { - LOGI("stop tracing requested"); + LOGI("Stop tracing requested"); tracing_state = STOPPING; strcpy(monitor_stop_reason, "user requested"); @@ -231,7 +210,7 @@ void rezygiskd_listener_callback() { break; } case EXIT: { - LOGI("prepare for exit ..."); + LOGI("Prepare for exit ..."); tracing_state = EXITING; strcpy(monitor_stop_reason, "user requested"); @@ -241,97 +220,167 @@ void rezygiskd_listener_callback() { break; } - case ZYGOTE64_INJECTED: { - status64.zygote_injected = true; - - update_status(NULL); - - break; - } + case ZYGOTE64_INJECTED: 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 = strdup(msg_data); - if (!status64.daemon_info) { - PLOGE("malloc daemon64 info"); - - break; - } + LOGI("Received Zygote%s injected command", cmd == ZYGOTE64_INJECTED ? "64" : "32"); + + struct rezygiskd_status *status = cmd == ZYGOTE64_INJECTED ? &status64 : &status32; + status->zygote_injected = true; update_status(NULL); break; } + case DAEMON64_SET_INFO: case DAEMON32_SET_INFO: { - LOGD("received daemon32 info %s", msg_data); + LOGD("Received ReZygiskd%s info", cmd == DAEMON64_SET_INFO ? "64" : "32"); - if (status32.daemon_info) { - free(status32.daemon_info); - status32.daemon_info = NULL; - } - - status32.daemon_info = strdup(msg_data); - if (!status32.daemon_info) { - PLOGE("malloc daemon32 info"); - - break; - } - - 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 = strdup(msg_data); - if (!status64.daemon_error_info) { - PLOGE("malloc daemon64 error info"); + uint32_t root_impl_len; + if (read_uint32_t(monitor_sock_fd, &root_impl_len) != sizeof(root_impl_len)) { + LOGE("read ReZygiskd%s root impl len", cmd == DAEMON64_SET_INFO ? "64" : "32"); break; } + struct environment_information *environment_information = cmd == DAEMON64_SET_INFO ? &environment_information64 : &environment_information32; + if (environment_information->root_impl) { + LOGD("freeing old ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + free((void *)environment_information->root_impl); + environment_information->root_impl = NULL; + } + + environment_information->root_impl = malloc(root_impl_len + 1); + if (environment_information->root_impl == NULL) { + PLOGE("malloc ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + break; + } + + if (read_loop(monitor_sock_fd, (void *)environment_information->root_impl, root_impl_len) != (ssize_t)root_impl_len) { + LOGE("read ReZygiskd%s root impl", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + free((void *)environment_information->root_impl); + environment_information->root_impl = NULL; + + break; + } + + environment_information->root_impl[root_impl_len] = '\0'; + LOGD("ReZygiskd%s root impl: %s", cmd == DAEMON64_SET_INFO ? "64" : "32", environment_information->root_impl); + + if (read_uint32_t(monitor_sock_fd, &environment_information->modules_len) != sizeof(environment_information->modules_len)) { + LOGE("read ReZygiskd%s modules len", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + free((void *)environment_information->root_impl); + environment_information->root_impl = NULL; + + break; + } + + if (environment_information->modules) { + LOGD("freeing old ReZygiskd%s modules", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + for (size_t i = 0; i < environment_information->modules_len; i++) { + free((void *)environment_information->modules[i]); + } + + free((void *)environment_information->modules); + environment_information->modules = NULL; + } + + environment_information->modules = malloc(environment_information->modules_len * sizeof(char *)); + if (environment_information->modules == NULL) { + PLOGE("malloc ReZygiskd%s modules", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + free((void *)environment_information->root_impl); + environment_information->root_impl = NULL; + + break; + } + + for (size_t i = 0; i < environment_information->modules_len; i++) { + uint32_t module_name_len; + if (read_uint32_t(monitor_sock_fd, &module_name_len) != sizeof(module_name_len)) { + LOGE("read ReZygiskd%s module name len", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + goto rezygiskd64_set_info_modules_cleanup; + } + + environment_information->modules[i] = malloc(module_name_len + 1); + if (environment_information->modules[i] == NULL) { + PLOGE("malloc ReZygiskd%s module name", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + goto rezygiskd64_set_info_modules_cleanup; + } + + if (read_loop(monitor_sock_fd, (void *)environment_information->modules[i], module_name_len) != (ssize_t)module_name_len) { + LOGE("read ReZygiskd%s module name", cmd == DAEMON64_SET_INFO ? "64" : "32"); + + goto rezygiskd64_set_info_modules_cleanup; + } + + environment_information->modules[i][module_name_len] = '\0'; + LOGD("ReZygiskd%s module %zu: %s", cmd == DAEMON64_SET_INFO ? "64" : "32", i, environment_information->modules[i]); + + continue; + + rezygiskd64_set_info_modules_cleanup: + free((void *)environment_information->root_impl); + environment_information->root_impl = NULL; + + for (size_t j = 0; j < i; j++) { + free((void *)environment_information->modules[j]); + } + + free((void *)environment_information->modules); + environment_information->modules = NULL; + + break; + } + update_status(NULL); break; } + case DAEMON64_SET_ERROR_INFO: case DAEMON32_SET_ERROR_INFO: { - LOGD("received daemon32 error info %s", msg_data); + LOGD("Received ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32"); - status32.daemon_running = false; - - if (status32.daemon_error_info) { - free(status32.daemon_error_info); - status32.daemon_error_info = NULL; - } - - status32.daemon_error_info = strdup(msg_data); - if (!status32.daemon_error_info) { - PLOGE("malloc daemon32 error info"); + uint32_t error_info_len; + if (read_uint32_t(monitor_sock_fd, &error_info_len) != sizeof(error_info_len)) { + LOGE("read ReZygiskd%s error info len", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32"); break; } + struct rezygiskd_status *status = cmd == DAEMON64_SET_ERROR_INFO ? &status64 : &status32; + if (status->daemon_error_info) { + LOGD("freeing old ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32"); + + free(status->daemon_error_info); + status->daemon_error_info = NULL; + } + + status->daemon_error_info = malloc(error_info_len + 1); + if (status->daemon_error_info == NULL) { + PLOGE("malloc ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32"); + + break; + } + + if (read_loop(monitor_sock_fd, status->daemon_error_info, error_info_len) != (ssize_t)error_info_len) { + LOGE("read ReZygiskd%s error info", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32"); + + free(status->daemon_error_info); + status->daemon_error_info = NULL; + + break; + } + + status->daemon_error_info[error_info_len] = '\0'; + LOGD("ReZygiskd%s error info: %s", cmd == DAEMON64_SET_ERROR_INFO ? "64" : "32", status->daemon_error_info); + update_status(NULL); break; @@ -347,8 +396,6 @@ void rezygiskd_listener_callback() { } } - if (msg_data) free(msg_data); - break; } } @@ -423,51 +470,49 @@ static bool ensure_daemon_created(bool is_64bit) { return true; } -#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 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 = strdup(status_str); \ + if (!status##abi.daemon_error_info) { \ + LOGE("malloc daemon" #abi " error info failed"); \ + \ + return; \ + } \ + } \ + \ + 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; \ - } \ +#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; \ + strcpy(monitor_stop_reason, "Zygote crashed"); \ + ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ + \ + break; \ + } \ + \ + if (!ensure_daemon_created(is_64)) { \ + LOGW("ReZygiskd " #abi "-bit not running, stop injecting"); \ + \ + tracing_state = STOPPING; \ + strcpy(monitor_stop_reason, "ReZygiskd not running"); \ + ptrace(PTRACE_INTERRUPT, 1, 0, 0); \ + \ + break; \ + } \ } int sigchld_signal_fd; @@ -661,8 +706,6 @@ void sigchld_listener_callback() { } } } while (false); - - update_status(NULL); } else { char status_str[64]; parse_status(sigchld_status, status_str, sizeof(status_str)); @@ -702,27 +745,20 @@ 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, ", ReZygisk " # suffix "-bit: "); \ \ - strcat(status_text, "daemon" # suffix ": "); \ - if (status ## suffix.daemon_running) { \ - strcat(status_text, "😋 running "); \ + if (tracing_state != TRACING) strcat(status_text, "❌"); \ + else if (status ## suffix.zygote_injected && status ## suffix.daemon_running) \ + strcat(status_text, "✅"); \ + else strcat(status_text, "⚠️"); \ \ - 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, "("); \ + if (!status ## suffix.daemon_running) { \ + if (status ## suffix.daemon_error_info) { \ + strcat(status_text, "(ReZygiskd: "); \ strcat(status_text, status ## suffix.daemon_error_info); \ strcat(status_text, ")"); \ + } else { \ + strcat(status_text, "(ReZygiskd: not running)"); \ } \ } \ } @@ -742,41 +778,106 @@ static bool update_status(const char *message) { return true; } - char status_text[1024] = "monitor: "; - + char status_text[256] = "Monitor: "; switch (tracing_state) { case TRACING: { - strcat(status_text, "😋 tracing"); + strcat(status_text, "✅"); break; } case STOPPING: [[fallthrough]]; case STOPPED: { - strcat(status_text, "❌ stopped"); + strcat(status_text, "⛔"); break; } case EXITING: { - strcat(status_text, "❌ exited"); + strcat(status_text, "❌"); 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); + if (environment_information64.root_impl || environment_information32.root_impl) { + FILE *json = fopen("/data/adb/rezygisk/state.json", "w"); + if (json == NULL) { + PLOGE("failed to open state.json"); + + return false; + } + + fprintf(json, "{\n"); + fprintf(json, " \"root\": \"%s\",\n", environment_information64.root_impl ? environment_information64.root_impl : environment_information32.root_impl); + + fprintf(json, " \"monitor\": {\n"); + fprintf(json, " \"state\": \"%d\"", tracing_state); + if (monitor_stop_reason[0] != '\0') fprintf(json, ",\n \"reason\": \"%s\",\n", monitor_stop_reason); + else fprintf(json, "\n"); + fprintf(json, " },\n"); + + + fprintf(json, " \"rezygiskd\": {\n"); + if (status64.supported) { + fprintf(json, " \"64\": {\n"); + fprintf(json, " \"state\": %d,\n", status64.daemon_running); + if (status64.daemon_error_info) fprintf(json, " \"reason\": \"%s\",\n", status64.daemon_error_info); + fprintf(json, " \"modules\": ["); + + if (environment_information64.modules) for (uint32_t i = 0; i < environment_information64.modules_len; i++) { + if (i > 0) fprintf(json, ", "); + fprintf(json, "\"%s\"", environment_information64.modules[i]); + } + + fprintf(json, "]\n"); + fprintf(json, " }"); + if (status32.supported) fprintf(json, ",\n"); + else fprintf(json, "\n"); + } + + if (status32.supported) { + fprintf(json, " \"32\": {\n"); + fprintf(json, " \"state\": %d,\n", status32.daemon_running); + if (status32.daemon_error_info) fprintf(json, " \"reason\": \"%s\",\n", status32.daemon_error_info); + fprintf(json, " \"modules\": ["); + + if (environment_information32.modules) for (uint32_t i = 0; i < environment_information32.modules_len; i++) { + if (i > 0) fprintf(json, ", "); + fprintf(json, "\"%s\"", environment_information32.modules[i]); + } + + fprintf(json, "]\n"); + fprintf(json, " }\n"); + } + + fprintf(json, " },\n"); + + fprintf(json, " \"zygote\": {\n"); + if (status64.supported) { + fprintf(json, " \"64\": %d", status64.zygote_injected); + if (status32.zygote_injected) fprintf(json, ",\n"); + else fprintf(json, "\n"); + } + if (status32.zygote_injected) { + fprintf(json, " \"32\": %d\n", status32.zygote_injected); + } + fprintf(json, " }\n"); + fprintf(json, "}\n"); + + fclose(json); + } else { + if (remove("/data/adb/rezygisk/state.json") == -1) { + PLOGE("failed to remove state.json"); + } + } + + LOGI("status updated: %s", status_text); + return true; } @@ -821,7 +922,7 @@ static bool prepare_environment() { return false; } - return update_status(NULL); + return true; } void init_monitor() { @@ -864,7 +965,23 @@ void init_monitor() { if (status32.daemon_info) free(status32.daemon_info); if (status32.daemon_error_info) free(status32.daemon_error_info); - LOGI("exit"); + if (environment_information64.root_impl) free((void *)environment_information64.root_impl); + if (environment_information64.modules) { + for (uint32_t i = 0; i < environment_information64.modules_len; i++) { + free((void *)environment_information64.modules[i]); + } + free((void *)environment_information64.modules); + } + + if (environment_information32.root_impl) free((void *)environment_information32.root_impl); + if (environment_information32.modules) { + for (uint32_t i = 0; i < environment_information32.modules_len; i++) { + free((void *)environment_information32.modules[i]); + } + free((void *)environment_information32.modules); + } + + LOGI("Terminating ReZygisk monitor"); } int send_control_command(enum rezygiskd_command cmd) { @@ -880,9 +997,10 @@ int send_control_command(enum rezygiskd_command cmd) { socklen_t socklen = sizeof(sa_family_t) + sun_path_len; - ssize_t nsend = sendto(sockfd, (void *)&cmd, sizeof(cmd), 0, (struct sockaddr *)&addr, socklen); + uint8_t cmd_op = cmd; + ssize_t nsend = sendto(sockfd, (void *)&cmd_op, sizeof(cmd_op), 0, (struct sockaddr *)&addr, socklen); close(sockfd); - return nsend != sizeof(cmd) ? -1 : 0; + return nsend != sizeof(cmd_op) ? -1 : 0; } diff --git a/webroot/js/main.js b/webroot/js/main.js index da52ee1..1ed1b01 100644 --- a/webroot/js/main.js +++ b/webroot/js/main.js @@ -91,101 +91,106 @@ async function getModuleNames(modules) { const unameCmd = await exec('/system/bin/uname -r') if (unameCmd.errno !== 0) return setError('WebUI', unameCmd.stderr) - document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout - console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout) + document.getElementById('kernel_version_div').innerHTML = unameCmd.stdout.trim() + console.log('[rezygisk.js] Kernel version: ', unameCmd.stdout.trim()) const catCmd = await exec('/system/bin/cat /data/adb/rezygisk/module.prop') console.log(`[rezygisk.js] ReZygisk module infomation:\n${catCmd.stdout}`) - let expectedWorking = 0 - let actuallyWorking = 0 + if (catCmd.errno !== 0) { + console.error('[rezygisk.js] Failed to retrieve ReZygisk module information:', catCmd.stderr) - const ReZygiskInfo = { - rootImpl: null, - monitor: null, - zygotes: [], - daemons: [] - } + rezygisk_state.innerHTML = translations.page.home.status.notWorking + rezygisk_icon_state.innerHTML = '' - if (catCmd.errno === 0) { - /* INFO: Just ensure that they won't appear unless there's info */ + rootCss.style.setProperty('--bright', '#766000') + + /* INFO: Hide zygote divs */ zygote_divs.forEach((zygote_div) => { zygote_div.style.display = 'none' }) - version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim() + loading_screen.style.display = 'none' + bottom_nav.style.display = 'flex' - let moduleInfo = catCmd.stdout.split('\n').find((line) => line.startsWith('description=')).substring('description='.length).split('[')[1].split(']')[0] + return; + } - const daemonModules = [] - moduleInfo.match(/\(([^)]+)\)/g).forEach((area) => { - moduleInfo = moduleInfo.replace(area, ',') + /* INFO: Just ensure that they won't appear unless there's info */ + zygote_divs.forEach((zygote_div) => { + zygote_div.style.display = 'none' + }) - const info = area.substring(1, area.length - 1).split(', ') - if (info.length === 1) return; /* INFO: undefined as object */ + version.innerHTML = catCmd.stdout.split('\n').find((line) => line.startsWith('version=')).substring('version='.length).trim() - const rootImpl = info[0].substring('Root: '.length) + const stateCmd = await exec('/system/bin/cat /data/adb/rezygisk/state.json') + if (stateCmd.errno !== 0) { + console.error('[rezygisk.js] Failed to retrieve ReZygisk state information:', stateCmd.stderr) - info[1] = info[1].substring('Modules: '.length) - const modules = info.slice(1, info.length) + rezygisk_state.innerHTML = translations.page.home.status.notWorking + rezygisk_icon_state.innerHTML = '' - ReZygiskInfo.rootImpl = rootImpl - if (modules[0] !== 'None') daemonModules.push(modules) + rootCss.style.setProperty('--bright', '#766000') + + /* INFO: Hide zygote divs */ + zygote_divs.forEach((zygote_div) => { + zygote_div.style.display = 'none' }) - const infoArea = moduleInfo.split(', ') - infoArea.forEach((info) => { - if (info.startsWith('monitor:')) { - ReZygiskInfo.monitor = info.substring('monitor: X '.length).trim() - } + loading_screen.style.display = 'none' + bottom_nav.style.display = 'flex' - if (info.startsWith('zygote')) { - ReZygiskInfo.zygotes.push({ - bits: info.substring('zygote'.length, 'zygote'.length + 'XX'.length), - state: info.substring('zygoteXX: X '.length).trim() - }) - } + return; + } - if (info.startsWith('daemon')) { - ReZygiskInfo.daemons.push({ - bits: info.substring('daemon'.length, 'daemon'.length + 'XX'.length), - state: info.substring('daemonXX: X '.length).trim(), - modules: daemonModules[ReZygiskInfo.daemons.length] || [] - }) - } - }) + const ReZygiskState = JSON.parse(stateCmd.stdout) - switch (ReZygiskInfo.monitor) { - case 'tracing': monitor_status.innerHTML = translations.page.actions.status.tracing; break; - case 'stopping': monitor_status.innerHTML = translations.page.actions.status.stopping; break; - case 'stopped': monitor_status.innerHTML = translations.page.actions.status.stopped; break; - case 'exiting': monitor_status.innerHTML = translations.page.actions.status.exiting; break; - default: monitor_status.innerHTML = translations.page.actions.status.unknown; + root_impl.innerHTML = ReZygiskState.root + + switch (ReZygiskState.monitor.state) { + case 0: monitor_status.innerHTML = translations.page.actions.status.tracing; break; + case 1: monitor_status.innerHTML = translations.page.actions.status.stopping; break; + case 2: monitor_status.innerHTML = translations.page.actions.status.stopped; break; + case 3: monitor_status.innerHTML = translations.page.actions.status.exiting; break; + default: monitor_status.innerHTML = translations.page.actions.status.unknown; + } + + const expectedWorking = (ReZygiskState.zygote['64'] !== undefined ? 1 : 0) + (ReZygiskState.zygote['32'] !== undefined ? 1 : 0) + let actuallyWorking = 0 + + if (ReZygiskState.zygote['64'] !== undefined) { + const zygote64 = ReZygiskState.zygote['64'] + + zygote_divs[0].style.display = 'block' + + switch (zygote64) { + case 0: { + zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.injected + + actuallyWorking++ + + break + } + case 1: zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.notInjected; break + default: zygote_status_divs[0].innerHTML = translations.page.home.info.zygote.unknown } + } - expectedWorking = ReZygiskInfo.zygotes.length + if (ReZygiskState.zygote['32'] !== undefined) { + const zygote32 = ReZygiskState.zygote['32'] - for (let i = 0; i < ReZygiskInfo.zygotes.length; i++) { - const zygote = ReZygiskInfo.zygotes[i] - /* INFO: Not used ATM */ - /* const daemon = ReZygiskInfo.daemons[i] */ + zygote_divs[1].style.display = 'block' - const zygoteDiv = zygote_divs[zygote.bits === '64' ? 0 : 1] - const zygoteStatusDiv = zygote_status_divs[zygote.bits === '64' ? 0 : 1] + switch (zygote32) { + case 0: { + zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.injected - zygoteDiv.style.display = 'block' + actuallyWorking++ - switch (zygote.state) { - case 'injected': { - zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.injected; - - actuallyWorking++ - - break; - } - case 'not injected': zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.notInjected; break; - default: zygoteStatusDiv.innerHTML = translations.page.home.info.zygote.unknown; + break } + case 1: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.notInjected; break + default: zygote_status_divs[1].innerHTML = translations.page.home.info.zygote.unknown } } @@ -203,24 +208,24 @@ async function getModuleNames(modules) { rezygisk_icon_state.innerHTML = '' } - if (ReZygiskInfo.rootImpl) - root_impl.innerHTML = ReZygiskInfo.rootImpl - const all_modules = [] - ReZygiskInfo.daemons.forEach((daemon) => { - daemon.modules.forEach((module_id) => { - const module = all_modules.find((mod) => mod.id === module_id) + Object.keys(ReZygiskState.rezygiskd).forEach((daemon_bit) => { + const daemon = ReZygiskState.rezygiskd[daemon_bit] - if (module) { - module.bitsUsed.push(daemon.bits) - } else { - all_modules.push({ - id: module_id, - name: null, - bitsUsed: [ daemon.bits ] - }) - } - }) + if (daemon.modules && daemon.modules.length > 0) { + daemon.modules.forEach((module_id) => { + const module = all_modules.find((mod) => mod.id === module_id) + if (module) { + module.bitsUsed.push(daemon_bit) + } else { + all_modules.push({ + id: module_id, + name: null, + bitsUsed: [ daemon_bit ] + }) + } + }) + } }) if (all_modules.length !== 0) { @@ -251,7 +256,6 @@ async function getModuleNames(modules) { loading_screen.style.display = 'none' bottom_nav.style.display = 'flex' - const start_time = Number(localStorage.getItem('/system/boot-time')) console.log('[rezygisk.js] boot time: ', Date.now() - start_time, 'ms') localStorage.removeItem('/system/boot_time') diff --git a/zygiskd/src/zygiskd.c b/zygiskd/src/zygiskd.c index d813933..8f62b83 100644 --- a/zygiskd/src/zygiskd.c +++ b/zygiskd/src/zygiskd.c @@ -261,11 +261,6 @@ static int spawn_companion(char *restrict argv[], char *restrict name, int lib_f exit(0); } -struct __attribute__((__packed__)) MsgHead { - unsigned int cmd; - int length; -}; - /* WARNING: Dynamic memory based */ void zygiskd_start(char *restrict argv[]) { /* INFO: When implementation is None or Multiple, it won't set the values @@ -276,118 +271,42 @@ void zygiskd_start(char *restrict argv[]) { struct root_impl impl; get_impl(&impl); if (impl.impl == None || impl.impl == Multiple) { - char *msg_data = NULL; + unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_ERROR_INFO }, sizeof(uint8_t)); - if (impl.impl == None) msg_data = "Unsupported environment: Unknown root implementation"; - else msg_data = "Unsupported environment: Multiple root implementations found"; + const char *msg = NULL; + if (impl.impl == None) msg = "Unsupported environment: Unknown root implementation"; + else msg = "Unsupported environment: Multiple root implementations found"; - struct MsgHead msg = { - .cmd = DAEMON_SET_ERROR_INFO, - .length = (int)strlen(msg_data) + 1 - }; + LOGE("%s", msg); - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, (size_t)msg.length); + uint32_t msg_len = (uint32_t)strlen(msg); + unix_datagram_sendto(CONTROLLER_SOCKET, &msg_len, sizeof(msg_len)); + unix_datagram_sendto(CONTROLLER_SOCKET, (void *)msg, msg_len); exit(EXIT_FAILURE); } else { enum Architecture arch = get_arch(); load_modules(arch, &context); - char *module_list = NULL; - size_t module_list_len = 0; - if (context.len == 0) { - module_list = strdup("None"); - module_list_len = strlen("None"); - } else { - for (size_t i = 0; i < context.len; i++) { - if (i != context.len - 1) { - char *tmp_module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1); - if (tmp_module_list == NULL) { - LOGE("Failed reallocating memory for module list.\n"); - - char *kmsg_failure = "Failed reallocating memory for module list"; - struct MsgHead msg = { - .cmd = DAEMON_SET_ERROR_INFO, - .length = (int)strlen(kmsg_failure) + 1 - }; - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length); - - free(module_list); - free_modules(&context); - - exit(EXIT_FAILURE); - } - module_list = tmp_module_list; - - strcpy(module_list + module_list_len, context.modules[i].name); - - module_list_len += strlen(context.modules[i].name); - - strcpy(module_list + module_list_len, ", "); - - module_list_len += strlen(", "); - } else { - char *tmp_module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1); - if (tmp_module_list == NULL) { - LOGE("Failed reallocating memory for module list.\n"); - - char *kmsg_failure = "Failed reallocating memory for module list"; - struct MsgHead msg = { - .cmd = DAEMON_SET_ERROR_INFO, - .length = (int)strlen(kmsg_failure) + 1 - }; - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length); - - free(module_list); - free_modules(&context); - - exit(EXIT_FAILURE); - } - module_list = tmp_module_list; - - strcpy(module_list + module_list_len, context.modules[i].name); - - module_list_len += strlen(context.modules[i].name); - } - } - } + unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ DAEMON_SET_INFO }, sizeof(uint8_t)); char impl_name[LONGEST_ROOT_IMPL_NAME]; stringify_root_impl_name(impl, impl_name); - size_t msg_length = strlen("Root: , Modules: ") + strlen(impl_name) + module_list_len + 1; + uint32_t root_impl_len = (uint32_t)strlen(impl_name); + unix_datagram_sendto(CONTROLLER_SOCKET, &root_impl_len, sizeof(root_impl_len)); + unix_datagram_sendto(CONTROLLER_SOCKET, impl_name, root_impl_len); - struct MsgHead msg = { - .cmd = DAEMON_SET_INFO, - .length = (int)msg_length - }; + uint32_t modules_len = (uint32_t)context.len; + unix_datagram_sendto(CONTROLLER_SOCKET, &modules_len, sizeof(modules_len)); - char *msg_data = malloc(msg_length); - if (msg_data == NULL) { - LOGE("Failed allocating memory for message data.\n"); - - char *kmsg_failure = "Failed allocating memory for message data"; - msg.cmd = DAEMON_SET_ERROR_INFO; - msg.length = (int)strlen(kmsg_failure) + 1; - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length); - - free(module_list); - free_modules(&context); - - exit(EXIT_FAILURE); + for (size_t i = 0; i < context.len; i++) { + uint32_t module_name_len = (uint32_t)strlen(context.modules[i].name); + unix_datagram_sendto(CONTROLLER_SOCKET, &module_name_len, sizeof(module_name_len)); + unix_datagram_sendto(CONTROLLER_SOCKET, context.modules[i].name, module_name_len); } - snprintf(msg_data, msg_length, "Root: %s, Modules: %s", impl_name, module_list); - - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, msg_length); - - free(msg_data); - free(module_list); + LOGI("Sent root implementation and modules information to controller socket"); } int socket_fd = create_daemon_socket(); @@ -427,12 +346,7 @@ void zygiskd_start(char *restrict argv[]) { switch (action) { case PingHeartbeat: { - struct MsgHead msg = { - .cmd = ZYGOTE_INJECTED, - .length = 0 - }; - - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); + unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ ZYGOTE_INJECTED }, sizeof(uint8_t)); break; } @@ -447,22 +361,7 @@ void zygiskd_start(char *restrict argv[]) { break; } case SystemServerStarted: { - struct MsgHead msg = { - .cmd = SYSTEM_SERVER_STARTED, - .length = 0 - }; - - unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead)); - - if (impl.impl == None || impl.impl == Multiple) { - LOGI("Unsupported environment detected. Exiting.\n"); - - close(client_fd); - close(socket_fd); - free_modules(&context); - - exit(1); - } + unix_datagram_sendto(CONTROLLER_SOCKET, &(uint8_t){ SYSTEM_SERVER_STARTED }, sizeof(uint8_t)); break; }