You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
Compare commits
3 Commits
a7917e20fe
...
7e823319b7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e823319b7 | ||
|
|
f9fcf1c2e7 | ||
|
|
08513b17e8 |
@@ -108,43 +108,32 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
|
||||
|
||||
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;
|
||||
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) {
|
||||
info->modules.modules = (char **)malloc(sizeof(char *) * info->modules.modules_count);
|
||||
if (!info->modules.modules) {
|
||||
PLOGE("allocating modules name memory");
|
||||
|
||||
free(info->modules);
|
||||
info->modules = NULL;
|
||||
info->modules->modules_count = 0;
|
||||
info->modules.modules_count = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < info->modules->modules_count; i++) {
|
||||
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;
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
char module_path[PATH_MAX];
|
||||
@@ -156,43 +145,49 @@ void rezygiskd_get_info(struct rezygisk_info *info) {
|
||||
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;
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
info->modules.modules[i] = NULL;
|
||||
|
||||
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);
|
||||
info->modules.modules[i] = strndup(line + 5, strlen(line) - 6);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (info->modules.modules[i] == NULL) {
|
||||
PLOGE("failed to read module name from %s", module_path);
|
||||
|
||||
fclose(module_prop);
|
||||
|
||||
goto info_cleanup;
|
||||
}
|
||||
|
||||
fclose(module_prop);
|
||||
|
||||
continue;
|
||||
|
||||
info_cleanup:
|
||||
info->modules.modules_count = i;
|
||||
free_rezygisk_info(info);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
for (size_t i = 0; i < info->modules.modules_count; i++) {
|
||||
free(info->modules.modules[i]);
|
||||
}
|
||||
|
||||
free(info->modules);
|
||||
free(info->modules.modules);
|
||||
info->modules.modules = NULL;
|
||||
}
|
||||
|
||||
bool rezygiskd_read_modules(struct zygisk_modules *modules) {
|
||||
@@ -237,13 +232,11 @@ bool rezygiskd_read_modules(struct zygisk_modules *modules) {
|
||||
}
|
||||
|
||||
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);
|
||||
for (size_t i = 0; i < modules->modules_count; i++) {
|
||||
free(modules->modules[i]);
|
||||
}
|
||||
|
||||
free(modules->modules);
|
||||
}
|
||||
|
||||
int rezygiskd_connect_companion(size_t index) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -159,14 +160,14 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
}
|
||||
|
||||
if (base) {
|
||||
/* LOGI: Due to the use in zygisk-ptracer, we need to allow pre-
|
||||
/* INFO: Due to the use in zygisk-ptracer, we need to allow pre-
|
||||
fetched bases to be passed, as the linker (Android 7.1
|
||||
and below) is not loaded from dlopen, which makes it not
|
||||
be visible with dl_iterate_phdr.
|
||||
*/
|
||||
img->base = base;
|
||||
|
||||
LOGI("Using provided base address 0x%p for %s", base, elf);
|
||||
LOGD("Using provided base address 0x%p for %s", base, elf);
|
||||
} else {
|
||||
if (!_find_module_base(img)) {
|
||||
LOGE("Failed to find module base for %s using dl_iterate_phdr", elf);
|
||||
@@ -387,7 +388,7 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
img->symstr_offset_for_symtab = 0;
|
||||
}
|
||||
} else {
|
||||
LOGI("No .symtab section found or section headers missing");
|
||||
LOGD("No .symtab section found or section headers missing");
|
||||
|
||||
img->symtab_start = NULL;
|
||||
img->symtab_count = 0;
|
||||
@@ -403,43 +404,22 @@ ElfImg *ElfImg_create(const char *elf, void *base) {
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
LOGI("Calculated bias %ld from PT_LOAD segment %d (vaddr %lx)", (long)img->bias, i, (unsigned long)phdr[i].p_vaddr);
|
||||
LOGD("Calculated bias %ld from PT_LOAD segment %d (vaddr %lx)", (long)img->bias, i, (unsigned long)phdr[i].p_vaddr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bias_calculated) {
|
||||
for (int i = 0; i < img->header->e_phnum; ++i) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
if (!bias_calculated) for (int i = 0; i < img->header->e_phnum; ++i) {
|
||||
if (phdr[i].p_type != PT_LOAD) continue;
|
||||
|
||||
LOGI("Calculated bias %ld from first PT_LOAD segment %d (vaddr %lx, offset %lx)",
|
||||
(long)img->bias, i, (unsigned long)phdr[i].p_vaddr, (unsigned long)phdr[i].p_offset);
|
||||
img->bias = phdr[i].p_vaddr - phdr[i].p_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGD("Calculated bias %ld from first PT_LOAD segment %d (vaddr %lx, offset %lx)",
|
||||
(long)img->bias, i, (unsigned long)phdr[i].p_vaddr, (unsigned long)phdr[i].p_offset);
|
||||
|
||||
if (!bias_calculated && shdr_base) {
|
||||
LOGW("Could not calculate bias from program headers, falling back to section method.");
|
||||
uintptr_t shoff_for_bias = (uintptr_t)shdr_base;
|
||||
for (int i = 0; i < img->header->e_shnum; i++, shoff_for_bias += img->header->e_shentsize) {
|
||||
ElfW(Shdr) *section_h = (ElfW(Shdr *))shoff_for_bias;
|
||||
|
||||
if ((section_h->sh_flags & SHF_ALLOC) && section_h->sh_addr != 0) {
|
||||
img->bias = (off_t)section_h->sh_addr - (off_t)section_h->sh_offset;
|
||||
bias_calculated = true;
|
||||
|
||||
char *sname = section_str ? (section_h->sh_name + section_str) : "<?>";
|
||||
LOGI("Calculated bias %ld from first allocated section %s (addr %lx, offset %lx)",
|
||||
(long)img->bias, sname, (unsigned long)section_h->sh_addr, (unsigned long)section_h->sh_offset);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +503,7 @@ bool _load_symtabs(ElfImg *img) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash, unsigned char *sym_type) {
|
||||
if (img->gnu_nbucket_ == 0 || img->gnu_bloom_size_ == 0 || !img->gnu_bloom_filter_ || !img->gnu_bucket_ || !img->gnu_chain_ || !img->dynsym_start || !img->strtab_start)
|
||||
return 0;
|
||||
|
||||
@@ -545,7 +525,7 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
|
||||
uint32_t sym_index = img->gnu_bucket_[hash % img->gnu_nbucket_];
|
||||
if (sym_index < img->gnu_symndx_) {
|
||||
LOGI("Symbol %s hash %u maps to bucket %u index %u (below gnu_symndx %u), not exported?", name, hash, hash % img->gnu_nbucket_, sym_index, img->gnu_symndx_);
|
||||
LOGW("Symbol %s hash %u maps to bucket %u index %u (below gnu_symndx %u), not exported?", name, hash, hash % img->gnu_nbucket_, sym_index, img->gnu_symndx_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -568,8 +548,12 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
|
||||
while ((chain_val & 1) == 0) {
|
||||
sym_index++;
|
||||
@@ -589,14 +573,18 @@ ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
|
||||
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash) {
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash, unsigned char *sym_type) {
|
||||
if (img->nbucket_ == 0 || !img->bucket_ || !img->chain_ || !img->dynsym_start || !img->strtab_start)
|
||||
return 0;
|
||||
|
||||
@@ -605,14 +593,18 @@ ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t h
|
||||
for (size_t n = img->bucket_[hash % img->nbucket_]; n != STN_UNDEF; n = img->chain_[n]) {
|
||||
ElfW(Sym) *sym = img->dynsym_start + n;
|
||||
|
||||
if (strcmp(name, strings + sym->st_name) == 0 && sym->st_shndx != SHN_UNDEF)
|
||||
if (strcmp(name, strings + sym->st_name) == 0 && sym->st_shndx != SHN_UNDEF) {
|
||||
unsigned int type = ELF_ST_TYPE(sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return sym->st_value;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name, unsigned char *sym_type) {
|
||||
if (!_load_symtabs(img)) {
|
||||
LOGE("Failed to load symtabs for linear lookup of %s", name);
|
||||
|
||||
@@ -623,7 +615,7 @@ ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
if (valid_symtabs_amount == 0) {
|
||||
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < valid_symtabs_amount; i++) {
|
||||
@@ -633,13 +625,16 @@ ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
|
||||
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
unsigned int type = ELF_ST_TYPE(img->symtabs_[i].sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return img->symtabs_[i].sym->st_value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix, unsigned char *sym_type) {
|
||||
if (!_load_symtabs(img)) {
|
||||
LOGE("Failed to load symtabs for linear lookup by prefix of %s", prefix);
|
||||
|
||||
@@ -650,7 +645,7 @@ ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
if (valid_symtabs_amount == 0) {
|
||||
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t prefix_len = strlen(prefix);
|
||||
@@ -666,45 +661,161 @@ ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
|
||||
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
unsigned int type = ELF_ST_TYPE(img->symtabs_[i].sym->st_info);
|
||||
if (sym_type) *sym_type = type;
|
||||
|
||||
return img->symtabs_[i].sym->st_value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name) {
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name, unsigned char *sym_type) {
|
||||
ElfW(Addr) offset = 0;
|
||||
|
||||
offset = GnuLookup(img, name, GnuHash(name));
|
||||
offset = GnuLookup(img, name, GnuHash(name), sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
offset = ElfLookup(img, name, ElfHash(name));
|
||||
offset = ElfLookup(img, name, ElfHash(name), sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
offset = LinearLookup(img, name);
|
||||
offset = LinearLookup(img, name, sym_type);
|
||||
if (offset != 0) return offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
/* INFO: Struct containing information about hardware capabilities used in resolver. This
|
||||
struct information is pulled directly from the AOSP code.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/ifunc.h#53
|
||||
*/
|
||||
struct __ifunc_arg_t {
|
||||
unsigned long _size;
|
||||
unsigned long _hwcap;
|
||||
unsigned long _hwcap2;
|
||||
};
|
||||
|
||||
/* INFO: This is a constant used in the AOSP code to indicate that the struct __ifunc_arg_t
|
||||
contains hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/ifunc.h#74
|
||||
*/
|
||||
#define _IFUNC_ARG_HWCAP (1ULL << 62)
|
||||
#elif defined(__riscv)
|
||||
/* INFO: Struct used in Linux RISC-V architecture to probe hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/kernel/uapi/asm-riscv/asm/hwprobe.h#10
|
||||
*/
|
||||
struct riscv_hwprobe {
|
||||
int64_t key;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
/* INFO: This function is used in the AOSP code to probe hardware capabilities on RISC-V architecture
|
||||
by calling the syscall __NR_riscv_hwprobe and passing the parameters that will filled with
|
||||
the device hardware capabilities.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/bionic/vdso.cpp#86
|
||||
*/
|
||||
int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpu_count, unsigned long *cpus, unsigned flags) {
|
||||
register long a0 __asm__("a0") = (long)pairs;
|
||||
register long a1 __asm__("a1") = pair_count;
|
||||
register long a2 __asm__("a2") = cpu_count;
|
||||
register long a3 __asm__("a3") = (long)cpus;
|
||||
register long a4 __asm__("a4") = flags;
|
||||
register long a7 __asm__("a7") = __NR_riscv_hwprobe;
|
||||
|
||||
__asm__ volatile(
|
||||
"ecall"
|
||||
: "=r"(a0)
|
||||
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a7)
|
||||
);
|
||||
|
||||
return -a0;
|
||||
}
|
||||
|
||||
/* INFO: This is a function pointer type that points how the signature of the __riscv_hwprobe
|
||||
function is.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/libc/include/sys/hwprobe.h#62
|
||||
*/
|
||||
typedef int (*__riscv_hwprobe_t)(struct riscv_hwprobe *__pairs, size_t __pair_count, size_t __cpu_count, unsigned long *__cpus, unsigned __flags);
|
||||
#endif
|
||||
|
||||
/* INFO: GNU ifuncs (indirect functions) are functions that does not execute the code by itself,
|
||||
but instead lead to other functions that may very according to hardware capabilities,
|
||||
or other reasons, depending of the architecture.
|
||||
|
||||
This function is based on AOSP's (Android Open Source Project) code, and resolves the
|
||||
indirect symbol, leading to the correct, most appropriate for the hardware, symbol.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/tags/android-16.0.0_r1/linker/linker.cpp#2594
|
||||
- https://android.googlesource.com/platform/bionic/+/tags/android-16.0.0_r1/libc/bionic/bionic_call_ifunc_resolver.cpp#41
|
||||
*/
|
||||
static ElfW(Addr) handle_indirect_symbol(ElfImg *img, ElfW(Off) offset) {
|
||||
ElfW(Addr) resolver_addr = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
|
||||
#ifdef __aarch64__
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, struct __ifunc_arg_t *);
|
||||
|
||||
struct __ifunc_arg_t args = {
|
||||
._size = sizeof(struct __ifunc_arg_t),
|
||||
._hwcap = getauxval(AT_HWCAP),
|
||||
._hwcap2 = getauxval(AT_HWCAP2)
|
||||
};
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(args._hwcap | _IFUNC_ARG_HWCAP, &args);
|
||||
#elif defined(__arm__)
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(unsigned long);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(getauxval(AT_HWCAP));
|
||||
#elif defined(__riscv)
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __riscv_hwprobe_t, void *);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)(getauxval(AT_HWCAP), __riscv_hwprobe, NULL);
|
||||
#else
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
|
||||
|
||||
return ((ifunc_resolver_t)resolver_addr)();
|
||||
#endif
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbAddress(ElfImg *img, const char *name) {
|
||||
ElfW(Addr) offset = getSymbOffset(img, name);
|
||||
unsigned char sym_type = 0;
|
||||
ElfW(Addr) offset = getSymbOffset(img, name, &sym_type);
|
||||
|
||||
if (offset == 0 || !img->base) return 0;
|
||||
|
||||
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
if (sym_type == STT_GNU_IFUNC) {
|
||||
LOGD("Resolving STT_GNU_IFUNC symbol %s", name);
|
||||
|
||||
return address;
|
||||
return handle_indirect_symbol(img, offset);
|
||||
}
|
||||
|
||||
return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
}
|
||||
|
||||
ElfW(Addr) getSymbAddressByPrefix(ElfImg *img, const char *prefix) {
|
||||
ElfW(Addr) offset = LinearLookupByPrefix(img, prefix);
|
||||
unsigned char sym_type = 0;
|
||||
ElfW(Addr) offset = LinearLookupByPrefix(img, prefix, &sym_type);
|
||||
|
||||
if (offset == 0 || !img->base) return 0;
|
||||
|
||||
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
if (sym_type == STT_GNU_IFUNC) {
|
||||
LOGD("Resolving STT_GNU_IFUNC symbol by prefix %s", prefix);
|
||||
|
||||
return address;
|
||||
return handle_indirect_symbol(img, offset);
|
||||
}
|
||||
|
||||
return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
|
||||
}
|
||||
|
||||
void *getSymbValueByPrefix(ElfImg *img, const char *prefix) {
|
||||
|
||||
@@ -25,7 +25,7 @@ int read_fd(int fd) {
|
||||
.msg_controllen = sizeof(cmsgbuf)
|
||||
};
|
||||
|
||||
ssize_t ret = recvmsg(fd, &msg, MSG_WAITALL);
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_WAITALL));
|
||||
if (ret == -1) {
|
||||
PLOGE("recvmsg");
|
||||
|
||||
@@ -47,14 +47,14 @@ int read_fd(int fd) {
|
||||
|
||||
ssize_t write_string(int fd, const char *str) {
|
||||
size_t str_len = strlen(str);
|
||||
ssize_t write_bytes = write(fd, &str_len, sizeof(size_t));
|
||||
ssize_t write_bytes = TEMP_FAILURE_RETRY(write(fd, &str_len, sizeof(size_t)));
|
||||
if (write_bytes != (ssize_t)sizeof(size_t)) {
|
||||
LOGE("Failed to write string length: Not all bytes were written (%zd != %zu).\n", write_bytes, sizeof(size_t));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_bytes = write(fd, str, str_len);
|
||||
write_bytes = TEMP_FAILURE_RETRY(write(fd, str, str_len));
|
||||
if (write_bytes != (ssize_t)str_len) {
|
||||
LOGE("Failed to write string: Promised bytes doesn't exist (%zd != %zu).\n", write_bytes, str_len);
|
||||
|
||||
@@ -66,7 +66,7 @@ ssize_t write_string(int fd, const char *str) {
|
||||
|
||||
char *read_string(int fd) {
|
||||
size_t str_len = 0;
|
||||
ssize_t read_bytes = read(fd, &str_len, sizeof(size_t));
|
||||
ssize_t read_bytes = TEMP_FAILURE_RETRY(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));
|
||||
|
||||
@@ -80,7 +80,7 @@ char *read_string(int fd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_bytes = read(fd, buf, str_len);
|
||||
read_bytes = TEMP_FAILURE_RETRY(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);
|
||||
|
||||
@@ -94,14 +94,14 @@ char *read_string(int fd) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define write_func(type) \
|
||||
ssize_t write_## type(int fd, type val) { \
|
||||
return write(fd, &val, sizeof(type)); \
|
||||
#define write_func(type) \
|
||||
ssize_t write_## type(int fd, type val) { \
|
||||
return TEMP_FAILURE_RETRY(write(fd, &val, sizeof(type))); \
|
||||
}
|
||||
|
||||
#define read_func(type) \
|
||||
ssize_t read_## type(int fd, type *val) { \
|
||||
return read(fd, val, sizeof(type)); \
|
||||
#define read_func(type) \
|
||||
ssize_t read_## type(int fd, type *val) { \
|
||||
return TEMP_FAILURE_RETRY(read(fd, val, sizeof(type))); \
|
||||
}
|
||||
|
||||
write_func(uint8_t)
|
||||
|
||||
@@ -42,7 +42,7 @@ enum root_impl {
|
||||
};
|
||||
|
||||
struct rezygisk_info {
|
||||
struct zygisk_modules *modules;
|
||||
struct zygisk_modules modules;
|
||||
enum root_impl root_impl;
|
||||
pid_t pid;
|
||||
bool running;
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
#ifndef ELF_UTIL_H
|
||||
#define ELF_UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <link.h>
|
||||
#include <linux/elf.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h> // Added for threading primitives
|
||||
|
||||
#define SHT_GNU_HASH 0x6ffffff6
|
||||
|
||||
// Function pointer types for constructors and destructors
|
||||
typedef void (*linker_simple_func_t)(void);
|
||||
typedef void (*linker_ctor_function_t)(int, char**, char**);
|
||||
typedef void (*linker_dtor_function_t)(void);
|
||||
|
||||
|
||||
struct symtabs {
|
||||
char *name;
|
||||
ElfW(Sym) *sym;
|
||||
@@ -54,17 +61,7 @@ void ElfImg_destroy(ElfImg *img);
|
||||
|
||||
ElfImg *ElfImg_create(const char *elf, void *base);
|
||||
|
||||
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash);
|
||||
|
||||
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash);
|
||||
|
||||
ElfW(Addr) LinearLookup(ElfImg *restrict img, const char *restrict name);
|
||||
|
||||
ElfW(Addr) LinearLookupByPrefix(ElfImg *restrict img, const char *name);
|
||||
|
||||
int dl_cb(struct dl_phdr_info *info, size_t size, void *data);
|
||||
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name);
|
||||
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name, unsigned char *sym_type);
|
||||
|
||||
ElfW(Addr) getSymbAddress(ElfImg *img, const char *name);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "daemon.h"
|
||||
#include "logging.h"
|
||||
#include "solist.h"
|
||||
#include "zygisk.hpp"
|
||||
|
||||
using namespace std;
|
||||
@@ -8,7 +9,7 @@ void *start_addr = nullptr;
|
||||
size_t block_size = 0;
|
||||
|
||||
extern "C" [[gnu::visibility("default")]]
|
||||
void entry(void* addr, size_t size, const char* path) {
|
||||
void entry(void *addr, size_t size) {
|
||||
LOGD("Zygisk library injected, version %s", ZKSU_VERSION);
|
||||
|
||||
start_addr = addr;
|
||||
@@ -23,7 +24,10 @@ void entry(void* addr, size_t size, const char* path) {
|
||||
LOGD("start plt hooking");
|
||||
hook_functions();
|
||||
|
||||
void *module_addrs[1] = { addr };
|
||||
clean_trace(path, module_addrs, 1, 1, 0);
|
||||
solist_drop_so_path(addr, true);
|
||||
solist_reset_counters(1, 1);
|
||||
|
||||
send_seccomp_event();
|
||||
|
||||
LOGD("Zygisk library execution done, addr: %p, size: %zu", addr, size);
|
||||
}
|
||||
|
||||
@@ -734,24 +734,23 @@ void ZygiskContext::run_modules_post() {
|
||||
if (flags[APP_SPECIALIZE]) m.postAppSpecialize(args.app);
|
||||
else if (flags[SERVER_FORK_AND_SPECIALIZE]) m.postServerSpecialize(args.server);
|
||||
|
||||
/* INFO: If module is unloaded by dlclose, there's no need to
|
||||
hide it from soinfo manually. */
|
||||
if (m.tryUnload()) modules_unloaded++;
|
||||
else {
|
||||
bool has_dropped = solist_drop_so_path(m.getEntry(), false);
|
||||
if (!has_dropped) continue;
|
||||
|
||||
LOGD("Dropped solist record for %p", m.getEntry());
|
||||
}
|
||||
}
|
||||
|
||||
if (modules.size() > 0) {
|
||||
LOGD("modules unloaded: %zu/%zu", modules_unloaded, modules.size());
|
||||
LOGD("Modules unloaded: %zu/%zu", modules_unloaded, modules.size());
|
||||
|
||||
/* INFO: While Variable Length Arrays (VLAs) aren't usually
|
||||
recommended due to the ease of using too much of the
|
||||
stack, this should be fine since it should not be
|
||||
possible to exhaust the stack with only a few addresses. */
|
||||
void *module_addrs[modules.size() * sizeof(void *)];
|
||||
solist_reset_counters(modules.size(), modules_unloaded);
|
||||
|
||||
size_t i = 0;
|
||||
for (const auto &m : modules) {
|
||||
module_addrs[i++] = m.getEntry();
|
||||
}
|
||||
|
||||
clean_trace("/data/adb", module_addrs, modules.size(), modules.size(), modules_unloaded);
|
||||
LOGD("Returned global counters to their original values");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1006,22 +1005,6 @@ static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_
|
||||
#define PLT_HOOK_REGISTER(DEV, INODE, NAME) \
|
||||
PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME)
|
||||
|
||||
/* INFO: module_addrs_length is always the same as "load" */
|
||||
void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload) {
|
||||
LOGD("cleaning trace for path %s", path);
|
||||
|
||||
if (load > 0 || unload > 0) solist_reset_counters(load, unload);
|
||||
|
||||
LOGD("Dropping solist record for %s", path);
|
||||
|
||||
for (size_t i = 0; i < module_addrs_length; i++) {
|
||||
bool has_dropped = solist_drop_so_path(module_addrs[i]);
|
||||
if (!has_dropped) continue;
|
||||
|
||||
LOGD("Dropped solist record for %p", module_addrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void hook_functions() {
|
||||
plt_hook_list = new vector<tuple<dev_t, ino_t, const char *, void **>>();
|
||||
jni_hook_list = new map<string, vector<JNINativeMethod>>();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <android/dlext.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <linux/limits.h>
|
||||
|
||||
@@ -24,6 +23,7 @@
|
||||
static const char *(*get_realpath_sym)(SoInfo *) = NULL;
|
||||
static void (*soinfo_free)(SoInfo *) = NULL;
|
||||
static SoInfo *(*find_containing_library)(const void *p) = NULL;
|
||||
static void (*purge_unused_memory)(void) = NULL;
|
||||
|
||||
static inline const char *get_path(SoInfo *self) {
|
||||
if (get_realpath_sym)
|
||||
@@ -104,6 +104,8 @@ static bool solist_init() {
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,6 +117,8 @@ static bool solist_init() {
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,9 +130,26 @@ static bool solist_init() {
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is find_containing_library", (void *)find_containing_library);
|
||||
|
||||
purge_unused_memory = (void (*)())getSymbAddress(linker, "__dl__Z19purge_unused_memoryv");
|
||||
if (purge_unused_memory == NULL) {
|
||||
LOGE("Failed to find purge_unused_memory __dl__Z19purge_unused_memoryv");
|
||||
|
||||
ElfImg_destroy(linker);
|
||||
|
||||
somain = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGD("%p is purge_unused_memory", (void *)purge_unused_memory);
|
||||
|
||||
g_module_load_counter = (size_t *)getSymbAddress(linker, "__dl__ZL21g_module_load_counter");
|
||||
if (g_module_load_counter != NULL) LOGD("found symbol g_module_load_counter");
|
||||
|
||||
@@ -154,7 +175,7 @@ static bool solist_init() {
|
||||
|
||||
/* INFO: find_containing_library returns the SoInfo for the library that contains
|
||||
that memory inside its limits, hence why named "lib_memory" in ReZygisk. */
|
||||
bool solist_drop_so_path(void *lib_memory) {
|
||||
bool solist_drop_so_path(void *lib_memory, bool unload) {
|
||||
if (somain == NULL && !solist_init()) {
|
||||
LOGE("Failed to initialize solist");
|
||||
|
||||
@@ -165,6 +186,8 @@ bool solist_drop_so_path(void *lib_memory) {
|
||||
if (found == NULL) {
|
||||
LOGD("Could not find containing library for %p", lib_memory);
|
||||
|
||||
purge_unused_memory();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -176,16 +199,44 @@ bool solist_drop_so_path(void *lib_memory) {
|
||||
|
||||
return false;
|
||||
}
|
||||
strcpy(path, get_path(found));
|
||||
strncpy(path, get_path(found), sizeof(path) - 1);
|
||||
|
||||
/* INFO: This area is guarded. Must unprotect first. */
|
||||
pdg_unprotect();
|
||||
|
||||
set_size(found, 0);
|
||||
soinfo_free(found);
|
||||
if (unload) pdg_protect();
|
||||
|
||||
pdg_protect();
|
||||
LOGD("Set size of %p to 0", (void *)found);
|
||||
|
||||
LOGD("Successfully dropped so path for: %s", path);
|
||||
/* INFO: We know that as libzygisk.so our limits, but modules are arbitrary, so
|
||||
calling deconstructors might break them. To avoid that, we manually call
|
||||
the separated structures, that however won't clean all traces in soinfo,
|
||||
not for now, at least. */
|
||||
if (unload && dlclose((void *)found) == -1) {
|
||||
LOGE("Failed to dlclose so path for %s: %s", path, dlerror());
|
||||
|
||||
return false;
|
||||
} else if (!unload) {
|
||||
LOGD("Not unloading so path for %s, only dropping it", path);
|
||||
|
||||
/* TODO: call notify_gdb_of_unload(found); (it is static) to avoid leaving traces in
|
||||
r_debug_tail.
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/heads/main/linker/linker_gdb_support.cpp#94
|
||||
*/
|
||||
/* INFO: unregister_soinfo_tls cannot be used since module might use JNI which may
|
||||
require TLS, so we cannot remove it. */
|
||||
soinfo_free(found);
|
||||
|
||||
pdg_protect();
|
||||
}
|
||||
|
||||
LOGD("Successfully hidden soinfo traces for %s", path);
|
||||
|
||||
/* INFO: Avoid leaks by ensuring the freed places are munmapped */
|
||||
purge_unused_memory();
|
||||
|
||||
LOGD("Purged unused memory successfully");
|
||||
|
||||
/* INFO: Let's avoid trouble regarding detections */
|
||||
memset(path, strlen(path), 0);
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct SoInfo SoInfo;
|
||||
|
||||
struct SoInfo {
|
||||
char data[0];
|
||||
};
|
||||
typedef void SoInfo;
|
||||
|
||||
#define FuncType(name) void (*name)
|
||||
|
||||
@@ -28,14 +24,20 @@ struct pdg {
|
||||
libzygisk.so, so that it doesn't create gaps between current module info
|
||||
and the next (soinfo).
|
||||
|
||||
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.
|
||||
To do that, we use 2 functions: set_size and dlclose, which will first zero
|
||||
zero the size that the linker believes the shared library is, and then dlclose.
|
||||
Because the size is 0, it won't munmap the library, allowing us to keep loaded while
|
||||
having all other traces removed.
|
||||
|
||||
For the case of modules, which are arbitrary, we won't call dlclose, as it could break
|
||||
the module. Instead of using dlclose, we separately call soinfo_free, which will free
|
||||
the soinfo structure. That will allow to keep the data initialized by constructors
|
||||
mmaped, hence properly dropping most traces without breaking the module.
|
||||
|
||||
SOURCES:
|
||||
- https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1712
|
||||
*/
|
||||
bool solist_drop_so_path(void *lib_memory);
|
||||
bool solist_drop_so_path(void *lib_memory, bool unload);
|
||||
|
||||
/*
|
||||
INFO: When dlopen'ing a library, the system will increment 1 to a global
|
||||
|
||||
@@ -7,6 +7,4 @@ extern size_t block_size;
|
||||
|
||||
void hook_functions();
|
||||
|
||||
void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload);
|
||||
|
||||
extern "C" void send_seccomp_event();
|
||||
|
||||
@@ -77,11 +77,11 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.modules->modules_count != 0) {
|
||||
printf("Modules: %zu\n", info.modules->modules_count);
|
||||
if (info.modules.modules_count != 0) {
|
||||
printf("Modules: %zu\n", info.modules.modules_count);
|
||||
|
||||
for (size_t i = 0; i < info.modules->modules_count; i++) {
|
||||
printf(" - %s\n", info.modules->modules[i]);
|
||||
for (size_t i = 0; i < info.modules.modules_count; i++) {
|
||||
printf(" - %s\n", info.modules.modules[i]);
|
||||
}
|
||||
} else {
|
||||
printf("Modules: N/A\n");
|
||||
|
||||
@@ -66,6 +66,7 @@ struct rezygiskd_status status32 = {
|
||||
|
||||
int monitor_epoll_fd;
|
||||
bool monitor_events_running = true;
|
||||
typedef void (*monitor_event_callback_t)();
|
||||
|
||||
bool monitor_events_init() {
|
||||
monitor_epoll_fd = epoll_create(1);
|
||||
@@ -78,14 +79,9 @@ bool monitor_events_init() {
|
||||
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) {
|
||||
bool monitor_events_register_event(monitor_event_callback_t event_cb, int fd, uint32_t events) {
|
||||
struct epoll_event ev = {
|
||||
.data.ptr = event_cbs,
|
||||
.data.ptr = (void *)event_cb,
|
||||
.events = events
|
||||
};
|
||||
|
||||
@@ -116,15 +112,16 @@ 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");
|
||||
if (nfds == -1 && errno != EINTR) {
|
||||
PLOGE("epoll_wait");
|
||||
|
||||
continue;
|
||||
monitor_events_running = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
struct monitor_event_cbs *event_cbs = (struct monitor_event_cbs *)events[i].data.ptr;
|
||||
event_cbs->callback();
|
||||
for (int i = 0; i < nfds; i++) {
|
||||
((monitor_event_callback_t)events[i].data.ptr)();
|
||||
|
||||
if (!monitor_events_running) break;
|
||||
}
|
||||
@@ -132,11 +129,6 @@ void monitor_events_loop() {
|
||||
|
||||
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;
|
||||
@@ -272,15 +264,13 @@ void rezygiskd_listener_callback() {
|
||||
status64.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_info = (char *)malloc(msg.length);
|
||||
status64.daemon_info = strdup(msg_data);
|
||||
if (!status64.daemon_info) {
|
||||
PLOGE("malloc daemon64 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_info, msg_data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
@@ -293,15 +283,13 @@ void rezygiskd_listener_callback() {
|
||||
status32.daemon_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_info = (char *)malloc(msg.length);
|
||||
status32.daemon_info = strdup(msg_data);
|
||||
if (!status32.daemon_info) {
|
||||
PLOGE("malloc daemon32 info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_info, msg_data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
@@ -316,15 +304,13 @@ void rezygiskd_listener_callback() {
|
||||
status64.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status64.daemon_error_info = (char *)malloc(msg.length);
|
||||
status64.daemon_error_info = strdup(msg_data);
|
||||
if (!status64.daemon_error_info) {
|
||||
PLOGE("malloc daemon64 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status64.daemon_error_info, msg_data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
@@ -339,15 +325,13 @@ void rezygiskd_listener_callback() {
|
||||
status32.daemon_error_info = NULL;
|
||||
}
|
||||
|
||||
status32.daemon_error_info = (char *)malloc(msg.length);
|
||||
status32.daemon_error_info = strdup(msg_data);
|
||||
if (!status32.daemon_error_info) {
|
||||
PLOGE("malloc daemon32 error info");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(status32.daemon_error_info, msg_data);
|
||||
|
||||
update_status(NULL);
|
||||
|
||||
break;
|
||||
@@ -847,10 +831,6 @@ void init_monitor() {
|
||||
|
||||
monitor_events_init();
|
||||
|
||||
struct monitor_event_cbs listener_cbs = {
|
||||
.callback = rezygiskd_listener_callback,
|
||||
.stop_callback = rezygiskd_listener_stop
|
||||
};
|
||||
if (!rezygiskd_listener_init()) {
|
||||
LOGE("failed to create socket");
|
||||
|
||||
@@ -859,12 +839,8 @@ void init_monitor() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
monitor_events_register_event(&listener_cbs, monitor_sock_fd, EPOLLIN | EPOLLET);
|
||||
monitor_events_register_event(rezygiskd_listener_callback, monitor_sock_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
struct monitor_event_cbs sigchld_cbs = {
|
||||
.callback = sigchld_listener_callback,
|
||||
.stop_callback = sigchld_listener_stop
|
||||
};
|
||||
if (sigchld_listener_init() == false) {
|
||||
LOGE("failed to create signalfd");
|
||||
|
||||
@@ -874,10 +850,15 @@ void init_monitor() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
monitor_events_register_event(&sigchld_cbs, sigchld_signal_fd, EPOLLIN | EPOLLET);
|
||||
monitor_events_register_event(sigchld_listener_callback, sigchld_signal_fd, EPOLLIN | EPOLLET);
|
||||
|
||||
monitor_events_loop();
|
||||
|
||||
/* INFO: Once it stops the loop, we cannot access the epool data, so we
|
||||
either manually call the stops or save to a structure. */
|
||||
rezygiskd_listener_stop();
|
||||
sigchld_listener_stop();
|
||||
|
||||
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);
|
||||
|
||||
@@ -161,13 +161,15 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
|
||||
const char *libdl_path = NULL;
|
||||
const char *libc_path = NULL;
|
||||
for (size_t i = 0; i < local_map->size; i++) {
|
||||
if (local_map->maps[i].path == NULL) continue;
|
||||
for (size_t i = 0; i < map->size; i++) {
|
||||
if (map->maps[i].path == NULL) continue;
|
||||
|
||||
const char *filename = position_after(local_map->maps[i].path, '/');
|
||||
const char *filename = position_after(map->maps[i].path, '/');
|
||||
|
||||
if (strcmp(filename, "libdl.so") == 0) {
|
||||
libdl_path = local_map->maps[i].path;
|
||||
if (!libdl_path && strcmp(filename, "libdl.so") == 0) {
|
||||
libdl_path = map->maps[i].path;
|
||||
|
||||
LOGD("found libdl.so at %s", libdl_path);
|
||||
|
||||
/* INFO: If we had found libc.so too, no need to continue searching */
|
||||
if (libc_path) break;
|
||||
@@ -175,8 +177,10 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(filename, "libc.so") == 0) {
|
||||
libc_path = local_map->maps[i].path;
|
||||
if (!libc_path && strcmp(filename, "libc.so") == 0) {
|
||||
libc_path = map->maps[i].path;
|
||||
|
||||
LOGD("found libc.so at %s", libc_path);
|
||||
|
||||
/* INFO: If we had found libdl.so too, no need to continue searching */
|
||||
if (libdl_path) break;
|
||||
@@ -364,10 +368,8 @@ bool inject_on_main(int pid, const char *lib_path) {
|
||||
/* 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);
|
||||
remote_call(pid, ®s, injector_entry, (uintptr_t)libc_return_addr, args, 2);
|
||||
|
||||
free(args);
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ struct maps *parse_maps(const char *filename) {
|
||||
path_offset++;
|
||||
}
|
||||
|
||||
maps->maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
|
||||
if (!maps->maps) {
|
||||
struct map *tmp_maps = (struct map *)realloc(maps->maps, (i + 1) * sizeof(struct map));
|
||||
if (!tmp_maps) {
|
||||
LOGE("Failed to allocate memory for maps->maps");
|
||||
|
||||
maps->size = i;
|
||||
@@ -130,6 +130,7 @@ struct maps *parse_maps(const char *filename) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
maps->maps = tmp_maps;
|
||||
|
||||
maps->maps[i].start = addr_start;
|
||||
maps->maps[i].end = addr_end;
|
||||
|
||||
@@ -153,7 +153,14 @@ void companion_entry(int fd) {
|
||||
LOGI("New companion request.\n - Module name: %s\n - Client fd: %d\n", name, client_fd);
|
||||
|
||||
ret = write_uint8_t(client_fd, 1);
|
||||
ASSURE_SIZE_WRITE("ZygiskdCompanion", "client_fd", ret, sizeof(uint8_t));
|
||||
if (ret != sizeof(uint8_t)) {
|
||||
LOGE("Failed to sent client_fd in ZygiskdCompanion: Expected %zu, got %zd\n", sizeof(uint8_t), ret);
|
||||
|
||||
free(args);
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_t thread;
|
||||
if (pthread_create(&thread, NULL, entry_thread, (void *)args) == 0)
|
||||
|
||||
@@ -73,6 +73,14 @@ struct packages_config {
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void _apatch_free_package_config(struct packages_config *restrict config) {
|
||||
for (size_t i = 0; i < config->size; i++) {
|
||||
free(config->configs[i].process);
|
||||
}
|
||||
|
||||
free(config->configs);
|
||||
}
|
||||
|
||||
/* WARNING: Dynamic memory based */
|
||||
bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
config->configs = NULL;
|
||||
@@ -96,14 +104,16 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
config->configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config));
|
||||
if (config->configs == NULL) {
|
||||
struct package_config *tmp_configs = realloc(config->configs, (config->size + 1) * sizeof(struct package_config));
|
||||
if (tmp_configs == NULL) {
|
||||
LOGE("Failed to realloc APatch config struct: %s\n", strerror(errno));
|
||||
|
||||
_apatch_free_package_config(config);
|
||||
fclose(fp);
|
||||
|
||||
return false;
|
||||
}
|
||||
config->configs = tmp_configs;
|
||||
|
||||
config->configs[config->size].process = strdup(strtok(line, ","));
|
||||
|
||||
@@ -128,21 +138,9 @@ bool _apatch_get_package_config(struct packages_config *restrict config) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void _apatch_free_package_config(struct packages_config *restrict config) {
|
||||
for (size_t i = 0; i < config->size; i++) {
|
||||
free(config->configs[i].process);
|
||||
}
|
||||
|
||||
free(config->configs);
|
||||
}
|
||||
|
||||
bool apatch_uid_granted_root(uid_t uid) {
|
||||
struct packages_config config;
|
||||
if (!_apatch_get_package_config(&config)) {
|
||||
_apatch_free_package_config(&config);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!_apatch_get_package_config(&config)) return false;
|
||||
|
||||
for (size_t i = 0; i < config.size; i++) {
|
||||
if (config.configs[i].uid != uid) continue;
|
||||
@@ -162,11 +160,7 @@ bool apatch_uid_granted_root(uid_t uid) {
|
||||
|
||||
bool apatch_uid_should_umount(uid_t uid, const char *const process) {
|
||||
struct packages_config config;
|
||||
if (!_apatch_get_package_config(&config)) {
|
||||
_apatch_free_package_config(&config);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!_apatch_get_package_config(&config)) return false;
|
||||
|
||||
for (size_t i = 0; i < config.size; i++) {
|
||||
if (config.configs[i].uid != uid) continue;
|
||||
|
||||
@@ -74,8 +74,7 @@ void root_impls_setup(void) {
|
||||
}
|
||||
|
||||
void get_impl(struct root_impl *uimpl) {
|
||||
uimpl->impl = impl.impl;
|
||||
uimpl->variant = impl.variant;
|
||||
*uimpl = impl;
|
||||
}
|
||||
|
||||
bool uid_granted_root(uid_t uid) {
|
||||
|
||||
@@ -109,12 +109,9 @@ static void get_current_attr(char *restrict output, size_t size) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fread(output, 1, size, current) == 0) {
|
||||
if (fread(output, 1, size, current) == 0)
|
||||
LOGE("fread: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(current);
|
||||
}
|
||||
|
||||
@@ -543,15 +540,13 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
|
||||
&optional_start, &optional_end, &type_start, &type_end,
|
||||
&source_start, &source_end, &fs_option_start, &fs_option_end);
|
||||
|
||||
mounts->mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo));
|
||||
if (!mounts->mounts) {
|
||||
struct mountinfo *tmp_mounts = (struct mountinfo *)realloc(mounts->mounts, (i + 1) * sizeof(struct mountinfo));
|
||||
if (!tmp_mounts) {
|
||||
LOGE("Failed to allocate memory for mounts->mounts");
|
||||
|
||||
fclose(mountinfo);
|
||||
free_mounts(mounts);
|
||||
|
||||
return false;
|
||||
goto cleanup_mount_allocs;
|
||||
}
|
||||
mounts->mounts = tmp_mounts;
|
||||
|
||||
unsigned int shared = 0;
|
||||
unsigned int master = 0;
|
||||
@@ -572,16 +567,64 @@ bool parse_mountinfo(const char *restrict pid, struct mountinfos *restrict mount
|
||||
mounts->mounts[i].parent = parent;
|
||||
mounts->mounts[i].device = (dev_t)(makedev(maj, min));
|
||||
mounts->mounts[i].root = strndup(line + root_start, (size_t)(root_end - root_start));
|
||||
if (mounts->mounts[i].root == NULL) {
|
||||
LOGE("Failed to allocate memory for root\n");
|
||||
|
||||
goto cleanup_mount_allocs;
|
||||
}
|
||||
mounts->mounts[i].target = strndup(line + target_start, (size_t)(target_end - target_start));
|
||||
if (mounts->mounts[i].target == NULL) {
|
||||
LOGE("Failed to allocate memory for target\n");
|
||||
|
||||
goto cleanup_root;
|
||||
}
|
||||
mounts->mounts[i].vfs_option = strndup(line + vfs_option_start, (size_t)(vfs_option_end - vfs_option_start));
|
||||
if (mounts->mounts[i].vfs_option == NULL) {
|
||||
LOGE("Failed to allocate memory for vfs_option\n");
|
||||
|
||||
goto cleanup_target;
|
||||
}
|
||||
mounts->mounts[i].optional.shared = shared;
|
||||
mounts->mounts[i].optional.master = master;
|
||||
mounts->mounts[i].optional.propagate_from = propagate_from;
|
||||
mounts->mounts[i].type = strndup(line + type_start, (size_t)(type_end - type_start));
|
||||
if (mounts->mounts[i].type == NULL) {
|
||||
LOGE("Failed to allocate memory for type\n");
|
||||
|
||||
goto cleanup_vfs_option;
|
||||
}
|
||||
mounts->mounts[i].source = strndup(line + source_start, (size_t)(source_end - source_start));
|
||||
if (mounts->mounts[i].source == NULL) {
|
||||
LOGE("Failed to allocate memory for source\n");
|
||||
|
||||
goto cleanup_type;
|
||||
}
|
||||
mounts->mounts[i].fs_option = strndup(line + fs_option_start, (size_t)(fs_option_end - fs_option_start));
|
||||
if (mounts->mounts[i].fs_option == NULL) {
|
||||
LOGE("Failed to allocate memory for fs_option\n");
|
||||
|
||||
goto cleanup_source;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
continue;
|
||||
|
||||
cleanup_source:
|
||||
free((void *)mounts->mounts[i].source);
|
||||
cleanup_type:
|
||||
free((void *)mounts->mounts[i].type);
|
||||
cleanup_vfs_option:
|
||||
free((void *)mounts->mounts[i].vfs_option);
|
||||
cleanup_target:
|
||||
free((void *)mounts->mounts[i].target);
|
||||
cleanup_root:
|
||||
free((void *)mounts->mounts[i].root);
|
||||
cleanup_mount_allocs:
|
||||
fclose(mountinfo);
|
||||
free_mounts(mounts);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(mountinfo);
|
||||
|
||||
@@ -123,7 +123,6 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
context->modules = realloc(context->modules, (size_t)((context->len + 1) * sizeof(struct Module)));
|
||||
if (context->modules == NULL) {
|
||||
LOGE("Failed reallocating memory for modules.\n");
|
||||
@@ -143,8 +142,10 @@ static void load_modules(enum Architecture arch, struct Context *restrict contex
|
||||
static void free_modules(struct Context *restrict context) {
|
||||
for (size_t i = 0; i < context->len; i++) {
|
||||
free(context->modules[i].name);
|
||||
if (context->modules[i].companion != -1) close(context->modules[i].companion);
|
||||
if (context->modules[i].companion >= 0) close(context->modules[i].companion);
|
||||
}
|
||||
|
||||
free(context->modules);
|
||||
}
|
||||
|
||||
static int create_daemon_socket(void) {
|
||||
@@ -288,7 +289,7 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, msg_data, (size_t)msg.length);
|
||||
|
||||
free(msg_data);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
enum Architecture arch = get_arch();
|
||||
load_modules(arch, &context);
|
||||
@@ -301,12 +302,24 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
} else {
|
||||
for (size_t i = 0; i < context.len; i++) {
|
||||
if (i != context.len - 1) {
|
||||
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1);
|
||||
if (module_list == NULL) {
|
||||
char *tmp_module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + strlen(", ") + 1);
|
||||
if (tmp_module_list == NULL) {
|
||||
LOGE("Failed reallocating memory for module list.\n");
|
||||
|
||||
return;
|
||||
char *kmsg_failure = "Failed reallocating memory for module list";
|
||||
struct MsgHead msg = {
|
||||
.cmd = DAEMON_SET_ERROR_INFO,
|
||||
.length = (int)strlen(kmsg_failure) + 1
|
||||
};
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length);
|
||||
|
||||
free(module_list);
|
||||
free_modules(&context);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
module_list = tmp_module_list;
|
||||
|
||||
strcpy(module_list + module_list_len, context.modules[i].name);
|
||||
|
||||
@@ -316,12 +329,24 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
|
||||
module_list_len += strlen(", ");
|
||||
} else {
|
||||
module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1);
|
||||
if (module_list == NULL) {
|
||||
char *tmp_module_list = realloc(module_list, module_list_len + strlen(context.modules[i].name) + 1);
|
||||
if (tmp_module_list == NULL) {
|
||||
LOGE("Failed reallocating memory for module list.\n");
|
||||
|
||||
return;
|
||||
char *kmsg_failure = "Failed reallocating memory for module list";
|
||||
struct MsgHead msg = {
|
||||
.cmd = DAEMON_SET_ERROR_INFO,
|
||||
.length = (int)strlen(kmsg_failure) + 1
|
||||
};
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length);
|
||||
|
||||
free(module_list);
|
||||
free_modules(&context);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
module_list = tmp_module_list;
|
||||
|
||||
strcpy(module_list + module_list_len, context.modules[i].name);
|
||||
|
||||
@@ -344,7 +369,16 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
if (msg_data == NULL) {
|
||||
LOGE("Failed allocating memory for message data.\n");
|
||||
|
||||
return;
|
||||
char *kmsg_failure = "Failed allocating memory for message data";
|
||||
msg.cmd = DAEMON_SET_ERROR_INFO;
|
||||
msg.length = (int)strlen(kmsg_failure) + 1;
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, &msg, sizeof(struct MsgHead));
|
||||
unix_datagram_sendto(CONTROLLER_SOCKET, kmsg_failure, (size_t)msg.length);
|
||||
|
||||
free(module_list);
|
||||
free_modules(&context);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(msg_data, msg_length, "Root: %s, Modules: %s", impl_name, module_list);
|
||||
@@ -360,6 +394,8 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
if (socket_fd == -1) {
|
||||
LOGE("Failed creating daemon socket\n");
|
||||
|
||||
free_modules(&context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,7 +408,7 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
if (client_fd == -1) {
|
||||
LOGE("accept: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t action8 = 0;
|
||||
@@ -380,11 +416,11 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
if (len == -1) {
|
||||
LOGE("read: %s\n", strerror(errno));
|
||||
|
||||
return;
|
||||
break;
|
||||
} else if (len == 0) {
|
||||
LOGI("Client disconnected\n");
|
||||
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
enum DaemonSocketAction action = (enum DaemonSocketAction)action8;
|
||||
@@ -402,10 +438,10 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
}
|
||||
case ZygoteRestart: {
|
||||
for (size_t i = 0; i < context.len; i++) {
|
||||
if (context.modules[i].companion != -1) {
|
||||
close(context.modules[i].companion);
|
||||
context.modules[i].companion = -1;
|
||||
}
|
||||
if (context.modules[i].companion <= -1) continue;
|
||||
|
||||
close(context.modules[i].companion);
|
||||
context.modules[i].companion = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -566,9 +602,19 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
ssize_t ret = read_size_t(client_fd, &index);
|
||||
ASSURE_SIZE_READ_BREAK("RequestCompanionSocket", "index", ret, sizeof(index));
|
||||
|
||||
struct Module *module = &context.modules[index];
|
||||
if (index >= context.len) {
|
||||
LOGE("Invalid module index: %zu\n", index);
|
||||
|
||||
if (module->companion != -1) {
|
||||
ret = write_uint8_t(client_fd, 0);
|
||||
ASSURE_SIZE_WRITE_BREAK("RequestCompanionSocket", "response", ret, sizeof(int));
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
struct Module *module = &context.modules[index];
|
||||
if (module->companion >= 0) {
|
||||
if (!check_unix_socket(module->companion, false)) {
|
||||
LOGE(" - Companion for module \"%s\" crashed\n", module->name);
|
||||
|
||||
@@ -577,10 +623,10 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (module->companion == -1) {
|
||||
if (module->companion <= -1) {
|
||||
module->companion = spawn_companion(argv, module->name, module->lib_fd);
|
||||
|
||||
if (module->companion > 0) {
|
||||
if (module->companion >= 0) {
|
||||
LOGI(" - Spawned companion for \"%s\": %d\n", module->name, module->companion);
|
||||
} else {
|
||||
if (module->companion == -2) {
|
||||
@@ -597,7 +643,7 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
so just sending the file descriptor of the client is
|
||||
safe.
|
||||
*/
|
||||
if (module->companion != -1) {
|
||||
if (module->companion >= 0) {
|
||||
LOGI(" - Sending companion fd socket of module \"%s\"\n", module->name);
|
||||
|
||||
if (write_fd(module->companion, client_fd) == -1) {
|
||||
@@ -629,6 +675,17 @@ void zygiskd_start(char *restrict argv[]) {
|
||||
ssize_t ret = read_size_t(client_fd, &index);
|
||||
ASSURE_SIZE_READ_BREAK("GetModuleDir", "index", ret, sizeof(index));
|
||||
|
||||
if (index >= context.len) {
|
||||
LOGE("Invalid module index: %zu\n", index);
|
||||
|
||||
ret = write_uint8_t(client_fd, 0);
|
||||
ASSURE_SIZE_WRITE_BREAK("GetModuleDir", "response", ret, sizeof(int));
|
||||
|
||||
close(client_fd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
char module_dir[PATH_MAX];
|
||||
snprintf(module_dir, PATH_MAX, "%s/%s", PATH_MODULES_DIR, context.modules[index].name);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user