Files
ReZygisk/zygiskd-new/zygiskd.c
ThePedroo da68db8a9a add: base for C99 zygiskd
This commit adds the first base for C99 zygiskd, that is not fully working or code-ready.
2024-11-10 23:03:26 -03:00

723 lines
21 KiB
C

#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <linux/limits.h>
#include <pthread.h>
#include "root_impl/common.h"
#include "constants.h"
#include "utils.h"
struct Module {
char *name;
int lib_fd;
int companion;
};
struct Context {
struct Module *modules;
int len;
};
enum Architecture {
ARM32,
ARM64,
X86,
X86_64,
};
#define PATH_MODULES_DIR "/data/adb/modules"
#define TMP_PATH "/data/adb/rezygisk"
#define CONTROLLER_SOCKET TMP_PATH "/init_monitor"
#define PATH_CP_NAME TMP_PATH "/" lp_select("cp32.sock", "cp64.sock")
#define ZYGISKD_FILE "zygiskd" lp_select("32", "64")
#define ZYGISKD_PATH "/data/adb/modules/zygisksu/bin/zygiskd" lp_select("32", "64")
#define ASSURE_SIZE_WRITE(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to sent " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
return; \
}
#define ASSURE_SIZE_READ(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to read " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
return; \
}
#define ASSURE_SIZE_WRITE_BREAK(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to sent " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
break; \
}
#define ASSURE_SIZE_READ_BREAK(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to read " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
break; \
}
#define ASSURE_SIZE_WRITE_WR(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to sent " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
return -1; \
}
#define ASSURE_SIZE_READ_WR(area_name, subarea_name, sent_size, expected_size) \
if (sent_size != (ssize_t)(expected_size)) { \
LOGE("Failed to read " subarea_name " in " area_name ": Expected %zu, got %zd\n", expected_size, sent_size); \
\
return -1; \
}
static enum Architecture get_arch(void) {
char system_arch[32];
get_property("ro.product.cpu.abi", system_arch);
if (strstr(system_arch, "arm") != NULL) return lp_select(ARM32, ARM64);
if (strstr(system_arch, "x86") != NULL) return lp_select(X86, X86_64);
LOGE("Unsupported system architecture: %s\n", system_arch);
exit(1);
}
int create_library_fd(const char *so_path) {
int memfd = memfd_create("jit-cache-zygisk", MFD_ALLOW_SEALING);
if (memfd == -1) {
perror("memfd_create");
return -1;
}
int so_fd = open(so_path, O_RDONLY);
if (so_fd == -1) {
perror("open");
close(memfd);
return -1;
}
struct stat st;
if (fstat(so_fd, &st) == -1) {
perror("fstat");
close(so_fd);
close(memfd);
return -1;
}
if (sendfile(memfd, so_fd, NULL, st.st_size) == -1) {
perror("sendfile");
close(so_fd);
close(memfd);
return -1;
}
close(so_fd);
if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL) == -1) {
perror("fcntl");
close(memfd);
return -1;
}
return memfd;
}
/* WARNING: Dynamic memory based */
static void load_modules(enum Architecture arch, struct Context *context) {
context->len = 0;
context->modules = malloc(1);
// LOGI("Previous error{25}: %s\n", strerror(errno));
DIR *dir = opendir(PATH_MODULES_DIR);
if (dir == NULL) {
LOGE("Failed opening modules directory: %s.", PATH_MODULES_DIR);
return;
}
// LOGI("Previous error{27}: %s\n", strerror(errno));
char arch_str[32];
switch (arch) {
case ARM32: {
strcpy(arch_str, "armeabi-v7a");
break;
}
case ARM64: {
strcpy(arch_str, "arm64-v8a");
break;
}
case X86: {
strcpy(arch_str, "x86");
break;
}
case X86_64: {
strcpy(arch_str, "x86_64");
break;
}
}
LOGI("Loading modules for architecture: %s\n", arch_str);
// LOGI("Previous error{28}: %s\n", strerror(errno));
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// LOGI("Previous error{29}: %s\n", strerror(errno));
if (entry->d_type != DT_DIR) continue; /* INFO: Only directories */
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, "zygisksu") == 0) continue;
char *name = entry->d_name;
char so_path[PATH_MAX];
snprintf(so_path, PATH_MAX, "/data/adb/modules/%s/zygisk/%s.so", name, arch_str);
struct stat st;
if (stat(so_path, &st) == -1) {
errno = 0;
continue;
}
char disabled[PATH_MAX];
snprintf(disabled, PATH_MAX, "/data/adb/modules/%s/disable", name);
if (stat(disabled, &st) != -1) {
errno = 0;
continue;
}
LOGI("Loading module `%s`...\n", name);
int lib_fd = create_library_fd(so_path);
if (lib_fd == -1) {
LOGE("Failed loading module `%s`\n", name);
continue;
}
LOGI("Loaded module lib fd: %d\n", lib_fd);
context->modules = realloc(context->modules, ((context->len + 1) * sizeof(struct Module)));
context->modules[context->len].name = strdup(name);
context->modules[context->len].lib_fd = lib_fd;
context->modules[context->len].companion = -1;
context->len++;
}
}
static void free_modules(struct Context *context) {
for (int i = 0; i < context->len; i++) {
free(context->modules[i].name);
if (context->modules[i].companion != -1) close(context->modules[i].companion);
}
}
static int create_daemon_socket(void) {
set_socket_create_context("u:r:zygote:s0");
return unix_listener_from_path(PATH_CP_NAME);
}
static int spawn_companion(char *name, int lib_fd) {
// LOGI("Previous error{15}: %s\n", strerror(errno));
int sockets[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
LOGE("Failed creating socket pair.\n");
return -1;
}
int daemon_fd = sockets[0];
int companion_fd = sockets[1];
LOGI("Companion fd: %d\n", companion_fd);
LOGI("Daemon fd: %d\n", daemon_fd);
// LOGI("Previous error{13}: %s\n", strerror(errno));
pid_t pid = fork();
LOGI("Forked: %d\n", pid);
if (pid < 0) {
LOGE("Failed forking companion: %s\n", strerror(errno));
close(companion_fd);
close(daemon_fd);
exit(1);
} else if (pid > 0) {
// LOGI("Previous error{11}: %s\n", strerror(errno));
close(companion_fd);
LOGI("Waiting for companion to start (%d)\n", pid);
// LOGI("Previous error{8}: %s\n", strerror(errno));
int status;
waitpid(pid, &status, 0);
LOGI("Companion exited with status %d\n", status);
// LOGI("Previous error{9}: %s\n", strerror(errno));
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
if (write_string(daemon_fd, name) == -1) return -1;
if (send_fd(daemon_fd, lib_fd) == -1) return -1;
LOGI("Sent module name and lib fd\n");
uint8_t response_buf[1];
ssize_t ret = read(daemon_fd, &response_buf, sizeof(response_buf));
ASSURE_SIZE_READ_WR("companion", "response", ret, sizeof(response_buf));
uint8_t response = response_buf[0];
LOGI("Companion response: %hhu\n", response);
if (response == 0) return -2;
else if (response == 1) return daemon_fd;
else return -2;
} else {
LOGE("Exited with status %d\n", status);
close(daemon_fd);
return -1;
}
/* INFO: if pid == 0: */
} else {
LOGI("Companion started (%d)\n", pid);
/* INFO: There is no case where this will fail with a valid fd. */
fcntl(companion_fd, F_SETFD, 0);
}
char companion_fd_str[32];
snprintf(companion_fd_str, 32, "%d", companion_fd);
LOGI("Executing companion...\n");
// LOGI("Previous error{10}: %s\n", strerror(errno));
char *argv[] = { ZYGISKD_FILE, "companion", companion_fd_str, NULL };
if (execv(ZYGISKD_PATH, argv) == -1) {
LOGE("Failed executing companion: %s\n", strerror(errno));
close(companion_fd);
exit(1);
}
exit(0);
}
/* TODO: Is packed attribute really necessary? */
struct __attribute__((__packed__)) MsgHead {
unsigned int cmd;
int length;
char data[0];
};
void zygiskd_start(void) {
LOGI("Welcome to ReZygisk %s!", ZKSU_VERSION);
// LOGI("Previous error{26}: %s\n", strerror(errno));
enum Architecture arch = get_arch();
// LOGI("Previous error{25}: %s\n", strerror(errno));
struct Context context;
load_modules(arch, &context);
struct MsgHead *msg = NULL;
size_t msg_sz = 0;
// LOGI("Previous error{24}: %s\n", strerror(errno));
switch (get_impl()) {
case None: {
/* INFO: Stop, compiler. */
break;
}
case Multiple: {
/* INFO: Stop, compiler. */
break;
}
case KernelSU: {
if (context.len == 0) {
msg_sz = sizeof(struct MsgHead) + strlen("Root: KernelSU, Modules: None") + 1;
msg = malloc(msg_sz);
msg->cmd = DAEMON_SET_INFO;
msg->length = strlen("Root: KernelSU, Modules: None") + 1;
memcpy(msg->data, "Root: KernelSU, Modules: None", strlen("Root: KernelSU, Modules: None"));
} else {
char *module_list = malloc(1);
size_t module_list_len = 0;
for (int i = 0; i < context.len; i++) {
if (i != context.len - 1) {
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1);
memcpy(module_list + module_list_len, context.modules[i].name, strlen(context.modules[i].name));
module_list_len += strlen(context.modules[i].name);
memcpy(module_list + module_list_len, ", ", strlen(", "));
module_list_len += strlen(", ");
} else {
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1);
memcpy(module_list + module_list_len, context.modules[i].name, strlen(context.modules[i].name));
module_list_len += strlen(context.modules[i].name);
}
}
msg_sz = sizeof(struct MsgHead) + strlen("Root: KernelSU, Modules: ") + module_list_len + 1;
msg = malloc(msg_sz);
msg->cmd = DAEMON_SET_INFO;
msg->length = strlen("Root: KernelSU, Modules: ") + module_list_len + 1;
memcpy(msg->data, "Root: KernelSU, Modules: ", strlen("Root: KernelSU, Modules: "));
memcpy(msg->data + strlen("Root: KernelSU, Modules: "), module_list, module_list_len);
free(module_list);
}
break;
}
default: {
msg_sz = sizeof(struct MsgHead) + strlen("Invalid root implementation") + 1;
msg = malloc(msg_sz);
msg->cmd = DAEMON_SET_ERROR_INFO;
msg->length = strlen("Invalid root implementation") + 1;
memcpy(msg->data, "Invalid root implementation", strlen("Invalid root implementation"));
break;
}
}
// LOGI("Previous error{22}: %s\n", strerror(errno));
unix_datagram_sendto(CONTROLLER_SOCKET, (void *)msg, msg_sz);
free(msg);
// LOGI("Previous error{21}: %s\n", strerror(errno));
int socket_fd = create_daemon_socket();
if (socket_fd == -1) {
LOGE("Failed creating daemon socket\n");
return;
}
while (1) {
// LOGI("Previous error{20}: %s\n", strerror(errno));
int client_fd = accept(socket_fd, NULL, NULL);
if (client_fd == -1) {
LOGE("accept: %s\n", strerror(errno));
return;
}
LOGI("Accepted client: %d\n", client_fd);
// LOGI("Previous error{19}: %s\n", strerror(errno));
unsigned char buf[1];
ssize_t len = read(client_fd, buf, sizeof(buf));
if (len == -1) {
LOGE("read: %s\n", strerror(errno));
return;
} else if (len == 0) {
LOGI("Client disconnected\n");
return;
}
LOGI("Action: %hhu\n", (uint8_t)buf[0]);
enum DaemonSocketAction action = (enum DaemonSocketAction)buf[0];
switch (action) {
case PingHeartbeat: {
enum DaemonSocketAction msgr = ZYGOTE_INJECTED;
unix_datagram_sendto(CONTROLLER_SOCKET, &msgr, sizeof(enum DaemonSocketAction));
break;
}
case ZygoteRestart: {
LOGI("Zygote restart\n");
for (int i = 0; i < context.len; i++) {
if (context.modules[i].companion != -1) {
close(context.modules[i].companion);
context.modules[i].companion = -1;
}
}
break;
}
case SystemServerStarted: {
enum DaemonSocketAction msgr = SYSTEM_SERVER_STARTED;
unix_datagram_sendto(CONTROLLER_SOCKET, &msgr, sizeof(enum DaemonSocketAction));
break;
}
case RequestLogcatFd: {
char level_buf[1];
ssize_t ret = read(client_fd, &level_buf, sizeof(level_buf));
ASSURE_SIZE_READ_BREAK("RequestLogcatFd", "level", ret, sizeof(level_buf));
char level = level_buf[0];
char tag[128 + 1];
ret = read_string(client_fd, tag, sizeof(tag) - 1);
if (ret == -1) break;
tag[ret] = '\0';
char message[1024];
ret = read_string(client_fd, message, sizeof(message));
if (ret == -1) break;
__android_log_print(level, tag, "%.*s", (int)ret, message);
break;
}
case GetProcessFlags: {
LOGI("Getting process flags\n");
uid_t uid_buf[1];
ssize_t ret = read(client_fd, &uid_buf, sizeof(uid_buf));
ASSURE_SIZE_READ_BREAK("GetProcessFlags", "uid", ret, sizeof(uid_buf));
uid_t uid = uid_buf[0];
LOGI("Checking flags for uid: %d\n", uid);
uint32_t flags = 0;
if (uid_is_manager(uid)) {
flags |= PROCESS_IS_MANAGER;
} else {
if (uid_granted_root(uid)) {
flags |= PROCESS_GRANTED_ROOT;
}
if (uid_should_umount(uid)) {
flags |= PROCESS_ON_DENYLIST;
}
}
LOGI("Flags for uid %d: %d\n", uid, flags);
switch (get_impl()) {
case None: {
break;
}
case Multiple: {
break;
}
case KernelSU: {
flags |= PROCESS_ROOT_IS_KSU;
}
}
// LOGI("Flags for uid %d: %d\n", uid, flags);
LOGI("Sending flags\n");
ret = write(client_fd, &flags, sizeof(flags));
// ASSURE_SIZE_WRITE_BREAK("GetProcessFlags", "flags", ret, sizeof(flags));
LOGI("Sent flags\n");
break;
}
case GetInfo: {
uint32_t flags = 0;
LOGI("Getting info\n");
switch (get_impl()) {
case None: {
break;
}
case Multiple: {
break;
}
case KernelSU: {
flags |= PROCESS_ROOT_IS_KSU;
}
}
LOGI("Flags: %d\n", flags);
ssize_t ret = write(client_fd, &flags, sizeof(flags));
ASSURE_SIZE_WRITE_BREAK("GetInfo", "flags", ret, sizeof(flags));
pid_t pid = getpid();
LOGI("Getting pid: %d\n", pid);
ret = write(client_fd, &pid, sizeof(pid));
ASSURE_SIZE_WRITE_BREAK("GetInfo", "pid", ret, sizeof(pid));
LOGI("Sent pid\n");
break;
}
case ReadModules: {
LOGI("Reading modules to stream\n");
size_t clen = context.len;
ssize_t ret = write(client_fd, &clen, sizeof(clen));
ASSURE_SIZE_WRITE_BREAK("ReadModules", "len", ret, sizeof(clen));
for (int i = 0; i < (int)clen; i++) {
LOGI("Hey, we're talking about: %d\n", i);
LOGI("Writing module `%s` to stream\n", context.modules[i].name);
LOGI("Lib fd: %d\n", context.modules[i].lib_fd);
size_t name_len = strlen(context.modules[i].name);
LOGI("Name length: %zu\n", name_len);
ret = write(client_fd, &name_len, sizeof(name_len));
ASSURE_SIZE_WRITE_BREAK("ReadModules", "name length", ret, sizeof(name_len));
LOGI("Writing name: %s\n", context.modules[i].name);
ret = write(client_fd, context.modules[i].name, name_len);
ASSURE_SIZE_WRITE_BREAK("ReadModules", "name", ret, name_len);
LOGI("Writing lib fd: %d\n", context.modules[i].lib_fd);
if (send_fd(client_fd, context.modules[i].lib_fd) == -1) break;
}
LOGI("Finished reading modules to stream\n");
break;
}
case RequestCompanionSocket: {
LOGI("Requesting companion socket\n");
// LOGI("Previous error{17}: %s\n", strerror(errno));
size_t index_buf[1];
ssize_t ret = read(client_fd, &index_buf, sizeof(index_buf));
ASSURE_SIZE_READ_BREAK("RequestCompanionSocket", "index", ret, sizeof(index_buf));
// LOGI("Previous error{16}: %s\n", strerror(errno));
size_t index = index_buf[0];
struct Module *module = &context.modules[index];
int companion_fd = module->companion;
if (companion_fd != -1) {
LOGI("Companion for module `%s` already exists\n", module->name);
if (fcntl(companion_fd, F_GETFD) == -1) {
LOGE("Poll companion for module `%s` crashed\n", module->name);
close(companion_fd);
module->companion = -1;
}
}
if (companion_fd == -1) {
LOGI("Spawning companion for `%s`\n", module->name);
companion_fd = spawn_companion(module->name, module->lib_fd);
if (companion_fd != -1) {
LOGI("Spawned companion for `%s`\n", module->name);
module->companion = companion_fd;
if (send_fd(client_fd, companion_fd) == -1) break;
} else if (companion_fd == -2) {
LOGI("Could not spawn companion for `%s` as it has no entry\n", module->name);
/* TODO: Avoid duplicated code -- Merge this and the one below. */
uint8_t response = 0;
ret = write(client_fd, &response, sizeof(response));
ASSURE_SIZE_WRITE_BREAK("RequestCompanionSocket", "response", ret, sizeof(response));
} else {
LOGE("Failed to spawn companion for `%s`\n", module->name);
uint8_t response = 0;
ret = write(client_fd, &response, sizeof(response));
ASSURE_SIZE_WRITE_BREAK("RequestCompanionSocket", "response", ret, sizeof(response));
}
LOGI("Companion fd: %d\n", companion_fd);
}
break;
}
case GetModuleDir: {
LOGI("Getting module directory\n");
size_t index_buf[1];
ssize_t ret = read(client_fd, &index_buf, sizeof(index_buf));
ASSURE_SIZE_READ_BREAK("GetModuleDir", "index", ret, sizeof(index_buf));
size_t index = index_buf[0];
LOGI("Index: %zu\n", index);
char dir[PATH_MAX];
snprintf(dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context.modules[index].name);
LOGI("Module directory: %s\n", dir);
int dir_fd = open(dir, O_RDONLY);
LOGI("Module directory fd: %d\n", dir_fd);
if (send_fd(client_fd, dir_fd) == -1) break;
LOGI("Sent module directory fd\n");
break;
}
close(client_fd);
}
continue;
}
close(socket_fd);
free_modules(&context);
}