diff --git a/loader/build.gradle.kts b/loader/build.gradle.kts index f9d9f8e..748350a 100644 --- a/loader/build.gradle.kts +++ b/loader/build.gradle.kts @@ -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", diff --git a/loader/src/CMakeLists.txt b/loader/src/CMakeLists.txt index 84ba7b9..a0b60b5 100644 --- a/loader/src/CMakeLists.txt +++ b/loader/src/CMakeLists.txt @@ -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) diff --git a/loader/src/common/daemon.c b/loader/src/common/daemon.c new file mode 100644 index 0000000..9910ef1 --- /dev/null +++ b/loader/src/common/daemon.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include + +#include + +#include "logging.h" +#include "socket_utils.h" + +#include "daemon.h" + +int rezygiskd_connect(uint8_t retry) { + retry++; + + int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (fd == -1) { + PLOGE("socket create"); + + return -1; + } + + struct sockaddr_un addr = { + .sun_family = AF_UNIX, + .sun_path = { 0 } + }; + + /* + INFO: Application must assume that sun_path can hold _POSIX_PATH_MAX characters. + + Sources: + - https://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/un.h.html + */ + strcpy(addr.sun_path, TMP_PATH "/" SOCKET_FILE_NAME); + socklen_t socklen = sizeof(addr); + + while (--retry) { + int ret = connect(fd, (struct sockaddr *)&addr, socklen); + if (ret == 0) return fd; + if (retry) { + PLOGE("Retrying to connect to ReZygiskd, sleep 1s"); + + sleep(1); + } + } + + close(fd); + + return -1; +} + +bool rezygiskd_ping() { + int fd = rezygiskd_connect(5); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return false; + } + + write_uint8_t(fd, (uint8_t)PingHeartbeat); + + close(fd); + + return true; +} + +uint32_t rezygiskd_get_process_flags(uid_t uid) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return 0; + } + + write_uint8_t(fd, (uint8_t)GetProcessFlags); + write_uint32_t(fd, (uint32_t)uid); + + uint32_t res = 0; + read_uint32_t(fd, &res); + + close(fd); + + return res; +} + +void rezygiskd_get_info(struct rezygisk_info *info) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + info->running = false; + + return; + } + + info->running = true; + + write_uint8_t(fd, (uint8_t)GetInfo); + + uint32_t flags = 0; + read_uint32_t(fd, &flags); + + if (flags & (1 << 27)) info->root_impl = ROOT_IMPL_APATCH; + else if (flags & (1 << 29)) info->root_impl = ROOT_IMPL_KERNELSU; + else if (flags & (1 << 30)) info->root_impl = ROOT_IMPL_MAGISK; + else info->root_impl = ROOT_IMPL_NONE; + + read_uint32_t(fd, (uint32_t *)&info->pid); + + read_size_t(fd, &info->modules->modules_count); + if (info->modules->modules_count == 0) { + info->modules->modules = NULL; + + close(fd); + + return; + } + + info->modules->modules = (char **)malloc(sizeof(char *) * info->modules->modules_count); + if (info->modules->modules == NULL) { + PLOGE("allocating modules name memory"); + + free(info->modules); + info->modules = NULL; + info->modules->modules_count = 0; + + close(fd); + + return; + } + + for (size_t i = 0; i < info->modules->modules_count; i++) { + char *module_name = read_string(fd); + if (module_name == NULL) { + PLOGE("reading module name"); + + info->modules->modules_count = i; + + free_rezygisk_info(info); + + info->modules = NULL; + info->modules->modules_count = 0; + + close(fd); + + return; + } + + char module_path[PATH_MAX]; + snprintf(module_path, sizeof(module_path), "/data/adb/modules/%s/module.prop", module_name); + + FILE *module_prop = fopen(module_path, "r"); + if (!module_prop) { + PLOGE("failed to open module prop file %s", module_path); + + info->modules->modules_count = i; + + free_rezygisk_info(info); + + info->modules = NULL; + info->modules->modules_count = 0; + + close(fd); + + return; + } + + char line[1024]; + while (fgets(line, sizeof(line), module_prop) != NULL) { + if (strncmp(line, "name=", strlen("name=")) != 0) continue; + + info->modules->modules[i] = strndup(line + 5, strlen(line) - 6); + + break; + } + + fclose(module_prop); + } + + close(fd); +} + +void free_rezygisk_info(struct rezygisk_info *info) { + if (info->modules->modules) { + for (size_t i = 0; i < info->modules->modules_count; i++) { + free(info->modules->modules[i]); + } + + free(info->modules->modules); + } + + free(info->modules); +} + +bool rezygiskd_read_modules(struct zygisk_modules *modules) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return false; + } + + write_uint8_t(fd, (uint8_t)ReadModules); + + size_t len = 0; + read_size_t(fd, &len); + + modules->modules = malloc(len * sizeof(char *)); + if (!modules->modules) { + PLOGE("allocating modules name memory"); + + close(fd); + + return false; + } + modules->modules_count = len; + + for (size_t i = 0; i < len; i++) { + char *lib_path = read_string(fd); + if (!lib_path) { + PLOGE("reading module lib_path"); + + close(fd); + + return false; + } + + modules->modules[i] = lib_path; + } + + close(fd); + + return true; +} + +void free_modules(struct zygisk_modules *modules) { + if (modules->modules) { + for (size_t i = 0; i < modules->modules_count; i++) { + free(modules->modules[i]); + } + + free(modules->modules); + } +} + +int rezygiskd_connect_companion(size_t index) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return -1; + } + + write_uint8_t(fd, (uint8_t)RequestCompanionSocket); + write_size_t(fd, index); + + uint8_t res = 0; + read_uint8_t(fd, &res); + + if (res == 1) return fd; + else { + close(fd); + + return -1; + } +} + +int rezygiskd_get_module_dir(size_t index) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return -1; + } + + write_uint8_t(fd, (uint8_t)GetModuleDir); + write_size_t(fd, index); + + int dirfd = read_fd(fd); + + close(fd); + + return dirfd; +} + +void rezygiskd_zygote_restart() { + int fd = rezygiskd_connect(1); + if (fd == -1) { + if (errno == ENOENT) LOGD("Could not notify ZygoteRestart (maybe it hasn't been created)"); + else PLOGE("Could not notify ZygoteRestart"); + + return; + } + + if (!write_uint8_t(fd, (uint8_t)ZygoteRestart)) + PLOGE("Failed to request ZygoteRestart"); + + close(fd); +} + +void rezygiskd_system_server_started() { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("Failed to report system server started"); + + return; + } + + if (!write_uint8_t(fd, (uint8_t)SystemServerStarted)) + PLOGE("Failed to request SystemServerStarted"); + + close(fd); +} + +bool rezygiskd_update_mns(enum mount_namespace_state nms_state, char *buf, size_t buf_size) { + int fd = rezygiskd_connect(1); + if (fd == -1) { + PLOGE("connection to ReZygiskd"); + + return false; + } + + write_uint8_t(fd, (uint8_t)UpdateMountNamespace); + write_uint32_t(fd, (uint32_t)getpid()); + write_uint8_t(fd, (uint8_t)nms_state); + + uint32_t target_pid = 0; + if (read_uint32_t(fd, &target_pid) < 0) { + PLOGE("Failed to read target pid"); + + close(fd); + + return false; + } + + uint32_t target_fd = 0; + if (read_uint32_t(fd, &target_fd) < 0) { + PLOGE("Failed to read target fd"); + + close(fd); + + return false; + } + + snprintf(buf, buf_size, "/proc/%u/fd/%u", target_pid, target_fd); + + close(fd); + + return true; +} diff --git a/loader/src/common/daemon.cpp b/loader/src/common/daemon.cpp deleted file mode 100644 index 44c7660..0000000 --- a/loader/src/common/daemon.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "daemon.h" -#include "socket_utils.h" - -namespace zygiskd { - static std::string TMP_PATH; - void Init(const char *path) { - TMP_PATH = path; - } - - std::string GetTmpPath() { - return TMP_PATH; - } - - int Connect(uint8_t retry) { - retry++; - - int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - struct sockaddr_un addr = { - .sun_family = AF_UNIX, - .sun_path = { 0 } - }; - - auto socket_path = TMP_PATH + kCPSocketName; - strcpy(addr.sun_path, socket_path.c_str()); - socklen_t socklen = sizeof(addr); - - while (--retry) { - int r = connect(fd, (struct sockaddr *)&addr, socklen); - if (r == 0) return fd; - if (retry) { - PLOGE("Retrying to connect to zygiskd, sleep 1s"); - - sleep(1); - } - } - - close(fd); - - return -1; - } - - bool PingHeartbeat() { - int fd = Connect(5); - if (fd == -1) { - PLOGE("Connect to zygiskd"); - - return false; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::PingHeartBeat); - - close(fd); - - return true; - } - - uint32_t GetProcessFlags(uid_t uid) { - int fd = Connect(1); - if (fd == -1) { - PLOGE("GetProcessFlags"); - - return 0; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::GetProcessFlags); - socket_utils::write_u32(fd, uid); - - uint32_t res = socket_utils::read_u32(fd); - - close(fd); - - return res; - } - - std::vector ReadModules() { - std::vector modules; - int fd = Connect(1); - if (fd == -1) { - PLOGE("ReadModules"); - - return modules; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::ReadModules); - size_t len = socket_utils::read_usize(fd); - for (size_t i = 0; i < len; i++) { - std::string lib_path = socket_utils::read_string(fd); - std::string name = socket_utils::read_string(fd); - modules.emplace_back(lib_path, name); - } - - close(fd); - - return modules; - } - - int ConnectCompanion(size_t index) { - int fd = Connect(1); - if (fd == -1) { - PLOGE("ConnectCompanion"); - - return -1; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::RequestCompanionSocket); - socket_utils::write_usize(fd, index); - - uint8_t res = socket_utils::read_u8(fd); - - if (res == 1) return fd; - else { - close(fd); - - return -1; - } - } - - int GetModuleDir(size_t index) { - int fd = Connect(1); - if (fd == -1) { - PLOGE("GetModuleDir"); - - return -1; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::GetModuleDir); - socket_utils::write_usize(fd, index); - int nfd = socket_utils::recv_fd(fd); - - close(fd); - - return nfd; - } - - void ZygoteRestart() { - int fd = Connect(1); - if (fd == -1) { - if (errno == ENOENT) LOGD("Could not notify ZygoteRestart (maybe it hasn't been created)"); - else PLOGE("Could not notify ZygoteRestart"); - - return; - } - - if (!socket_utils::write_u8(fd, (uint8_t) SocketAction::ZygoteRestart)) - PLOGE("Failed to request ZygoteRestart"); - - close(fd); - } - - void SystemServerStarted() { - int fd = Connect(1); - if (fd == -1) PLOGE("Failed to report system server started"); - else { - if (!socket_utils::write_u8(fd, (uint8_t) SocketAction::SystemServerStarted)) - PLOGE("Failed to report system server started"); - } - - close(fd); - } - - void GetInfo(struct zygote_info *info) { - /* TODO: Optimize and avoid re-connect twice here */ - int fd = Connect(1); - - if (fd != -1) { - info->running = true; - - socket_utils::write_u8(fd, (uint8_t) SocketAction::GetInfo); - - int flags = socket_utils::read_u32(fd); - - if (flags & (1 << 27)) { - info->root_impl = ZYGOTE_ROOT_IMPL_APATCH; - } else if (flags & (1 << 29)) { - info->root_impl = ZYGOTE_ROOT_IMPL_KERNELSU; - } else if (flags & (1 << 30)) { - info->root_impl = ZYGOTE_ROOT_IMPL_MAGISK; - } else { - info->root_impl = ZYGOTE_ROOT_IMPL_NONE; - } - - info->pid = socket_utils::read_u32(fd); - - info->modules = (struct zygote_modules *)malloc(sizeof(struct zygote_modules)); - if (info->modules == NULL) { - info->modules->modules_count = 0; - - close(fd); - - return; - } - - info->modules->modules_count = socket_utils::read_usize(fd); - - if (info->modules->modules_count == 0) { - info->modules->modules = NULL; - - close(fd); - - return; - } - - info->modules->modules = (char **)malloc(sizeof(char *) * info->modules->modules_count); - if (info->modules->modules == NULL) { - free(info->modules); - info->modules = NULL; - info->modules->modules_count = 0; - - close(fd); - - return; - } - - for (size_t i = 0; i < info->modules->modules_count; i++) { - /* INFO by ThePedroo: Ugly solution to read with std::string existance (temporary) */ - std::string name = socket_utils::read_string(fd); - - char module_path[PATH_MAX]; - snprintf(module_path, sizeof(module_path), "/data/adb/modules/%s/module.prop", name.c_str()); - - FILE *module_prop = fopen(module_path, "r"); - if (module_prop == NULL) { - info->modules->modules[i] = strdup(name.c_str()); - } else { - char line[1024]; - while (fgets(line, sizeof(line), module_prop) != NULL) { - if (strncmp(line, "name=", 5) == 0) { - info->modules->modules[i] = strndup(line + 5, strlen(line) - 6); - - break; - } - } - - fclose(module_prop); - } - } - - close(fd); - } else info->running = false; - } - - std::string UpdateMountNamespace(enum mount_namespace_state nms_state) { - int fd = Connect(1); - if (fd == -1) { - PLOGE("UpdateMountNamespace"); - - return ""; - } - - socket_utils::write_u8(fd, (uint8_t) SocketAction::UpdateMountNamespace); - socket_utils::write_u32(fd, getpid()); - socket_utils::write_u8(fd, (uint8_t)nms_state); - - uint32_t target_pid = socket_utils::read_u32(fd); - int target_fd = 0; - - if (target_pid == 0) goto error; - - target_fd = (int)socket_utils::read_u32(fd); - if (target_fd == 0) goto error; - - close(fd); - - return "/proc/" + std::to_string(target_pid) + "/fd/" + std::to_string(target_fd); - - error: - close(fd); - - return ""; - } -} diff --git a/loader/src/common/elf_util.c b/loader/src/common/elf_util.c new file mode 100644 index 0000000..bb4ee69 --- /dev/null +++ b/loader/src/common/elf_util.c @@ -0,0 +1,394 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "logging.h" + +#include "elf_util.h" + +#define SHT_GNU_HASH 0x6ffffff6 + +uint32_t ElfHash(const char *name) { + uint32_t h = 0, g = 0; + + while (*name) { + h = (h << 4) + (unsigned char)*name++; + g = h & 0xf0000000; + + if (g) { + h ^= g >> 24; + } + + h &= ~g; + } + + return h; +} + +uint32_t GnuHash(const char *name) { + uint32_t h = 5381; + + while (*name) { + h = (h << 5) + h + (unsigned char)(*name++); + } + + return h; +} + +ElfW(Shdr) *offsetOf_Shdr(ElfW(Ehdr) * head, ElfW(Off) off) { + return (ElfW(Shdr) *)(((uintptr_t)head) + off); +} + +char *offsetOf_char(ElfW(Ehdr) * head, ElfW(Off) off) { + return (char *)(((uintptr_t)head) + off); +} + +ElfW(Sym) *offsetOf_Sym(ElfW(Ehdr) * head, ElfW(Off) off) { + return (ElfW(Sym) *)(((uintptr_t)head) + off); +} + +ElfW(Word) *offsetOf_Word(ElfW(Ehdr) * head, ElfW(Off) off) { + return (ElfW(Word) *)(((uintptr_t)head) + off); +} + +int dl_cb(struct dl_phdr_info *info, size_t size, void *data) { + (void) size; + + if ((info)->dlpi_name == NULL) return 0; + + ElfImg *img = (ElfImg *)data; + + if (strstr(info->dlpi_name, img->elf)) { + img->elf = strdup(info->dlpi_name); + img->base = (void *)info->dlpi_addr; + + return 1; + } + + return 0; +} + +bool find_module_base(ElfImg *img) { + dl_iterate_phdr(dl_cb, img); + + return img->base != NULL; +} + +size_t calculate_valid_symtabs_amount(ElfImg *img) { + size_t count = 0; + + if (img->symtab_start == NULL || img->symstr_offset_for_symtab == 0) return count; + + for (ElfW(Off) i = 0; i < img->symtab_count; i++) { + unsigned int st_type = ELF_ST_TYPE(img->symtab_start[i].st_info); + + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && img->symtab_start[i].st_size) + count++; + } + + return count; +} + +void ElfImg_destroy(ElfImg *img) { + if (img->elf) { + free(img->elf); + img->elf = NULL; + } + + if (img->symtabs_) { + size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img); + if (valid_symtabs_amount == 0) goto finalize; + + for (size_t i = 0; i < valid_symtabs_amount; i++) { + free(img->symtabs_[i].name); + } + + free(img->symtabs_); + img->symtabs_ = NULL; + } + + if (img->header) { + munmap(img->header, img->size); + img->header = NULL; + } + + finalize: + free(img); + img = NULL; +} + +ElfImg *ElfImg_create(const char *elf) { + ElfImg *img = (ElfImg *)calloc(1, sizeof(ElfImg)); + if (!img) { + LOGE("Failed to allocate memory for ElfImg"); + + return NULL; + } + + img->bias = -4396; + img->elf = strdup(elf); + img->base = NULL; + + if (!find_module_base(img)) { + LOGE("Failed to find module base for %s", img->elf); + + ElfImg_destroy(img); + + return NULL; + } + + int fd = open(img->elf, O_RDONLY); + if (fd < 0) { + LOGE("failed to open %s", img->elf); + + ElfImg_destroy(img); + + return NULL; + } + + img->size = lseek(fd, 0, SEEK_END); + if (img->size <= 0) { + LOGE("lseek() failed for %s", img->elf); + + ElfImg_destroy(img); + + return NULL; + } + + img->header = (ElfW(Ehdr) *)mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0); + + close(fd); + + img->section_header = offsetOf_Shdr(img->header, img->header->e_shoff); + + uintptr_t shoff = (uintptr_t)img->section_header; + char *section_str = offsetOf_char(img->header, img->section_header[img->header->e_shstrndx].sh_offset); + + for (int i = 0; i < img->header->e_shnum; i++, shoff += img->header->e_shentsize) { + ElfW(Shdr) *section_h = (ElfW(Shdr *))shoff; + + char *sname = section_h->sh_name + section_str; + size_t entsize = section_h->sh_entsize; + + switch (section_h->sh_type) { + case SHT_DYNSYM: { + if (img->bias == -4396) { + img->dynsym = section_h; + img->dynsym_offset = section_h->sh_offset; + img->dynsym_start = offsetOf_Sym(img->header, img->dynsym_offset); + } + + break; + } + case SHT_SYMTAB: { + if (strcmp(sname, ".symtab") == 0) { + img->symtab = section_h; + img->symtab_offset = section_h->sh_offset; + img->symtab_size = section_h->sh_size; + img->symtab_count = img->symtab_size / entsize; + img->symtab_start = offsetOf_Sym(img->header, img->symtab_offset); + } + + break; + } + case SHT_STRTAB: { + if (img->bias == -4396) { + img->strtab = section_h; + img->symstr_offset = section_h->sh_offset; + img->strtab_start = offsetOf_Sym(img->header, img->symstr_offset); + } + + if (strcmp(sname, ".strtab") == 0) { + img->symstr_offset_for_symtab = section_h->sh_offset; + } + + break; + } + case SHT_PROGBITS: { + if (img->strtab == NULL || img->dynsym == NULL) + break; + + if (img->bias == -4396) { + img->bias = (off_t)section_h->sh_addr - (off_t)section_h->sh_offset; + } + + break; + } + case SHT_HASH: { + ElfW(Word) *d_un = offsetOf_Word(img->header, section_h->sh_offset); + img->nbucket_ = d_un[0]; + img->bucket_ = d_un + 2; + img->chain_ = img->bucket_ + img->nbucket_; + + break; + } + case SHT_GNU_HASH: { + ElfW(Word) *d_buf = (ElfW(Word) *)(((size_t)img->header) + section_h->sh_offset); + img->gnu_nbucket_ = d_buf[0]; + img->gnu_symndx_ = d_buf[1]; + img->gnu_bloom_size_ = d_buf[2]; + img->gnu_shift2_ = d_buf[3]; + img->gnu_bloom_filter_ = (uintptr_t *)(d_buf + 4); + img->gnu_bucket_ = (uint32_t *)(img->gnu_bloom_filter_ + img->gnu_bloom_size_); + img->gnu_chain_ = img->gnu_bucket_ + img->gnu_nbucket_ - img->gnu_symndx_; + + break; + } + } + } + + return img; +} + +ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash) { + if (img->nbucket_ == 0) + return 0; + + char *strings = (char *)img->strtab_start; + + for (size_t n = img->bucket_[hash % img->nbucket_]; n != 0; n = img->chain_[n]) { + ElfW(Sym) *sym = img->dynsym_start + n; + + if (strncmp(name, strings + sym->st_name, strlen(name)) == 0) + return sym->st_value; + } + return 0; +} + +ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) { + static size_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8; + + if (img->gnu_nbucket_ == 0 || img->gnu_bloom_size_ == 0) + return 0; + + size_t bloom_word = + img->gnu_bloom_filter_[(hash / bloom_mask_bits) % img->gnu_bloom_size_]; + uintptr_t mask = 0 | (uintptr_t)1 << (hash % bloom_mask_bits) | + (uintptr_t)1 << ((hash >> img->gnu_shift2_) % bloom_mask_bits); + if ((mask & bloom_word) == mask) { + size_t sym_index = img->gnu_bucket_[hash % img->gnu_nbucket_]; + if (sym_index >= img->gnu_symndx_) { + char *strings = (char *)img->strtab_start; + do { + ElfW(Sym) *sym = img->dynsym_start + sym_index; + + if (((img->gnu_chain_[sym_index] ^ hash) >> 1) == 0 && + name == strings + sym->st_name) { + return sym->st_value; + } + } while ((img->gnu_chain_[sym_index++] & 1) == 0); + } + } + + return 0; +} + +ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) { + size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img); + if (valid_symtabs_amount == 0) return 0; + + if (!img->symtabs_) { + img->symtabs_ = (struct symtabs *)calloc(1, sizeof(struct symtabs) * valid_symtabs_amount); + if (!img->symtabs_) return 0; + + + if (img->symtab_start != NULL && img->symstr_offset_for_symtab != 0) { + ElfW(Off) i = 0; + for (ElfW(Off) pos = 0; pos < img->symtab_count; pos++) { + unsigned int st_type = ELF_ST_TYPE(img->symtab_start[pos].st_info); + const char *st_name = offsetOf_char(img->header, img->symstr_offset_for_symtab + img->symtab_start[pos].st_name); + + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && img->symtab_start[pos].st_size) { + img->symtabs_[i].name = strdup(st_name); + img->symtabs_[i].sym = &img->symtab_start[pos]; + + i++; + } + } + } + } + + for (size_t i = 0; i < valid_symtabs_amount; i++) { + if (strcmp(name, img->symtabs_[i].name) != 0) continue; + + return img->symtabs_[i].sym->st_value; + } + + return 0; +} + +ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *name) { + size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img); + if (valid_symtabs_amount == 0) return 0; + + if (!img->symtabs_) { + img->symtabs_ = (struct symtabs *)malloc(sizeof(struct symtabs) * valid_symtabs_amount); + if (!img->symtabs_) return 0; + + if (img->symtab_start != NULL && img->symstr_offset_for_symtab != 0) { + ElfW(Off) i = 0; + for (ElfW(Off) pos = 0; pos < img->symtab_count; pos++) { + unsigned int st_type = ELF_ST_TYPE(img->symtab_start[pos].st_info); + const char *st_name = offsetOf_char(img->header, img->symstr_offset_for_symtab + img->symtab_start[pos].st_name); + + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && img->symtab_start[pos].st_size) { + img->symtabs_[i].name = strdup(st_name); + img->symtabs_[i].sym = &img->symtab_start[pos]; + + i++; + } + } + } + } + + for (size_t i = 0; i < valid_symtabs_amount; i++) { + if (strlen(img->symtabs_[i].name) < strlen(name)) + continue; + + if (strncmp(img->symtabs_[i].name, name, strlen(name)) == 0) + return img->symtabs_[i].sym->st_value; + } + + return 0; +} + +ElfW(Addr) getSymbOffset(ElfImg *img, const char *name) { + ElfW(Addr) offset = GnuLookup(img, name, GnuHash(name)); + if (offset > 0) return offset; + + offset = ElfLookup(img, name, ElfHash(name)); + if (offset > 0) return offset; + + offset = LinearLookup(img, name); + if (offset > 0) return offset; + + return 0; +} + +ElfW(Addr) getSymbAddress(ElfImg *img, const char *name) { + ElfW(Addr) offset = getSymbOffset(img, name); + + if (offset < 0 || !img->base) return 0; + + return ((uintptr_t)img->base + offset - img->bias); +} + +ElfW(Addr) getSymbAddressByPrefix(ElfImg *img, const char *prefix) { + ElfW(Addr) offset = LinearLookupByPrefix(img, prefix); + + if (offset < 0 || !img->base) return 0; + + return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias); +} + +void *getSymbValueByPrefix(ElfImg *img, const char *prefix) { + ElfW(Addr) address = getSymbAddressByPrefix(img, prefix); + + return address == 0 ? NULL : *((void **)address); +} diff --git a/loader/src/common/elf_util.cpp b/loader/src/common/elf_util.cpp deleted file mode 100644 index 2ce61b8..0000000 --- a/loader/src/common/elf_util.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This file is part of LSPosed. - * - * LSPosed is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LSPosed is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LSPosed. If not, see . - * - * Copyright (C) 2019 Swift Gan - * Copyright (C) 2021 LSPosed Contributors - */ -#include -#include -#include -#include -#include -#include -#include -#include "elf_util.h" - -using namespace SandHook; - -template -inline constexpr auto offsetOf(ElfW(Ehdr) *head, ElfW(Off) off) { - return reinterpret_cast, T, T *>>( - reinterpret_cast(head) + off); -} - -ElfImg::ElfImg(std::string_view base_name) : elf(base_name) { - if (!findModuleBase()) { - base = nullptr; - return; - } - - //load elf - int fd = open(elf.data(), O_RDONLY); - if (fd < 0) { - // LOGE("failed to open %s", elf.data()); - return; - } - - size = lseek(fd, 0, SEEK_END); - if (size <= 0) { - // LOGE("lseek() failed for %s", elf.data()); - } - - header = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); - - close(fd); - - section_header = offsetOf(header, header->e_shoff); - - auto shoff = reinterpret_cast(section_header); - char *section_str = offsetOf(header, section_header[header->e_shstrndx].sh_offset); - - for (int i = 0; i < header->e_shnum; i++, shoff += header->e_shentsize) { - auto *section_h = (ElfW(Shdr) *) shoff; - char *sname = section_h->sh_name + section_str; - auto entsize = section_h->sh_entsize; - switch (section_h->sh_type) { - case SHT_DYNSYM: { - if (bias == -4396) { - dynsym = section_h; - dynsym_offset = section_h->sh_offset; - dynsym_start = offsetOf(header, dynsym_offset); - } - break; - } - case SHT_SYMTAB: { - if (strcmp(sname, ".symtab") == 0) { - symtab = section_h; - symtab_offset = section_h->sh_offset; - symtab_size = section_h->sh_size; - symtab_count = symtab_size / entsize; - symtab_start = offsetOf(header, symtab_offset); - } - break; - } - case SHT_STRTAB: { - if (bias == -4396) { - strtab = section_h; - symstr_offset = section_h->sh_offset; - strtab_start = offsetOf(header, symstr_offset); - } - if (strcmp(sname, ".strtab") == 0) { - symstr_offset_for_symtab = section_h->sh_offset; - } - break; - } - case SHT_PROGBITS: { - if (strtab == nullptr || dynsym == nullptr) break; - if (bias == -4396) { - bias = (off_t) section_h->sh_addr - (off_t) section_h->sh_offset; - } - break; - } - case SHT_HASH: { - auto *d_un = offsetOf(header, section_h->sh_offset); - nbucket_ = d_un[0]; - bucket_ = d_un + 2; - chain_ = bucket_ + nbucket_; - break; - } - case SHT_GNU_HASH: { - auto *d_buf = reinterpret_cast(((size_t) header) + - section_h->sh_offset); - gnu_nbucket_ = d_buf[0]; - gnu_symndx_ = d_buf[1]; - gnu_bloom_size_ = d_buf[2]; - gnu_shift2_ = d_buf[3]; - gnu_bloom_filter_ = reinterpret_cast(d_buf + 4); - gnu_bucket_ = reinterpret_cast(gnu_bloom_filter_ + - gnu_bloom_size_); - gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - gnu_symndx_; - break; - } - } - } -} - -ElfW(Addr) ElfImg::ElfLookup(std::string_view name, uint32_t hash) const { - if (nbucket_ == 0) return 0; - - char *strings = (char *) strtab_start; - - for (auto n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { - auto *sym = dynsym_start + n; - if (name == strings + sym->st_name) { - return sym->st_value; - } - } - return 0; -} - -ElfW(Addr) ElfImg::GnuLookup(std::string_view name, uint32_t hash) const { - static constexpr auto bloom_mask_bits = sizeof(ElfW(Addr)) * 8; - - if (gnu_nbucket_ == 0 || gnu_bloom_size_ == 0) return 0; - - auto bloom_word = gnu_bloom_filter_[(hash / bloom_mask_bits) % gnu_bloom_size_]; - uintptr_t mask = 0 - | (uintptr_t) 1 << (hash % bloom_mask_bits) - | (uintptr_t) 1 << ((hash >> gnu_shift2_) % bloom_mask_bits); - if ((mask & bloom_word) == mask) { - auto sym_index = gnu_bucket_[hash % gnu_nbucket_]; - if (sym_index >= gnu_symndx_) { - char *strings = (char *) strtab_start; - do { - auto *sym = dynsym_start + sym_index; - if (((gnu_chain_[sym_index] ^ hash) >> 1) == 0 - && name == strings + sym->st_name) { - return sym->st_value; - } - } while ((gnu_chain_[sym_index++] & 1) == 0); - } - } - return 0; -} - -ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { - if (symtabs_.empty()) { - symtabs_.reserve(symtab_count); - if (symtab_start != nullptr && symstr_offset_for_symtab != 0) { - for (ElfW(Off) i = 0; i < symtab_count; i++) { - unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); - const char *st_name = offsetOf(header, symstr_offset_for_symtab + - symtab_start[i].st_name); - if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) { - symtabs_.emplace(st_name, &symtab_start[i]); - } - } - } - } - - if (auto i = symtabs_.find(name); i != symtabs_.end()) { - return i->second->st_value; - } else { - return 0; - } -} - -ElfW(Addr) ElfImg::LinearLookupByPrefix(std::string_view name) const { - if (symtabs_.empty()) { - symtabs_.reserve(symtab_count); - if (symtab_start != nullptr && symstr_offset_for_symtab != 0) { - for (ElfW(Off) i = 0; i < symtab_count; i++) { - unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); - const char *st_name = offsetOf(header, symstr_offset_for_symtab + - symtab_start[i].st_name); - if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) { - symtabs_.emplace(st_name, &symtab_start[i]); - } - } - } - } - - auto size = name.size(); - for (auto symtab : symtabs_) { - if (symtab.first.size() < size) continue; - - if (symtab.first.substr(0, size) == name) { - return symtab.second->st_value; - } - } - - return 0; -} - - -ElfImg::~ElfImg() { - //open elf file local - if (buffer) { - free(buffer); - buffer = nullptr; - } - //use mmap - if (header) { - munmap(header, size); - } -} - -ElfW(Addr) ElfImg::getSymbOffset(std::string_view name, uint32_t gnu_hash, uint32_t elf_hash) const { - if (auto offset = GnuLookup(name, gnu_hash); offset > 0) { - // LOGD("found %s %p in %s in dynsym by gnuhash", name.data(), reinterpret_cast(offset), elf.data()); - return offset; - } else if (offset = ElfLookup(name, elf_hash); offset > 0) { - // LOGD("found %s %p in %s in dynsym by elfhash", name.data(), reinterpret_cast(offset), elf.data()); - return offset; - } else if (offset = LinearLookup(name); offset > 0) { - // LOGD("found %s %p in %s in symtab by linear lookup", name.data(), reinterpret_cast(offset), elf.data()); - return offset; - } else { - return 0; - } - -} - -bool ElfImg::findModuleBase() { - dl_iterate_phdr([](struct dl_phdr_info *info, size_t size, void *data) -> int { - (void) size; - - if ((info)->dlpi_name == nullptr) { - return 0; - } - - auto *self = reinterpret_cast(data); - if (strstr(info->dlpi_name, self->elf.data())) { - self->elf = info->dlpi_name; - self->base = reinterpret_cast(info->dlpi_addr); - return 1; - } - return 0; - }, this); - return base != 0; -} diff --git a/loader/src/common/files.cpp b/loader/src/common/files.cpp deleted file mode 100644 index ade40de..0000000 --- a/loader/src/common/files.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include - -#include "files.hpp" -#include "misc.hpp" - -using namespace std::string_view_literals; - -void file_readline(bool trim, FILE *fp, const std::function &fn) { - size_t len = 1024; - char *buf = (char *) malloc(len); - char *start; - ssize_t read; - while ((read = getline(&buf, &len, fp)) >= 0) { - start = buf; - if (trim) { - while (read && "\n\r "sv.find(buf[read - 1]) != std::string::npos) - --read; - buf[read] = '\0'; - while (*start == ' ') - ++start; - } - if (!fn(start)) - break; - } - free(buf); -} - -void file_readline(bool trim, const char *file, const std::function &fn) { - if (auto fp = open_file(file, "re")) - file_readline(trim, fp.get(), fn); -} -void file_readline(const char *file, const std::function &fn) { - file_readline(false, file, fn); -} - -std::vector parse_mount_info(const char *pid) { - char buf[PATH_MAX] = {}; - snprintf(buf, sizeof(buf), "/proc/%s/mountinfo", pid); - std::vector result; - - file_readline(buf, [&result](std::string_view line) -> bool { - int root_start = 0, root_end = 0; - int target_start = 0, target_end = 0; - int vfs_option_start = 0, vfs_option_end = 0; - int type_start = 0, type_end = 0; - int source_start = 0, source_end = 0; - int fs_option_start = 0, fs_option_end = 0; - int optional_start = 0, optional_end = 0; - unsigned int id, parent, maj, min; - sscanf(line.data(), - "%u " // (1) id - "%u " // (2) parent - "%u:%u " // (3) maj:min - "%n%*s%n " // (4) mountroot - "%n%*s%n " // (5) target - "%n%*s%n" // (6) vfs options (fs-independent) - "%n%*[^-]%n - " // (7) optional fields - "%n%*s%n " // (8) FS type - "%n%*s%n " // (9) source - "%n%*s%n", // (10) fs options (fs specific) - &id, &parent, &maj, &min, &root_start, &root_end, &target_start, - &target_end, &vfs_option_start, &vfs_option_end, - &optional_start, &optional_end, &type_start, &type_end, - &source_start, &source_end, &fs_option_start, &fs_option_end); - - auto root = line.substr(root_start, root_end - root_start); - auto target = line.substr(target_start, target_end - target_start); - auto vfs_option = - line.substr(vfs_option_start, vfs_option_end - vfs_option_start); - ++optional_start; - --optional_end; - auto optional = line.substr( - optional_start, - optional_end - optional_start > 0 ? optional_end - optional_start : 0); - - auto type = line.substr(type_start, type_end - type_start); - auto source = line.substr(source_start, source_end - source_start); - auto fs_option = - line.substr(fs_option_start, fs_option_end - fs_option_start); - - unsigned int shared = 0; - unsigned int master = 0; - unsigned int propagate_from = 0; - if (auto pos = optional.find("shared:"); pos != std::string_view::npos) { - shared = parse_int(optional.substr(pos + 7)); - } - if (auto pos = optional.find("master:"); pos != std::string_view::npos) { - master = parse_int(optional.substr(pos + 7)); - } - if (auto pos = optional.find("propagate_from:"); - pos != std::string_view::npos) { - propagate_from = parse_int(optional.substr(pos + 15)); - } - - result.emplace_back(mount_info { - .id = id, - .parent = parent, - .device = static_cast(makedev(maj, min)), - .root {root}, - .target {target}, - .vfs_option {vfs_option}, - .optional { - .shared = shared, - .master = master, - .propagate_from = propagate_from, - }, - .type {type}, - .source {source}, - .fs_option {fs_option}, - }); - return true; - }); - return result; -} - -sDIR make_dir(DIR *dp) { - return sDIR(dp, [](DIR *dp){ return dp ? closedir(dp) : 1; }); -} - -sFILE make_file(FILE *fp) { - return sFILE(fp, [](FILE *fp){ return fp ? fclose(fp) : 1; }); -} - -int get_path_from_fd(int fd, char *buf, size_t size) { - if (fd < 0 || !buf || size == 0) return -1; - - /* NOTE: We assume that the path is always at /data/adb/modules/xxx - which should never be longer than 128 chars. */ - char proc_path[128]; - snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); - - ssize_t len = readlink(proc_path, buf, size - 1); - if (len == -1) return -1; - - buf[len] = '\0'; - return 0; -} \ No newline at end of file diff --git a/loader/src/common/misc.c b/loader/src/common/misc.c new file mode 100644 index 0000000..bf80144 --- /dev/null +++ b/loader/src/common/misc.c @@ -0,0 +1,14 @@ +int parse_int(const char *str) { + int val = 0; + + char *c = (char *)str; + while (*c) { + if (*c > '9' || *c < '0') + return -1; + + val = val * 10 + *c - '0'; + c++; + } + + return val; +} diff --git a/loader/src/common/misc.cpp b/loader/src/common/misc.cpp deleted file mode 100644 index 0881664..0000000 --- a/loader/src/common/misc.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "misc.hpp" - -int new_daemon_thread(thread_entry entry, void *arg) { - pthread_t thread; - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - errno = pthread_create(&thread, &attr, entry, arg); - if (errno) { - PLOGE("pthread_create"); - } - return errno; -} - -int parse_int(std::string_view s) { - int val = 0; - for (char c : s) { - if (!c) break; - if (c > '9' || c < '0') - return -1; - val = val * 10 + c - '0'; - } - return val; -} - -std::list split_str(std::string_view s, std::string_view delimiter) { - std::list ret; - size_t pos = 0; - while (pos < s.size()) { - auto next = s.find(delimiter, pos); - if (next == std::string_view::npos) { - ret.emplace_back(s.substr(pos)); - break; - } - ret.emplace_back(s.substr(pos, next - pos)); - pos = next + delimiter.size(); - } - return ret; -} - -std::string join_str(const std::list& list, std::string_view delimiter) { - std::string ret; - for (auto& s : list) { - if (!ret.empty()) - ret += delimiter; - ret += s; - } - return ret; -} diff --git a/loader/src/common/socket_utils.c b/loader/src/common/socket_utils.c new file mode 100644 index 0000000..24bddc0 --- /dev/null +++ b/loader/src/common/socket_utils.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include + +#include "logging.h" + +#include "socket_utils.h" + +/* TODO: Standardize how to log errors */ +int read_fd(int fd) { + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + + int cnt = 1; + struct iovec iov = { + .iov_base = &cnt, + .iov_len = sizeof(cnt) + }; + + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf) + }; + + ssize_t ret = recvmsg(fd, &msg, MSG_WAITALL); + if (ret == -1) { + PLOGE("recvmsg"); + + return -1; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL) { + PLOGE("CMSG_FIRSTHDR"); + + return -1; + } + + int sendfd; + memcpy(&sendfd, CMSG_DATA(cmsg), sizeof(int)); + + return sendfd; +} + +char *read_string(int fd) { + size_t str_len = 0; + ssize_t read_bytes = read(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)); + + return NULL; + } + + char *buf = malloc(str_len + 1); + if (buf == NULL) { + PLOGE("allocate memory for string"); + + return NULL; + } + + read_bytes = read(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); + + free(buf); + + return NULL; + } + + if (str_len > 0) buf[str_len] = '\0'; + + return buf; +} + +#define write_func(type) \ + ssize_t write_## type(int fd, type val) { \ + return write(fd, &val, sizeof(type)); \ + } + +#define read_func(type) \ + ssize_t read_## type(int fd, type *val) { \ + return read(fd, val, sizeof(type)); \ + } + +write_func(uint8_t) +read_func(uint8_t) + +write_func(uint32_t) +read_func(uint32_t) + +write_func(size_t) +read_func(size_t) diff --git a/loader/src/common/socket_utils.cpp b/loader/src/common/socket_utils.cpp deleted file mode 100644 index 730d49d..0000000 --- a/loader/src/common/socket_utils.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include - -#include "socket_utils.h" - -namespace socket_utils { - ssize_t xread(int fd, void* buf, size_t count) { - size_t read_sz = 0; - ssize_t ret; - do { - ret = read(fd, (std::byte*) buf + read_sz, count - read_sz); - if (ret < 0) { - if (errno == EINTR) continue; - PLOGE("read"); - return ret; - } - read_sz += ret; - } while (read_sz != count && ret != 0); - if (read_sz != count) { - PLOGE("read (%zu != %zu)", count, read_sz); - } - return read_sz; - } - - size_t xwrite(int fd, const void* buf, size_t count) { - size_t write_sz = 0; - ssize_t ret; - do { - ret = write(fd, (std::byte*) buf + write_sz, count - write_sz); - if (ret < 0) { - if (errno == EINTR) continue; - PLOGE("write"); - return write_sz; - } - write_sz += ret; - } while (write_sz != count && ret != 0); - if (write_sz != count) { - PLOGE("write (%zu != %zu)", count, write_sz); - } - return write_sz; - } - - ssize_t xrecvmsg(int sockfd, struct msghdr* msg, int flags) { - int rec = recvmsg(sockfd, msg, flags); - if (rec < 0) PLOGE("recvmsg"); - return rec; - } - - template - inline T read_exact_or(int fd, T fail) { - T res; - return sizeof(T) == xread(fd, &res, sizeof(T)) ? res : fail; - } - - template - inline bool write_exact(int fd, T val) { - return sizeof(T) == xwrite(fd, &val, sizeof(T)); - } - - uint8_t read_u8(int fd) { - return read_exact_or(fd, 0); - } - - uint32_t read_u32(int fd) { - return read_exact_or(fd, 0); - } - - size_t read_usize(int fd) { - return read_exact_or(fd, 0); - } - - bool write_usize(int fd, size_t val) { - return write_exact(fd, val); - } - - std::string read_string(int fd) { - size_t len = read_usize(fd); - - char buf[len + 1]; - xread(fd, buf, len); - - buf[len] = '\0'; - - return buf; - } - - bool write_u8(int fd, uint8_t val) { - return write_exact(fd, val); - } - - void* recv_fds(int sockfd, char* cmsgbuf, size_t bufsz, int cnt) { - iovec iov = { - .iov_base = &cnt, - .iov_len = sizeof(cnt), - }; - msghdr msg = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsgbuf, - .msg_controllen = bufsz - }; - - xrecvmsg(sockfd, &msg, MSG_WAITALL); - cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - - if (msg.msg_controllen != bufsz || - cmsg == nullptr || - // TODO: pass from rust: 20, expected: 16 - // cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - return nullptr; - } - - return CMSG_DATA(cmsg); - } - - int recv_fd(int sockfd) { - char cmsgbuf[CMSG_SPACE(sizeof(int))]; - - void* data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1); - if (data == nullptr) return -1; - - int result; - memcpy(&result, data, sizeof(int)); - return result; - } - - bool write_u32(int fd, uint32_t val) { - return write_exact(fd, val); - } - - bool write_string(int fd, std::string_view str) { - return write_usize(fd, str.size()) && str.size() == xwrite(fd, str.data(), str.size()); - } -} diff --git a/loader/src/include/daemon.h b/loader/src/include/daemon.h index 9bc3617..2a30761 100644 --- a/loader/src/include/daemon.h +++ b/loader/src/include/daemon.h @@ -1,113 +1,91 @@ -#pragma once +#ifndef DAEMON_H +#define DAEMON_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include -#include -#include #include -#include -#if defined(__LP64__) -# define LP_SELECT(lp32, lp64) lp64 +#ifdef __LP64__ + #define LP_SELECT(lp32, lp64) lp64 #else -# define LP_SELECT(lp32, lp64) lp32 + #define LP_SELECT(lp32, lp64) lp32 #endif -constexpr auto kCPSocketName = "/" LP_SELECT("cp32", "cp64") ".sock"; +#define SOCKET_FILE_NAME LP_SELECT("cp32", "cp64") ".sock" -class UniqueFd { - using Fd = int; -public: - UniqueFd() = default; - - UniqueFd(Fd fd) : fd_(fd) {} - - ~UniqueFd() { if (fd_ >= 0) close(fd_); } - - // Disallow copy - UniqueFd(const UniqueFd&) = delete; - - UniqueFd& operator=(const UniqueFd&) = delete; - - // Allow move - UniqueFd(UniqueFd&& other) { std::swap(fd_, other.fd_); } - - UniqueFd& operator=(UniqueFd&& other) { - std::swap(fd_, other.fd_); - return *this; - } - - // Implict cast to Fd - operator const Fd&() const { return fd_; } - -private: - Fd fd_ = -1; +enum rezygiskd_actions { + PingHeartbeat, + GetProcessFlags, + GetInfo, + ReadModules, + RequestCompanionSocket, + GetModuleDir, + ZygoteRestart, + SystemServerStarted, + UpdateMountNamespace }; -struct zygote_modules { +struct zygisk_modules { char **modules; size_t modules_count; }; -enum zygote_root_impl { - ZYGOTE_ROOT_IMPL_NONE, - ZYGOTE_ROOT_IMPL_APATCH, - ZYGOTE_ROOT_IMPL_KERNELSU, - ZYGOTE_ROOT_IMPL_MAGISK +enum root_impl { + ROOT_IMPL_NONE, + ROOT_IMPL_APATCH, + ROOT_IMPL_KERNELSU, + ROOT_IMPL_MAGISK }; -struct zygote_info { - struct zygote_modules *modules; - enum zygote_root_impl root_impl; +struct rezygisk_info { + struct zygisk_modules *modules; + enum root_impl root_impl; pid_t pid; bool running; }; enum mount_namespace_state { - Clean, - Rooted, - Module + Clean, + Rooted, + Module }; -namespace zygiskd { +#define TMP_PATH "/data/adb/rezygisk" - struct ModuleInfo { - std::string path; - /* TODO: Perhaps we can also remove this and just send paths? */ - std::string name; - - inline explicit ModuleInfo(std::string path, std::string name) : path(path), name(name) {} - }; - - enum class SocketAction { - PingHeartBeat, - GetProcessFlags, - GetInfo, - ReadModules, - RequestCompanionSocket, - GetModuleDir, - ZygoteRestart, - SystemServerStarted, - UpdateMountNamespace - }; - - void Init(const char *path); - - std::string GetTmpPath(); - - bool PingHeartbeat(); - - std::vector ReadModules(); - - uint32_t GetProcessFlags(uid_t uid); - - int ConnectCompanion(size_t index); - - int GetModuleDir(size_t index); - - void ZygoteRestart(); - - void SystemServerStarted(); - - void GetInfo(struct zygote_info *info); - - std::string UpdateMountNamespace(enum mount_namespace_state mns_state); +static inline const char *rezygiskd_get_path() { + return TMP_PATH; } + +int rezygiskd_connect(uint8_t retry); + +bool rezygiskd_ping(); + +uint32_t rezygiskd_get_process_flags(uid_t uid); + +void rezygiskd_get_info(struct rezygisk_info *info); + +void free_rezygisk_info(struct rezygisk_info *info); + +bool rezygiskd_read_modules(struct zygisk_modules *modules); + +void free_modules(struct zygisk_modules *modules); + +int rezygiskd_connect_companion(size_t index); + +int rezygiskd_get_module_dir(size_t index); + +void rezygiskd_zygote_restart(); + +void rezygiskd_system_server_started(); + +bool rezygiskd_update_mns(enum mount_namespace_state nms_state, char *buf, size_t buf_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DAEMON_H */ \ No newline at end of file diff --git a/loader/src/include/elf_util.h b/loader/src/include/elf_util.h index 42a08c6..e1c4949 100644 --- a/loader/src/include/elf_util.h +++ b/loader/src/include/elf_util.h @@ -1,152 +1,74 @@ -/* - * This file is part of LSPosed. - * - * LSPosed is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LSPosed is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LSPosed. If not, see . - * - * Copyright (C) 2019 Swift Gan - * Copyright (C) 2021 LSPosed Contributors - */ -#ifndef SANDHOOK_ELF_UTIL_H -#define SANDHOOK_ELF_UTIL_H +#ifndef ELF_UTIL_H +#define ELF_UTIL_H -#include -#include +#include +#include #include #include -#include -#include #define SHT_GNU_HASH 0x6ffffff6 -namespace SandHook { - class ElfImg { - public: +struct symtabs { + char *name; + ElfW(Sym) *sym; +}; - ElfImg(std::string_view elf); +typedef struct { + char *elf; + void *base; + char *buffer; + off_t size; + off_t bias; + ElfW(Ehdr) *header; + ElfW(Shdr) *section_header; + ElfW(Shdr) *symtab; + ElfW(Shdr) *strtab; + ElfW(Shdr) *dynsym; + ElfW(Sym) *symtab_start; + ElfW(Sym) *dynsym_start; + ElfW(Sym) *strtab_start; + ElfW(Off) symtab_count; + ElfW(Off) symstr_offset; + ElfW(Off) symstr_offset_for_symtab; + ElfW(Off) symtab_offset; + ElfW(Off) dynsym_offset; + ElfW(Off) symtab_size; - constexpr ElfW(Addr) getSymbOffset(std::string_view name) const { - return getSymbOffset(name, GnuHash(name), ElfHash(name)); - } + uint32_t nbucket_; + uint32_t *bucket_; + uint32_t *chain_; - constexpr ElfW(Addr) getSymbAddress(std::string_view name) const { - ElfW(Addr) offset = getSymbOffset(name); - if (offset > 0 && base != nullptr) { - return static_cast((uintptr_t) base + offset - bias); - } else { - return 0; - } - } + uint32_t gnu_nbucket_; + uint32_t gnu_symndx_; + uint32_t gnu_bloom_size_; + uint32_t gnu_shift2_; + uintptr_t *gnu_bloom_filter_; + uint32_t *gnu_bucket_; + uint32_t *gnu_chain_; - constexpr ElfW(Addr) getSymbAddressByPrefix(std::string_view prefix) const { - ElfW(Addr) offset = LinearLookupByPrefix(prefix); - if (offset > 0 && base != nullptr) { - return static_cast((uintptr_t) base + offset - bias); - } else { - return 0; - } - } + struct symtabs *symtabs_; +} ElfImg; - template - constexpr T getSymbAddress(std::string_view name) const { - return reinterpret_cast(getSymbAddress(name)); - } +void ElfImg_destroy(ElfImg *img); - template - constexpr T getSymbAddressByPrefix(std::string_view prefix) const { - return reinterpret_cast(getSymbAddressByPrefix(prefix)); - } +ElfImg *ElfImg_create(const char *elf); - bool isValid() const { - return base != nullptr; - } +ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash); - const std::string name() const { - return elf; - } +ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash); - ~ElfImg(); +ElfW(Addr) LinearLookup(ElfImg *restrict img, const char *restrict name); - private: - ElfW(Addr) getSymbOffset(std::string_view name, uint32_t gnu_hash, uint32_t elf_hash) const; +ElfW(Addr) LinearLookupByPrefix(ElfImg *restrict img, const char *name); - ElfW(Addr) ElfLookup(std::string_view name, uint32_t hash) const; +int dl_cb(struct dl_phdr_info *info, size_t size, void *data); - ElfW(Addr) GnuLookup(std::string_view name, uint32_t hash) const; +ElfW(Addr) getSymbOffset(ElfImg *img, const char *name); - ElfW(Addr) LinearLookup(std::string_view name) const; +ElfW(Addr) getSymbAddress(ElfImg *img, const char *name); - ElfW(Addr) LinearLookupByPrefix(std::string_view name) const; +ElfW(Addr) getSymbAddressByPrefix(ElfImg *img, const char *prefix); - constexpr static uint32_t ElfHash(std::string_view name); +void *getSymbValueByPrefix(ElfImg *img, const char *prefix); - constexpr static uint32_t GnuHash(std::string_view name); - - bool findModuleBase(); - - std::string elf; - void *base = nullptr; - char *buffer = nullptr; - off_t size = 0; - off_t bias = -4396; - ElfW(Ehdr) *header = nullptr; - ElfW(Shdr) *section_header = nullptr; - ElfW(Shdr) *symtab = nullptr; - ElfW(Shdr) *strtab = nullptr; - ElfW(Shdr) *dynsym = nullptr; - ElfW(Sym) *symtab_start = nullptr; - ElfW(Sym) *dynsym_start = nullptr; - ElfW(Sym) *strtab_start = nullptr; - ElfW(Off) symtab_count = 0; - ElfW(Off) symstr_offset = 0; - ElfW(Off) symstr_offset_for_symtab = 0; - ElfW(Off) symtab_offset = 0; - ElfW(Off) dynsym_offset = 0; - ElfW(Off) symtab_size = 0; - - uint32_t nbucket_{}; - uint32_t *bucket_ = nullptr; - uint32_t *chain_ = nullptr; - - uint32_t gnu_nbucket_{}; - uint32_t gnu_symndx_{}; - uint32_t gnu_bloom_size_; - uint32_t gnu_shift2_; - uintptr_t *gnu_bloom_filter_; - uint32_t *gnu_bucket_; - uint32_t *gnu_chain_; - - mutable std::unordered_map symtabs_; - }; - - constexpr uint32_t ElfImg::ElfHash(std::string_view name) { - uint32_t h = 0, g = 0; - for (unsigned char p: name) { - h = (h << 4) + p; - g = h & 0xf0000000; - h ^= g; - h ^= g >> 24; - } - return h; - } - - constexpr uint32_t ElfImg::GnuHash(std::string_view name) { - uint32_t h = 5381; - for (unsigned char p: name) { - h += (h << 5) + p; - } - return h; - } -} - -#endif //SANDHOOK_ELF_UTIL_H \ No newline at end of file +#endif /* ELF_UTIL_H */ diff --git a/loader/src/include/files.hpp b/loader/src/include/files.hpp deleted file mode 100644 index 4f16e0c..0000000 --- a/loader/src/include/files.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include - -struct mount_info { - unsigned int id; - unsigned int parent; - dev_t device; - std::string root; - std::string target; - std::string vfs_option; - struct { - unsigned int shared; - unsigned int master; - unsigned int propagate_from; - } optional; - std::string type; - std::string source; - std::string fs_option; -}; - -void file_readline(bool trim, FILE *fp, const std::function &fn); -void file_readline(bool trim, const char *file, const std::function &fn); -void file_readline(const char *file, const std::function &fn); - -std::vector parse_mount_info(const char *pid); - -int get_path_from_fd(int fd, char *buf, size_t size); - -using sFILE = std::unique_ptr; -using sDIR = std::unique_ptr; -sDIR make_dir(DIR *dp); -sFILE make_file(FILE *fp); - -static inline sDIR open_dir(const char *path) { - return make_dir(opendir(path)); -} - -static inline sDIR xopen_dir(const char *path) { - return make_dir(opendir(path)); -} - -static inline sDIR xopen_dir(int dirfd) { - return make_dir(fdopendir(dirfd)); -} - -static inline sFILE open_file(const char *path, const char *mode) { - return make_file(fopen(path, mode)); -} - -static inline sFILE xopen_file(const char *path, const char *mode) { - return make_file(fopen(path, mode)); -} - -static inline sFILE xopen_file(int fd, const char *mode) { - return make_file(fdopen(fd, mode)); -} diff --git a/loader/src/include/misc.h b/loader/src/include/misc.h new file mode 100644 index 0000000..168b9fb --- /dev/null +++ b/loader/src/include/misc.h @@ -0,0 +1,17 @@ +#ifndef MISC_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Bionic's atoi runs through strtol(). + * Use our own implementation for faster conversion. + */ +int parse_int(const char *str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MISC_H */ \ No newline at end of file diff --git a/loader/src/include/misc.hpp b/loader/src/include/misc.hpp deleted file mode 100644 index b526145..0000000 --- a/loader/src/include/misc.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "logging.h" - -#define DISALLOW_COPY_AND_MOVE(clazz) \ -clazz(const clazz &) = delete; \ -clazz(clazz &&) = delete; - -class mutex_guard { - DISALLOW_COPY_AND_MOVE(mutex_guard) -public: - explicit mutex_guard(pthread_mutex_t &m): mutex(&m) { - pthread_mutex_lock(mutex); - } - void unlock() { - pthread_mutex_unlock(mutex); - mutex = nullptr; - } - ~mutex_guard() { - if (mutex) pthread_mutex_unlock(mutex); - } -private: - pthread_mutex_t *mutex; -}; - -using thread_entry = void *(*)(void *); -int new_daemon_thread(thread_entry entry, void *arg); - -static inline bool str_contains(std::string_view s, std::string_view ss) { - return s.find(ss) != std::string_view::npos; -} - -template -class stateless_allocator { -public: - using value_type = T; - T *allocate(size_t num) { return static_cast(Impl::allocate(sizeof(T) * num)); } - void deallocate(T *ptr, size_t num) { Impl::deallocate(ptr, sizeof(T) * num); } - stateless_allocator() = default; - stateless_allocator(const stateless_allocator&) = default; - stateless_allocator(stateless_allocator&&) = default; - template - stateless_allocator(const stateless_allocator&) {} - bool operator==(const stateless_allocator&) { return true; } - bool operator!=(const stateless_allocator&) { return false; } -}; - -template -class reversed_container { -public: - reversed_container(T &base) : base(base) {} - decltype(std::declval().rbegin()) begin() { return base.rbegin(); } - decltype(std::declval().crbegin()) begin() const { return base.crbegin(); } - decltype(std::declval().crbegin()) cbegin() const { return base.crbegin(); } - decltype(std::declval().rend()) end() { return base.rend(); } - decltype(std::declval().crend()) end() const { return base.crend(); } - decltype(std::declval().crend()) cend() const { return base.crend(); } -private: - T &base; -}; - -template -reversed_container reversed(T &base) { - return reversed_container(base); -} - -template -static inline void default_new(T *&p) { p = new T(); } - -template -static inline void default_new(std::unique_ptr &p) { p.reset(new T()); } - -struct StringCmp { - using is_transparent = void; - bool operator()(std::string_view a, std::string_view b) const { return a < b; } -}; - -/* - * Bionic's atoi runs through strtol(). - * Use our own implementation for faster conversion. - */ -int parse_int(std::string_view s); - -std::list split_str(std::string_view s, std::string_view delimiter); - -std::string join_str(const std::list& list, std::string_view delimiter); - -template -static inline T align_to(T v, int a) { - static_assert(std::is_integral::value); - return (v + a - 1) / a * a; -} diff --git a/loader/src/include/native_bridge_callbacks.h b/loader/src/include/native_bridge_callbacks.h deleted file mode 100644 index 60ff4be..0000000 --- a/loader/src/include/native_bridge_callbacks.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -template -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; -}; diff --git a/loader/src/include/socket_utils.h b/loader/src/include/socket_utils.h index 09330de..4c40559 100644 --- a/loader/src/include/socket_utils.h +++ b/loader/src/include/socket_utils.h @@ -1,31 +1,25 @@ -#pragma once +#ifndef SOCKET_UTILS_H +#define SOCKET_UTILS_H -#include -#include +#include -#include "logging.h" +int read_fd(int fd); + +char *read_string(int fd); -namespace socket_utils { +#define write_func_def(type) \ + ssize_t write_## type(int fd, type val) - ssize_t xread(int fd, void *buf, size_t count); +#define read_func_def(type) \ + ssize_t read_## type(int fd, type *val) - size_t xwrite(int fd, const void *buf, size_t count); +write_func_def(uint8_t); +read_func_def(uint8_t); - uint8_t read_u8(int fd); +write_func_def(uint32_t); +read_func_def(uint32_t); - uint32_t read_u32(int fd); +write_func_def(size_t); +read_func_def(size_t); - size_t read_usize(int fd); - - std::string read_string(int fd); - - bool write_u8(int fd, uint8_t val); - - bool write_u32(int fd, uint32_t val); - - int recv_fd(int fd); - - bool write_usize(int fd, size_t val); - - bool write_string(int fd, std::string_view str); -} +#endif /* SOCKET_UTILS_H */ \ No newline at end of file diff --git a/loader/src/include/solist.hpp b/loader/src/include/solist.hpp deleted file mode 100644 index 6835517..0000000 --- a/loader/src/include/solist.hpp +++ /dev/null @@ -1,211 +0,0 @@ -// -// Original from https://github.com/LSPosed/NativeDetector/blob/master/app/src/main/jni/solist.cpp -// -#pragma once - -#include -#include "elf_util.h" -#include "logging.h" - -namespace SoList { - class SoInfo { - public: - #ifdef __LP64__ - inline static size_t solist_size_offset = 0x18; - inline static size_t solist_next_offset = 0x28; - constexpr static size_t solist_realpath_offset = 0x1a8; - #else - inline static size_t solist_size_offset = 0x90; - inline static size_t solist_next_offset = 0xa4; - constexpr static size_t solist_realpath_offset = 0x174; - #endif - - inline static const char *(*get_realpath_sym)(SoInfo *) = NULL; - inline static void (*soinfo_free)(SoInfo *) = NULL; - - inline SoInfo *get_next() { - return *(SoInfo **) ((uintptr_t) this + solist_next_offset); - } - - inline size_t get_size() { - return *(size_t *) ((uintptr_t) this + solist_size_offset); - } - - inline const char *get_path() { - if (get_realpath_sym) return get_realpath_sym(this); - - return ((std::string *) ((uintptr_t) this + solist_realpath_offset))->c_str(); - } - - void set_next(SoInfo *si) { - *(SoInfo **) ((uintptr_t) this + solist_next_offset) = si; - } - - void set_size(size_t size) { - *(size_t *) ((uintptr_t) this + solist_size_offset) = size; - } - }; - - class ProtectedDataGuard { - public: - ProtectedDataGuard() { - if (ctor != nullptr) - (this->*ctor)(); - } - - ~ProtectedDataGuard() { - if (dtor != nullptr) - (this->*dtor)(); - } - - static bool setup(const SandHook::ElfImg &linker) { - ctor = MemFunc{.data = {.p = reinterpret_cast(linker.getSymbAddress( - "__dl__ZN18ProtectedDataGuardC2Ev")), .adj = 0}}.f; - dtor = MemFunc{.data = {.p = reinterpret_cast(linker.getSymbAddress( - "__dl__ZN18ProtectedDataGuardD2Ev")), .adj = 0}}.f; - return ctor != nullptr && dtor != nullptr; - } - - ProtectedDataGuard(const ProtectedDataGuard &) = delete; - - void operator=(const ProtectedDataGuard &) = delete; - - private: - using FuncType = void (ProtectedDataGuard::*)(); - - inline static FuncType ctor = NULL; - inline static FuncType dtor = NULL; - - union MemFunc { - FuncType f; - - struct { - void *p; - std::ptrdiff_t adj; - } data; - }; - }; - - - static SoInfo *solist = NULL; - static SoInfo *somain = NULL; - static SoInfo **sonext = NULL; - - static uint64_t *g_module_load_counter = NULL; - static uint64_t *g_module_unload_counter = NULL; - - static bool Initialize(); - - template - inline T *getStaticPointer(const SandHook::ElfImg &linker, const char *name) { - auto *addr = reinterpret_cast(linker.getSymbAddress(name)); - - return addr == NULL ? NULL : *addr; - } - - template - inline T *getStaticPointerByPrefix(const SandHook::ElfImg &linker, const char *name) { - auto *addr = reinterpret_cast(linker.getSymbAddressByPrefix(name)); - - return addr == NULL ? NULL : *addr; - } - - static bool DropSoPath(const char* target_path) { - bool path_found = false; - if (solist == NULL && !Initialize()) { - LOGE("Failed to initialize solist"); - return path_found; - } - for (auto iter = solist; iter; iter = iter->get_next()) { - if (iter->get_path() && strstr(iter->get_path(), target_path)) { - SoList::ProtectedDataGuard guard; - LOGV("dropping solist record loaded at %s with size %zu", iter->get_path(), iter->get_size()); - if (iter->get_size() > 0) { - iter->set_size(0); - SoInfo::soinfo_free(iter); - path_found = true; - } - } - } - return path_found; - } - - static void ResetCounters(size_t load, size_t unload) { - if (solist == NULL && !Initialize()) { - LOGE("Failed to initialize solist"); - return; - } - if (g_module_load_counter == NULL || g_module_unload_counter == NULL) { - LOGD("g_module counters not defined, skip reseting them"); - return; - } - auto loaded_modules = *g_module_load_counter; - auto unloaded_modules = *g_module_unload_counter; - if (loaded_modules >= load) { - *g_module_load_counter = loaded_modules - load; - LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter); - } - if (unloaded_modules >= unload) { - *g_module_unload_counter = unloaded_modules - unload; - LOGD("reset g_module_unload_counter to %zu", (size_t) *g_module_unload_counter); - } - } - - static bool Initialize() { - SandHook::ElfImg linker("/linker"); - if (!ProtectedDataGuard::setup(linker)) return false; - LOGD("found symbol ProtectedDataGuard"); - - /* INFO: Since Android 15, the symbol names for the linker have a suffix, - this makes it impossible to hardcode the symbol names. To allow - this to work on all versions, we need to iterate over the loaded - symbols and find the correct ones. - - See #63 for more information. - */ - solist = getStaticPointerByPrefix(linker, "__dl__ZL6solist"); - if (solist == NULL) return false; - LOGD("found symbol solist"); - - somain = getStaticPointerByPrefix(linker, "__dl__ZL6somain"); - if (somain == NULL) return false; - LOGD("found symbol somain"); - - sonext = linker.getSymbAddressByPrefix("__dl__ZL6sonext"); - if (sonext == NULL) return false; - LOGD("found symbol sonext"); - - SoInfo *vdso = getStaticPointerByPrefix(linker, "__dl__ZL4vdso"); - if (vdso != NULL) LOGD("found symbol vdso"); - - SoInfo::get_realpath_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv")); - if (SoInfo::get_realpath_sym == NULL) return false; - LOGD("found symbol get_realpath_sym"); - - SoInfo::soinfo_free = reinterpret_cast(linker.getSymbAddressByPrefix("__dl__ZL11soinfo_freeP6soinfo")); - if (SoInfo::soinfo_free == NULL) return false; - LOGD("found symbol soinfo_free"); - - g_module_load_counter = reinterpret_cast(linker.getSymbAddress("__dl__ZL21g_module_load_counter")); - if (g_module_load_counter != NULL) LOGD("found symbol g_module_load_counter"); - - g_module_unload_counter = reinterpret_cast(linker.getSymbAddress("__dl__ZL23g_module_unload_counter")); - if (g_module_unload_counter != NULL) LOGD("found symbol g_module_unload_counter"); - - for (size_t i = 0; i < 1024 / sizeof(void *); i++) { - auto possible_field = (uintptr_t) solist + i * sizeof(void *); - auto possible_size_of_somain = *(size_t *)((uintptr_t) somain + i * sizeof(void *)); - if (possible_size_of_somain < 0x100000 && possible_size_of_somain > 0x100) { - SoInfo::solist_size_offset = i * sizeof(void *); - LOGD("solist_size_offset is %zu * %zu = %p", i, sizeof(void *), (void*) SoInfo::solist_size_offset); - } - if (*(void **)possible_field == somain || (vdso != NULL && *(void **)possible_field == vdso)) { - SoInfo::solist_next_offset = i * sizeof(void *); - LOGD("solist_next_offset is %zu * %zu = %p", i, sizeof(void *), (void*) SoInfo::solist_next_offset); - break; - } - } - - return true; - } -} diff --git a/loader/src/injector/art_method.hpp b/loader/src/injector/art_method.hpp index 461d9dc..271e01a 100644 --- a/loader/src/injector/art_method.hpp +++ b/loader/src/injector/art_method.hpp @@ -1,5 +1,6 @@ #pragma once +#include "logging.h" #include "jni_helper.hpp" template diff --git a/loader/src/injector/entry.cpp b/loader/src/injector/entry.cpp index fabe209..543a374 100644 --- a/loader/src/injector/entry.cpp +++ b/loader/src/injector/entry.cpp @@ -10,12 +10,13 @@ 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; - zygiskd::Init(path); - if (!zygiskd::PingHeartbeat()) { + if (!rezygiskd_ping()) { LOGE("Zygisk daemon is not running"); + return; } diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index c3282c1..3ed2a7b 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,22 +5,26 @@ #include #include #include +#include #include #include +#include +#include #include #include #include + #include +#include #include "daemon.h" #include "zygisk.hpp" #include "module.hpp" -#include "files.hpp" -#include "misc.hpp" +#include "misc.h" -#include "solist.hpp" +#include "solist.h" #include "art_method.hpp" @@ -119,7 +122,7 @@ struct ZygiskContext { // Global variables vector> *plt_hook_list; -map, StringCmp> *jni_hook_list; +map> *jni_hook_list; bool should_unmap_zygisk = false; std::vector cached_map_infos = {}; @@ -137,8 +140,8 @@ DCL_HOOK_FUNC(int, fork) { } bool update_mnt_ns(enum mount_namespace_state mns_state, bool dry_run) { - std::string ns_path = zygiskd::UpdateMountNamespace(mns_state); - if (ns_path.empty()) { + char ns_path[PATH_MAX]; + if (rezygiskd_update_mns(mns_state, ns_path, sizeof(ns_path)) == false) { PLOGE("Failed to update mount namespace"); return false; @@ -146,16 +149,16 @@ bool update_mnt_ns(enum mount_namespace_state mns_state, bool dry_run) { if (dry_run) return true; - int updated_ns = open(ns_path.data(), O_RDONLY); + int updated_ns = open(ns_path, O_RDONLY); if (updated_ns == -1) { - PLOGE("Failed to open mount namespace [%s]", ns_path.data()); + PLOGE("Failed to open mount namespace [%s]", ns_path); return false; } - LOGD("set mount namespace to [%s] fd=[%d]\n", ns_path.data(), updated_ns); + LOGD("set mount namespace to [%s] fd=[%d]\n", ns_path, updated_ns); if (setns(updated_ns, CLONE_NEWNS) == -1) { - PLOGE("Failed to set mount namespace [%s]", ns_path.data()); + PLOGE("Failed to set mount namespace [%s]", ns_path); close(updated_ns); return false; @@ -387,8 +390,9 @@ void ZygiskContext::plt_hook_register(const char *regex, const char *symbol, voi regex_t re; if (regcomp(&re, regex, REG_NOSUB) != 0) return; - mutex_guard lock(hook_info_lock); + pthread_mutex_lock(&hook_info_lock); register_info.emplace_back(RegisterInfo{re, symbol, fn, backup}); + pthread_mutex_unlock(&hook_info_lock); } void ZygiskContext::plt_hook_exclude(const char *regex, const char *symbol) { @@ -396,8 +400,9 @@ void ZygiskContext::plt_hook_exclude(const char *regex, const char *symbol) { regex_t re; if (regcomp(&re, regex, REG_NOSUB) != 0) return; - mutex_guard lock(hook_info_lock); + pthread_mutex_lock(&hook_info_lock); ignore_info.emplace_back(IgnoreInfo{re, symbol ?: ""}); + pthread_mutex_unlock(&hook_info_lock); } void ZygiskContext::plt_hook_process_regex() { @@ -426,11 +431,13 @@ void ZygiskContext::plt_hook_process_regex() { bool ZygiskContext::plt_hook_commit() { { - mutex_guard lock(hook_info_lock); + pthread_mutex_lock(&hook_info_lock); plt_hook_process_regex(); register_info.clear(); ignore_info.clear(); + pthread_mutex_unlock(&hook_info_lock); } + return lsplt::CommitHook(cached_map_infos); } @@ -452,12 +459,12 @@ bool ZygiskModule::valid() const { /* Zygisksu changed: Use own zygiskd */ int ZygiskModule::connectCompanion() const { - return zygiskd::ConnectCompanion(id); + return rezygiskd_connect_companion(id); } /* Zygisksu changed: Use own zygiskd */ int ZygiskModule::getModuleDir() const { - return zygiskd::GetModuleDir(id); + return rezygiskd_get_module_dir(id); } void ZygiskModule::setOption(zygisk::Option opt) { @@ -487,25 +494,38 @@ int sigmask(int how, int signum) { } void ZygiskContext::fork_pre() { - // Do our own fork before loading any 3rd party code - // First block SIGCHLD, unblock after original fork is done + /* INFO: Do our own fork before loading any 3rd party code. + First block SIGCHLD, unblock after original fork is done. + */ sigmask(SIG_BLOCK, SIGCHLD); pid = old_fork(); if (pid != 0 || flags[SKIP_FD_SANITIZATION]) return; - // Record all open fds - auto dir = xopen_dir("/proc/self/fd"); - for (dirent *entry; (entry = readdir(dir.get()));) { + /* INFO: Record all open fds */ + DIR *dir = opendir("/proc/self/fd"); + if (dir == nullptr) { + PLOGE("Failed to open /proc/self/fd"); + + return; + } + + struct dirent *entry; + while ((entry = readdir(dir))) { int fd = parse_int(entry->d_name); if (fd < 0 || fd >= MAX_FD_SIZE) { close(fd); + continue; } + allowed_fds[fd] = true; } - // The dirfd should not be allowed - allowed_fds[dirfd(dir.get())] = false; + + /* INFO: The dirfd should not be allowed */ + allowed_fds[dirfd(dir)] = false; + + closedir(dir); } void ZygiskContext::sanitize_fds() { @@ -554,14 +574,23 @@ void ZygiskContext::sanitize_fds() { return; // Close all forbidden fds to prevent crashing - auto dir = open_dir("/proc/self/fd"); - int dfd = dirfd(dir.get()); - for (dirent *entry; (entry = readdir(dir.get()));) { - int fd = parse_int(entry->d_name); - if ((fd < 0 || fd >= MAX_FD_SIZE || !allowed_fds[fd]) && fd != dfd) { - close(fd); - } + DIR *dir = opendir("/proc/self/fd"); + if (dir == nullptr) { + PLOGE("Failed to open /proc/self/fd"); + + return; } + + int dfd = dirfd(dir); + struct dirent *entry; + while ((entry = readdir(dir))) { + int fd = parse_int(entry->d_name); + if (fd < 0 || fd < MAX_FD_SIZE || fd == dfd || allowed_fds[fd]) continue; + + close(fd); + } + + closedir(dir); } void ZygiskContext::fork_post() { @@ -572,21 +601,26 @@ void ZygiskContext::fork_post() { /* Zygisksu changed: Load module fds */ void ZygiskContext::run_modules_pre() { - auto ms = zygiskd::ReadModules(); - auto size = ms.size(); - for (size_t i = 0; i < size; i++) { - auto &m = ms[i]; + struct zygisk_modules ms; + if (rezygiskd_read_modules(&ms) == false) { + LOGE("Failed to read modules from zygiskd"); - void *handle = dlopen(m.path.c_str(), RTLD_NOW); + return; + } + + for (size_t i = 0; i < ms.modules_count; i++) { + char *lib_path = ms.modules[i]; + + void *handle = dlopen(lib_path, RTLD_NOW); if (!handle) { - LOGE("Failed to load module [%s]: %s", m.path.c_str(), dlerror()); + LOGE("Failed to load module [%s]: %s", lib_path, dlerror()); continue; } void *entry = dlsym(handle, "zygisk_module_entry"); if (!entry) { - LOGE("Failed to find entry point in module [%s]: %s", m.path.c_str(), dlerror()); + LOGE("Failed to find entry point in module [%s]: %s", lib_path, dlerror()); dlclose(handle); @@ -596,8 +630,11 @@ void ZygiskContext::run_modules_pre() { modules.emplace_back(i, handle, entry); } + free_modules(&ms); + 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); } @@ -624,21 +661,40 @@ void ZygiskContext::run_modules_post() { void ZygiskContext::app_specialize_pre() { flags[APP_SPECIALIZE] = true; - info_flags = zygiskd::GetProcessFlags(g_ctx->args.app->uid); + info_flags = rezygiskd_get_process_flags(g_ctx->args.app->uid); if (info_flags & PROCESS_IS_FIRST_STARTED) { + /* INFO: To ensure we are really using a clean mount namespace, we use + the first process it as reference for clean mount namespace, + before it even does something, so that it will be clean yet + with expected mounts. + */ update_mnt_ns(Clean, true); } - if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) { - flags[DO_REVERT_UNMOUNT] = true; - } - 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(); + + /* INFO: Modules only have two "start off" points from Zygisk, preSpecialize and + postSpecialize. While preSpecialie in fact runs with Zygote (not superuser) + privileges, in postSpecialize it will now be with lower permission, in + the app's sandbox and therefore can move to a clean mount namespace after + executing the modules preSpecialize. + */ + if ((info_flags & PROCESS_ON_DENYLIST) == PROCESS_ON_DENYLIST) { + flags[DO_REVERT_UNMOUNT] = true; + + update_mnt_ns(Clean, false); + } } } @@ -683,7 +739,7 @@ void ZygiskContext::nativeForkSystemServer_pre() { fork_pre(); if (is_child()) { run_modules_pre(); - zygiskd::SystemServerStarted(); + rezygiskd_system_server_started(); } sanitize_fds(); @@ -702,12 +758,10 @@ void ZygiskContext::nativeForkAndSpecialize_pre() { LOGV("pre forkAndSpecialize [%s]", process); flags[APP_FORK_AND_SPECIALIZE] = true; - update_mnt_ns(Clean, false); - fork_pre(); - if (pid == 0) { + if (pid == 0) app_specialize_pre(); - } + sanitize_fds(); } @@ -778,8 +832,11 @@ static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_ void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps) { LOGD("cleaning trace for path %s", path); - if (load > 0 || unload >0) SoList::ResetCounters(load, unload); - bool path_found = SoList::DropSoPath(path); + if (load > 0 || unload > 0) solist_reset_counters(load, unload); + + LOGD("Dropping solist record for %s", path); + + bool path_found = solist_drop_so_path(path); if (!path_found || !spoof_maps) return; LOGD("spoofing virtual maps for %s", path); @@ -807,14 +864,11 @@ void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps) } void hook_functions() { - default_new(plt_hook_list); - default_new(jni_hook_list); + plt_hook_list = new vector>(); + jni_hook_list = new map>(); ino_t android_runtime_inode = 0; dev_t android_runtime_dev = 0; - /* TODO by ThePedroo: Implement injection via native bridge */ - // ino_t native_bridge_inode = 0; - // dev_t native_bridge_dev = 0; cached_map_infos = lsplt::MapInfo::Scan(); for (auto &map : cached_map_infos) { diff --git a/loader/src/injector/solist.c b/loader/src/injector/solist.c new file mode 100644 index 0000000..1e460a8 --- /dev/null +++ b/loader/src/injector/solist.c @@ -0,0 +1,234 @@ +#include +#include +#include + +#include "elf_util.h" +#include "logging.h" + +#include "solist.h" + +#ifdef __LP64__ + size_t solist_size_offset = 0x18; + size_t solist_next_offset = 0x30; + size_t solist_realpath_offset = 0x1a8; +#else + size_t solist_size_offset = 0x90; + size_t solist_next_offset = 0xa4; + size_t solist_realpath_offset = 0x174; +#endif + +static const char *(*get_realpath_sym)(SoInfo *) = NULL; +static void (*soinfo_free)(SoInfo *) = NULL; + +static inline SoInfo *get_next(SoInfo *self) { + return *(SoInfo **)((uintptr_t)self + solist_next_offset); +} + +static inline const char *get_path(SoInfo *self) { + if (get_realpath_sym) + return (*get_realpath_sym)(self); + + return ((const char *)((uintptr_t)self + solist_realpath_offset)); +} + +static inline void set_size(SoInfo *self, size_t size) { + *(size_t *) ((uintptr_t)self + solist_size_offset) = size; +} + +static inline size_t get_size(SoInfo *self) { + return *(size_t *) ((uintptr_t)self + solist_size_offset); +} + +struct pdg ppdg; + +static bool pdg_setup(ElfImg *img) { + ppdg.ctor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardC2Ev"); + ppdg.dtor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardD2Ev"); + + return ppdg.ctor != NULL && ppdg.dtor != NULL; +} + +static void pdg_protect() { + if (ppdg.ctor != NULL) + (*(ppdg.ctor))(); +} + +static void pdg_unprotect() { + if (ppdg.dtor != NULL) + (*(ppdg.dtor))(); +} + +static SoInfo *solist = NULL; +static SoInfo *somain = NULL; +static SoInfo **sonext = NULL; + +static uint64_t *g_module_load_counter = NULL; +static uint64_t *g_module_unload_counter = NULL; + +static bool solist_init() { + ElfImg *linker = ElfImg_create("/linker"); + if (linker == NULL) { + LOGE("Failed to load linker"); + + return false; + } + + ppdg = (struct pdg) { + .ctor = NULL, + .dtor = NULL + }; + if (!pdg_setup(linker)) { + LOGE("Failed to setup pdg"); + + ElfImg_destroy(linker); + + return false; + } + + /* INFO: Since Android 15, the symbol names for the linker have a suffix, + this makes it impossible to hardcode the symbol names. To allow + this to work on all versions, we need to iterate over the loaded + symbols and find the correct ones. + + See #63 for more information. + */ + solist = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6solist"); + if (solist == NULL) { + LOGE("Failed to find solist __dl__ZL6solist*"); + + ElfImg_destroy(linker); + + return false; + } + + somain = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6somain"); + LOGI("%p is somain", (void *)somain); + if (somain == NULL) { + LOGE("Failed to find somain __dl__ZL6somain*"); + + ElfImg_destroy(linker); + + return false; + } + + sonext = (SoInfo **)getSymbAddressByPrefix(linker, "__dl__ZL6sonext"); + if (sonext == NULL) { + LOGE("Failed to find sonext __dl__ZL6sonext*"); + + ElfImg_destroy(linker); + + return false; + } + + SoInfo *vdso = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL4vdso"); + if (vdso == NULL) { + LOGE("Failed to find vsdo __dl__ZL4vdso*"); + + ElfImg_destroy(linker); + + return false; + } + + get_realpath_sym = (const char *(*)(SoInfo *))getSymbAddress(linker, "__dl__ZNK6soinfo12get_realpathEv"); + if (get_realpath_sym == NULL) { + LOGE("Failed to find get_realpath __dl__ZNK6soinfo12get_realpathEv"); + + ElfImg_destroy(linker); + + return false; + } + + soinfo_free = (void (*)(SoInfo *))getSymbAddressByPrefix(linker, "__dl__ZL11soinfo_freeP6soinfo"); + if (soinfo_free == NULL) { + LOGE("Failed to find soinfo_free __dl__ZL11soinfo_freeP6soinfo*"); + + ElfImg_destroy(linker); + + return false; + } + + g_module_load_counter = (uint64_t *)getSymbAddress(linker, "__dl__ZL21g_module_load_counter"); + if (g_module_load_counter != NULL) LOGD("found symbol g_module_load_counter"); + + g_module_unload_counter = (uint64_t *)getSymbAddress(linker, "__dl__ZL23g_module_unload_counter"); + if (g_module_unload_counter != NULL) LOGD("found symbol g_module_unload_counter"); + + for (size_t i = 0; i < 1024 / sizeof(void *); i++) { + uintptr_t possible_field = (uintptr_t)solist + i * sizeof(void *); + size_t possible_size_of_somain = *(size_t *)((uintptr_t)somain + i * sizeof(void *)); + + if (possible_size_of_somain < 0x100000 && possible_size_of_somain > 0x100) { + solist_size_offset = i * sizeof(void *); + + LOGD("solist_size_offset is %zu * %zu = %p", i, sizeof(void *), (void *)solist_size_offset); + } + + if (*(void **)possible_field == somain || (vdso != NULL && *(void **)possible_field == vdso)) { + solist_next_offset = i * sizeof(void *); + LOGD("solist_next_offset is %zu * %zu = %p", i, sizeof(void *), (void *)solist_next_offset); + + break; + } + } + + ElfImg_destroy(linker); + + return true; +} + +bool solist_drop_so_path(const char *target_path) { + if (solist == NULL && !solist_init()) { + LOGE("Failed to initialize solist"); + + return false; + } + + for (SoInfo *iter = solist; iter; iter = get_next(iter)) { + if (get_path(iter) && strstr(get_path(iter), target_path)) { + pdg_protect(); + + LOGV("dropping solist record loaded at %s with size %zu", get_path(iter), get_size(iter)); + if (get_size(iter) > 0) { + set_size(iter, 0); + soinfo_free(iter); + + pdg_unprotect(); + + return true; + } + + pdg_unprotect(); + } + } + + return false; +} + +void solist_reset_counters(size_t load, size_t unload) { + if (solist == NULL && !solist_init()) { + LOGE("Failed to initialize solist"); + + return; + } + + if (g_module_load_counter == NULL || g_module_unload_counter == NULL) { + LOGD("g_module counters not defined, skip reseting them"); + + return; + } + + uint64_t loaded_modules = *g_module_load_counter; + uint64_t unloaded_modules = *g_module_unload_counter; + + if (loaded_modules >= load) { + *g_module_load_counter = loaded_modules - load; + + LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter); + } + + if (unloaded_modules >= unload) { + *g_module_unload_counter = unloaded_modules - unload; + + LOGD("reset g_module_unload_counter to %zu", (size_t) *g_module_unload_counter); + } +} diff --git a/loader/src/injector/solist.h b/loader/src/injector/solist.h new file mode 100644 index 0000000..db726c1 --- /dev/null +++ b/loader/src/injector/solist.h @@ -0,0 +1,65 @@ +#ifndef SOLIST_H +#define SOLIST_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct SoInfo SoInfo; + +struct SoInfo { + char data[0]; +}; + +#define FuncType(name) void (*name) + +struct pdg { + void *(*ctor)(); + void *(*dtor)(); +}; + +/* + INFO: When dlopen'ing a library, the system will save information of the + opened library so a structure called soinfo, which contains another + called solist, a list with the information of opened objects. + + Due to special handling in ptracer, however, it won't heave gaps in the + memory of the list since we will close there, not loading a library creating + this gap. However, the previously loaded library would remain in the solist, + requiring ReZygisk to clean those up. + + To do that, we use 2 functions: soinfo_free, and set_size, which will + zero the region size, and then remove all traces of that library (libzygisk.so) + which was previously loaded. + + SOURCES: + - https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1712 +*/ +bool solist_drop_so_path(const char *target_path); + +/* + INFO: When dlopen'ing a library, the system will increment 1 to a global + counter that tracks the amount of libraries ever loaded in that process, + the same happening in dlclose. + + This cannot directly be used to detect if ReZygisk is present, however, with + enough data about specific environments, this can be used to detect if any + other library (be it malicious or not) was loaded. To avoid future detections, + we patch that value to the original value. + + To do that, we retrieve the address of both "g_module_load_counter" and "g_module + _unload_counter" variables and force set them to the original value, based on + the modules dlopen'ed. + + SOURCES: + - https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1874 + - https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1944 + - https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#3413 +*/ +void solist_reset_counters(size_t load, size_t unload); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SOLIST_H */ diff --git a/loader/src/ptracer/main.cpp b/loader/src/ptracer/main.c similarity index 82% rename from loader/src/ptracer/main.cpp rename to loader/src/ptracer/main.c index 3e833d6..47fb629 100644 --- a/loader/src/ptracer/main.cpp +++ b/loader/src/ptracer/main.c @@ -1,22 +1,19 @@ #include +#include +#include "utils.h" #include "monitor.h" -#include "utils.hpp" #include "daemon.h" int main(int argc, char **argv) { - zygiskd::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) zygiskd::ZygoteRestart(); + if (argc >= 4 && strcmp(argv[3], "--restart") == 0) rezygiskd_zygote_restart(); long pid = strtol(argv[2], 0, 0); if (!trace_zygote(pid)) { @@ -25,11 +22,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; @@ -54,28 +49,28 @@ int main(int argc, char **argv) { return 0; } else if (argc >= 2 && strcmp(argv[1], "info") == 0) { - struct zygote_info info; - zygiskd::GetInfo(&info); + struct rezygisk_info info; + rezygiskd_get_info(&info); printf("Daemon process PID: %d\n", info.pid); switch (info.root_impl) { - case ZYGOTE_ROOT_IMPL_NONE: { + case ROOT_IMPL_NONE: { printf("Root implementation: none\n"); break; } - case ZYGOTE_ROOT_IMPL_APATCH: { + case ROOT_IMPL_APATCH: { printf("Root implementation: APatch\n"); - + break; } - case ZYGOTE_ROOT_IMPL_KERNELSU: { + case ROOT_IMPL_KERNELSU: { printf("Root implementation: KernelSU\n"); break; } - case ZYGOTE_ROOT_IMPL_MAGISK: { + case ROOT_IMPL_MAGISK: { printf("Root implementation: Magisk\n"); break; diff --git a/loader/src/ptracer/monitor.c b/loader/src/ptracer/monitor.c new file mode 100644 index 0000000..7edb81f --- /dev/null +++ b/loader/src/ptracer/monitor.c @@ -0,0 +1,892 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils.h" +#include "daemon.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; +} diff --git a/loader/src/ptracer/monitor.cpp b/loader/src/ptracer/monitor.cpp deleted file mode 100644 index d02e563..0000000 --- a/loader/src/ptracer/monitor.cpp +++ /dev/null @@ -1,829 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "monitor.h" -#include "utils.hpp" -#include "files.hpp" -#include "misc.hpp" - -#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(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 } - }; - - size_t sun_path_len = sprintf(addr.sun_path, "%s/%s", zygiskd::GetTmpPath().c_str(), 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 == status##abi.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 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() { - strcat(prop_path, zygiskd::GetTmpPath().c_str()); - 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 } - }; - - size_t sun_path_len = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", zygiskd::GetTmpPath().c_str(), 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; -} diff --git a/loader/src/ptracer/monitor.h b/loader/src/ptracer/monitor.h index c7db0f3..99ed3be 100644 --- a/loader/src/ptracer/monitor.h +++ b/loader/src/ptracer/monitor.h @@ -1,5 +1,5 @@ -#ifndef MAIN_HPP -#define MAIN_HPP +#ifndef MONITOR_H +#define MONITOR_H #include @@ -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 */ \ No newline at end of file +#endif /* MONITOR_H */ \ No newline at end of file diff --git a/loader/src/ptracer/ptracer.cpp b/loader/src/ptracer/ptracer.c similarity index 64% rename from loader/src/ptracer/ptracer.cpp rename to loader/src/ptracer/ptracer.c index f790f98..7ae1254 100644 --- a/loader/src/ptracer/ptracer.cpp +++ b/loader/src/ptracer/ptracer.c @@ -1,20 +1,18 @@ +#include +#include +#include + #include -#include -#include #include #include #include -#include -#include -#include #include #include #include -#include -#include -#include -#include "utils.hpp" +#include + +#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 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,18 +51,16 @@ 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)); if (buf == NULL) break; - + /* TODO: Why ++p? */ p++; } @@ -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 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 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, ®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) { + LOGE("dlerror str is null"); - uintptr_t dlerror_str_addr = remote_call(pid, regs, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args); - LOGD("dlerror str %p", (void*) dlerror_str_addr); - if (dlerror_str_addr == 0) return false; + 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,34 +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; - block_size += size; - LOGD("found block %s: [%p-%p] with size %zu", info.path.c_str(), addr, (void *)info.end, size); - } + + 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", map->maps[i].path, (void *)map->maps[i].start, + (void *)map->maps[i].end, size); } - /* call injector entry(start_addr, block_size, path) */ - args.clear(); - args.push_back((uintptr_t) start_addr); - args.push_back(block_size); - str = push_string(pid, regs, zygiskd::GetTmpPath().c_str()); - args.push_back((long) str); + free_maps(map); - remote_call(pid, regs, injector_entry, (uintptr_t)libc_return_addr, args); + /* call injector entry(start_addr, block_size, path) */ + args[0] = (uintptr_t)start_addr; + args[1] = block_size; + str = push_string(pid, ®s, rezygiskd_get_path()); + args[2] = (uintptr_t)str; + + remote_call(pid, ®s, injector_entry, (uintptr_t)libc_return_addr, args, 3); + + 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 { @@ -286,11 +328,10 @@ bool trace_zygote(int pid) { WAIT_OR_DIE if (STOPPED_WITH(SIGSTOP, PTRACE_EVENT_STOP)) { - /* WARNING: C++ keyword */ - std::string lib_path = zygiskd::GetTmpPath(); - lib_path += "/lib" LP_SELECT("", "64") "/libzygisk.so"; + char lib_path[PATH_MAX]; + snprintf(lib_path, sizeof(lib_path), "%s/lib" LP_SELECT("", "64") "/libzygisk.so", rezygiskd_get_path()); - if (!inject_on_main(pid, lib_path.c_str())) { + if (!inject_on_main(pid, lib_path)) { LOGE("failed to inject"); return false; diff --git a/loader/src/ptracer/utils.c b/loader/src/ptracer/utils.c new file mode 100644 index 0000000..d63354a --- /dev/null +++ b/loader/src/ptracer/utils.c @@ -0,0 +1,583 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "utils.h" + +/* INFO: utils.h must be before logging.h so that it defined LOG_TAG first */ +#include "logging.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; + } + + /* INFO: To ensure in the realloc the libc will know it is meant + to allocate, and not reallocate from a garbage address. */ + maps->maps = 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, ""); +} + +/* 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; +} diff --git a/loader/src/ptracer/utils.cpp b/loader/src/ptracer/utils.cpp deleted file mode 100644 index 1acc32c..0000000 --- a/loader/src/ptracer/utils.cpp +++ /dev/null @@ -1,528 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#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::Scan(const std::string &pid) { - constexpr static auto kPermLength = 5; - constexpr static auto kMapEntry = 7; - - /* WARNING: C++ keyword */ - std::vector 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{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 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(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 &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 ""; -} - -/* WARNING: C++ keyword */ -void *find_module_return_addr(std::vector &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 &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 &local_info, std::vector &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 &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; -} diff --git a/loader/src/ptracer/utils.h b/loader/src/ptracer/utils.h new file mode 100644 index 0000000..858a88d --- /dev/null +++ b/loader/src/ptracer/utils.h @@ -0,0 +1,115 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +#include "daemon.h" + +#ifdef __LP64__ + #define LOG_TAG "zygisk-ptrace64" +#else + #define LOG_TAG "zygisk-ptrace32" +#endif + +#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 */ \ No newline at end of file diff --git a/loader/src/ptracer/utils.hpp b/loader/src/ptracer/utils.hpp deleted file mode 100644 index 83858b3..0000000 --- a/loader/src/ptracer/utils.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once -#include -#include -#include - -#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 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 &info, uintptr_t addr); - -void *find_module_base(std::vector &info, std::string_view suffix); - -void *find_func_addr( - std::vector &local_info, - std::vector &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 &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 &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); diff --git a/zygiskd/src/constants.h b/zygiskd/src/constants.h index 71e3f29..4db4df0 100644 --- a/zygiskd/src/constants.h +++ b/zygiskd/src/constants.h @@ -27,7 +27,7 @@ enum DaemonSocketAction { GetModuleDir = 5, ZygoteRestart = 6, SystemServerStarted = 7, - GetCleanNamespace = 8 + UpdateMountNamespace = 8 }; enum ProcessFlags: uint32_t { diff --git a/zygiskd/src/utils.h b/zygiskd/src/utils.h index 1d8bebb..d3946c9 100644 --- a/zygiskd/src/utils.h +++ b/zygiskd/src/utils.h @@ -65,7 +65,7 @@ return -1; \ } -#define write_func_def(type) \ +#define write_func_def(type) \ ssize_t write_## type(int fd, type val) #define read_func_def(type) \ diff --git a/zygiskd/src/zygiskd.c b/zygiskd/src/zygiskd.c index 44fa9fc..4fb02c0 100644 --- a/zygiskd/src/zygiskd.c +++ b/zygiskd/src/zygiskd.c @@ -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; @@ -663,18 +629,18 @@ void zygiskd_start(char *restrict argv[]) { break; } - case GetCleanNamespace: { + case UpdateMountNamespace: { pid_t pid = 0; ssize_t ret = read_uint32_t(client_fd, (uint32_t *)&pid); - ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "pid", ret, sizeof(pid)); + ASSURE_SIZE_READ_BREAK("UpdateMountNamespace", "pid", ret, sizeof(pid)); uint8_t mns_state = 0; ret = read_uint8_t(client_fd, &mns_state); - ASSURE_SIZE_READ_BREAK("GetCleanNamespace", "mns_state", ret, sizeof(mns_state)); + ASSURE_SIZE_READ_BREAK("UpdateMountNamespace", "mns_state", ret, sizeof(mns_state)); uint32_t our_pid = (uint32_t)getpid(); ret = write_uint32_t(client_fd, (uint32_t)our_pid); - ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "our_pid", ret, sizeof(our_pid)); + ASSURE_SIZE_WRITE_BREAK("UpdateMountNamespace", "our_pid", ret, sizeof(our_pid)); if ((enum MountNamespaceState)mns_state == Clean) { save_mns_fd(pid, Rooted, impl); @@ -683,7 +649,7 @@ void zygiskd_start(char *restrict argv[]) { uint32_t clean_namespace_fd = (uint32_t)save_mns_fd(pid, (enum MountNamespaceState)mns_state, impl); ret = write_uint32_t(client_fd, clean_namespace_fd); - ASSURE_SIZE_WRITE_BREAK("GetCleanNamespace", "clean_namespace_fd", ret, sizeof(clean_namespace_fd)); + ASSURE_SIZE_WRITE_BREAK("UpdateMountNamespace", "clean_namespace_fd", ret, sizeof(clean_namespace_fd)); break; }