improve: ELF utils, libzygisk.so injection

This commit improves ELF utils, so that it can now retrieve symbols in other ways, like dynamic symbols or utilizing GNU hash. With the improved ELF utils, "libzygisk.so" injection in ptracer was improved, replacing the use of "dlopen" and "dlsym" in the function to get remote symbol address with the use of ELF utils, also allowing higher compatibility by using linker's dlopen, dlsym, dlerror when libdl.so is not loaded in Zygote.
This commit is contained in:
ThePedroo
2025-04-30 06:08:19 -03:00
parent 68f1b1fb35
commit 313822ddcd
5 changed files with 599 additions and 220 deletions

View File

@@ -8,6 +8,12 @@
#include <unistd.h>
#ifdef __LP64__
#define LOG_TAG "zygisk-elfutil64"
#else
#define LOG_TAG "zygisk-elfutil32"
#endif
#include "logging.h"
#include "elf_util.h"
@@ -41,31 +47,31 @@ uint32_t GnuHash(const char *name) {
return h;
}
ElfW(Shdr) *offsetOf_Shdr(ElfW(Ehdr) * head, ElfW(Off) off) {
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) {
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) {
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) {
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;
if (info->dlpi_name == NULL)
return 0;
ElfImg *img = (ElfImg *)data;
if (strstr(info->dlpi_name, img->elf)) {
img->elf = strdup(info->dlpi_name);
if (strstr(info->dlpi_name, img->elf)) {
img->base = (void *)info->dlpi_addr;
return 1;
@@ -74,7 +80,7 @@ int dl_cb(struct dl_phdr_info *info, size_t size, void *data) {
return 0;
}
bool find_module_base(ElfImg *img) {
bool _find_module_base(ElfImg *img) {
dl_iterate_phdr(dl_cb, img);
return img->base != NULL;
@@ -83,47 +89,59 @@ bool find_module_base(ElfImg *img) {
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;
if (img->symtab_start == NULL || img->symstr_offset_for_symtab == 0) {
LOGE("Invalid symtab_start or symstr_offset_for_symtab, cannot count valid symbols");
return 0;
}
char *symtab_strings = offsetOf_char(img->header, img->symstr_offset_for_symtab);
for (ElfW(Off) i = 0; i < img->symtab_count; i++) {
const char *sym_name = symtab_strings + img->symtab_start[i].st_name;
if (!sym_name)
continue;
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)
if ((st_type == STT_FUNC || st_type == STT_OBJECT) && img->symtab_start[i].st_size > 0 && img->symtab_start[i].st_name != 0)
count++;
}
return count;
}
void ElfImg_destroy(ElfImg *img) {
if (img->elf) {
free(img->elf);
img->elf = NULL;
}
if (!img) return;
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);
if (valid_symtabs_amount > 0) {
for (size_t i = 0; i < valid_symtabs_amount; i++) {
free(img->symtabs_[i].name);
}
}
free(img->symtabs_);
img->symtabs_ = NULL;
}
if (img->elf) {
free(img->elf);
img->elf = NULL;
}
if (img->header) {
munmap(img->header, img->size);
img->header = NULL;
}
finalize:
free(img);
img = NULL;
free(img);
}
ElfImg *ElfImg_create(const char *elf) {
ElfImg *ElfImg_create(const char *elf, void *base) {
ElfImg *img = (ElfImg *)calloc(1, sizeof(ElfImg));
if (!img) {
LOGE("Failed to allocate memory for ElfImg");
@@ -131,204 +149,487 @@ ElfImg *ElfImg_create(const char *elf) {
return NULL;
}
img->bias = -4396;
img->elf = strdup(elf);
img->base = NULL;
img->symtabs_ = NULL;
if (!img->elf) {
LOGE("Failed to duplicate elf path string");
if (!find_module_base(img)) {
LOGE("Failed to find module base for %s", img->elf);
ElfImg_destroy(img);
free(img);
return NULL;
}
int fd = open(img->elf, O_RDONLY);
if (base) {
/* LOGI: 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);
} else {
if (!_find_module_base(img)) {
LOGE("Failed to find module base for %s using dl_iterate_phdr", elf);
ElfImg_destroy(img);
return NULL;
}
}
int fd = open(elf, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
LOGE("failed to open %s", img->elf);
LOGE("failed to open %s", elf);
ElfImg_destroy(img);
return NULL;
}
img->size = lseek(fd, 0, SEEK_END);
if (img->size <= 0) {
LOGE("lseek() failed for %s", img->elf);
struct stat st;
if (fstat(fd, &st) != 0) {
LOGE("fstat() failed for %s", elf);
close(fd);
ElfImg_destroy(img);
return NULL;
}
img->header = (ElfW(Ehdr) *)mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0);
img->size = st.st_size;
if (img->size <= sizeof(ElfW(Ehdr))) {
LOGE("Invalid file size %zu for %s", img->size, elf);
close(fd);
ElfImg_destroy(img);
return NULL;
}
img->header = (ElfW(Ehdr) *)mmap(NULL, img->size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
img->section_header = offsetOf_Shdr(img->header, img->header->e_shoff);
if (img->header == MAP_FAILED) {
LOGE("mmap() failed for %s", elf);
uintptr_t shoff = (uintptr_t)img->section_header;
char *section_str = offsetOf_char(img->header, img->section_header[img->header->e_shstrndx].sh_offset);
img->header = NULL;
ElfImg_destroy(img);
for (int i = 0; i < img->header->e_shnum; i++, shoff += img->header->e_shentsize) {
ElfW(Shdr) *section_h = (ElfW(Shdr *))shoff;
return NULL;
}
char *sname = section_h->sh_name + section_str;
size_t entsize = section_h->sh_entsize;
if (memcmp(img->header->e_ident, ELFMAG, SELFMAG) != 0) {
LOGE("Invalid ELF header for %s", elf);
switch (section_h->sh_type) {
case SHT_DYNSYM: {
if (img->bias == -4396) {
img->dynsym = section_h;
ElfImg_destroy(img);
return NULL;
}
if (img->header->e_shoff == 0 || img->header->e_shentsize == 0 || img->header->e_shnum == 0) {
LOGW("Section header table missing or invalid in %s", elf);
} else {
img->section_header = offsetOf_Shdr(img->header, img->header->e_shoff);
}
if (img->header->e_phoff == 0 || img->header->e_phentsize == 0 || img->header->e_phnum == 0) {
LOGW("Program header table missing or invalid in %s", elf);
}
ElfW(Shdr) *dynsym_shdr = NULL;
ElfW(Shdr) *symtab_shdr = NULL;
char *section_str = NULL;
if (img->section_header && img->header->e_shstrndx != SHN_UNDEF) {
if (img->header->e_shstrndx < img->header->e_shnum) {
ElfW(Shdr) *shstrtab_hdr = img->section_header + img->header->e_shstrndx;
section_str = offsetOf_char(img->header, shstrtab_hdr->sh_offset);
} else {
LOGW("Section header string table index (%u) out of bounds (%u)", img->header->e_shstrndx, img->header->e_shnum);
}
} else {
LOGW("Section header string table index not set or no section headers");
}
if (img->section_header) {
uintptr_t shoff = (uintptr_t)img->section_header;
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_str ? (section_h->sh_name + section_str) : "<?>";
size_t entsize = section_h->sh_entsize;
switch (section_h->sh_type) {
case SHT_DYNSYM: {
dynsym_shdr = 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;
}
case SHT_SYMTAB: {
if (strcmp(sname, ".symtab") == 0) {
symtab_shdr = section_h;
img->symtab_offset = section_h->sh_offset;
img->symtab_size = section_h->sh_size;
if (entsize > 0) img->symtab_count = img->symtab_size / entsize;
else {
LOGW("Section %s has zero sh_entsize", sname);
img->symtab_count = 0;
}
img->symtab_start = offsetOf_Sym(img->header, img->symtab_offset);
}
break;
}
case SHT_STRTAB: break;
case SHT_PROGBITS: break;
case SHT_HASH: {
ElfW(Word) *d_un = offsetOf_Word(img->header, section_h->sh_offset);
if (section_h->sh_size >= 2 * sizeof(ElfW(Word))) {
img->nbucket_ = d_un[0];
if (img->nbucket_ > 0 && section_h->sh_size >= (2 + img->nbucket_ + d_un[1]) * sizeof(ElfW(Word))) {
img->bucket_ = d_un + 2;
img->chain_ = img->bucket_ + img->nbucket_;
} else {
LOGW("Invalid SHT_HASH size or nbucket count in section %s", sname);
img->nbucket_ = 0;
}
} else {
LOGW("SHT_HASH section %s too small", sname);
}
break;
}
case SHT_GNU_HASH: {
ElfW(Word) *d_buf = offsetOf_Word(img->header, section_h->sh_offset);
if (section_h->sh_size >= 4 * sizeof(ElfW(Word))) {
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];
size_t expected_min_size = 4 * sizeof(ElfW(Word)) +
img->gnu_bloom_size_ * sizeof(uintptr_t) +
img->gnu_nbucket_ * sizeof(uint32_t);
if (img->gnu_nbucket_ > 0 && img->gnu_bloom_size_ > 0 && section_h->sh_size >= expected_min_size) {
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_;
uintptr_t chain_start_offset = (uintptr_t)img->gnu_chain_ - (uintptr_t)img->header;
if (chain_start_offset < section_h->sh_offset || chain_start_offset >= section_h->sh_offset + section_h->sh_size) {
LOGW("Calculated GNU hash chain seems out of bounds for section %s", sname);
img->gnu_nbucket_ = 0;
}
} else {
LOGW("Invalid SHT_GNU_HASH size or parameters in section %s", sname);
img->gnu_nbucket_ = 0;
}
} else {
LOGW("SHT_GNU_HASH section %s too small", sname);
}
break;
}
}
}
}
ElfW(Shdr) *shdr_base = img->section_header;
if (dynsym_shdr && shdr_base) {
img->dynsym = dynsym_shdr;
if (dynsym_shdr->sh_link < img->header->e_shnum) {
ElfW(Shdr) *linked_strtab = shdr_base + dynsym_shdr->sh_link;
if (linked_strtab->sh_type == SHT_STRTAB) {
img->strtab = linked_strtab;
img->symstr_offset = linked_strtab->sh_offset;
img->strtab_start = (void *)offsetOf_char(img->header, img->symstr_offset);
} else {
LOGW("Section %u linked by .dynsym is not SHT_STRTAB (type %u)", dynsym_shdr->sh_link, linked_strtab->sh_type);
}
} else {
LOGE(".dynsym sh_link (%u) is out of bounds (%u)", dynsym_shdr->sh_link, img->header->e_shnum);
}
} else {
LOGW("No .dynsym section found or section headers missing");
}
if (symtab_shdr && shdr_base) {
img->symtab = symtab_shdr;
if (symtab_shdr->sh_link < img->header->e_shnum) {
ElfW(Shdr) *linked_strtab = shdr_base + symtab_shdr->sh_link;
if (linked_strtab->sh_type == SHT_STRTAB) {
/* INFO: For linear lookup */
img->symstr_offset_for_symtab = linked_strtab->sh_offset;
} else {
LOGW("Section %u linked by .symtab is not SHT_STRTAB (type %u)", symtab_shdr->sh_link, linked_strtab->sh_type);
img->symstr_offset_for_symtab = 0;
}
} else {
LOGE(".symtab sh_link (%u) is out of bounds (%u)", symtab_shdr->sh_link, img->header->e_shnum);
img->symstr_offset_for_symtab = 0;
}
} else {
LOGI("No .symtab section found or section headers missing");
img->symtab_start = NULL;
img->symtab_count = 0;
img->symstr_offset_for_symtab = 0;
}
bool bias_calculated = false;
if (img->header->e_phoff > 0 && img->header->e_phnum > 0) {
ElfW(Phdr) *phdr = (ElfW(Phdr) *)((uintptr_t)img->header + img->header->e_phoff);
for (int i = 0; i < img->header->e_phnum; ++i) {
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0) {
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);
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;
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;
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);
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_;
}
}
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;
}
}
}
if (!bias_calculated)
LOGE("Failed to calculate bias for %s. Assuming bias is 0.", elf);
if (!img->dynsym_start || !img->strtab_start) {
if (img->header->e_type == ET_DYN) LOGE("Failed to find .dynsym or its string table (.dynstr) in %s", elf);
else LOGW("No .dynsym or .dynstr found in %s (might be expected for ET_EXEC)", elf);
}
if (!img->gnu_bucket_ && !img->bucket_)
LOGW("No hash table (.gnu.hash or .hash) found in %s. Dynamic symbol lookup might be slow or fail.", elf);
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;
}
bool _load_symtabs(ElfImg *img) {
if (img->symtabs_) return true;
if (!img->symtab_start || img->symstr_offset_for_symtab == 0 || img->symtab_count == 0) {
LOGE("Cannot load symtabs: .symtab section or its string table not found/valid.");
return false;
}
size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img);
if (valid_symtabs_amount == 0) return false;
if (valid_symtabs_amount == 0) {
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
img->symtabs_ = (struct symtabs *)malloc(sizeof(struct symtabs) * valid_symtabs_amount);
if (!img->symtabs_) return false;
return false;
}
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);
img->symtabs_ = (struct symtabs *)calloc(valid_symtabs_amount, sizeof(struct symtabs));
if (!img->symtabs_) {
LOGE("Failed to allocate memory for symtabs array");
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];
return false;
}
i++;
char *symtab_strings = offsetOf_char(img->header, img->symstr_offset_for_symtab);
size_t current_valid_index = 0;
for (ElfW(Off) pos = 0; pos < img->symtab_count; pos++) {
ElfW(Sym) *current_sym = &img->symtab_start[pos];
unsigned int st_type = ELF_ST_TYPE(current_sym->st_info);
if ((st_type == STT_FUNC || st_type == STT_OBJECT) && current_sym->st_size > 0 && current_sym->st_name != 0) {
const char *st_name = symtab_strings + current_sym->st_name;
if (!st_name)
continue;
ElfW(Shdr) *symtab_str_shdr = img->section_header + img->symtab->sh_link;
if (current_sym->st_name >= symtab_str_shdr->sh_size) {
LOGE("Symbol name offset out of bounds");
continue;
}
img->symtabs_[current_valid_index].name = strdup(st_name);
if (!img->symtabs_[current_valid_index].name) {
LOGE("Failed to duplicate symbol name: %s", st_name);
for(size_t k = 0; k < current_valid_index; ++k) {
free(img->symtabs_[k].name);
}
free(img->symtabs_);
img->symtabs_ = NULL;
return false;
}
img->symtabs_[current_valid_index].sym = current_sym;
current_valid_index++;
if (current_valid_index == valid_symtabs_amount) break;
}
}
return true;
}
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;
ElfW(Addr) GnuLookup(ElfImg *restrict img, const char *name, uint32_t hash) {
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;
if (!_load_symtabs(img)) {
LOGE("Failed to load symtabs");
static const size_t bloom_mask_bits = sizeof(uintptr_t) * 8;
size_t bloom_idx = (hash / bloom_mask_bits) % img->gnu_bloom_size_;
uintptr_t bloom_word = img->gnu_bloom_filter_[bloom_idx];
uintptr_t mask = ((uintptr_t)1 << (hash % bloom_mask_bits)) |
((uintptr_t)1 << ((hash >> img->gnu_shift2_) % bloom_mask_bits));
if ((mask & bloom_word) != mask) {
LOGE("Symbol '%s' (hash %u) filtered out by GNU Bloom Filter (idx %zu, mask 0x%lx, word 0x%lx)",
name, hash, bloom_idx, (unsigned long)mask, (unsigned long)bloom_word);
return 0;
}
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_);
return 0;
}
char *strings = (char *)img->strtab_start;
uint32_t chain_val = img->gnu_chain_[sym_index - img->gnu_symndx_];
ElfW(Word) dynsym_count = img->dynsym->sh_size / img->dynsym->sh_entsize;
if (sym_index >= dynsym_count) {
LOGE("Symbol index %u out of bounds", sym_index);
return 0;
}
ElfW(Sym) *sym = img->dynsym_start + sym_index;
if (sym->st_name >= img->strtab->sh_size) {
LOGE("Symbol name offset %u out of bounds", sym->st_name);
return 0;
}
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
return sym->st_value;
while ((chain_val & 1) == 0) {
sym_index++;
if (sym_index >= dynsym_count) {
LOGE("Symbol index %u out of bounds during chain walk", sym_index);
return 0;
}
chain_val = img->gnu_chain_[sym_index - img->gnu_symndx_];
sym = img->dynsym_start + sym_index;
if (sym->st_name >= img->strtab->sh_size) {
LOGE("Symbol name offset %u out of bounds", sym->st_name);
break;
}
if ((((chain_val ^ hash) >> 1) == 0 && strcmp(name, strings + sym->st_name) == 0) && sym->st_shndx != SHN_UNDEF)
return sym->st_value;
}
return 0;
}
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash) {
if (img->nbucket_ == 0 || !img->bucket_ || !img->chain_ || !img->dynsym_start || !img->strtab_start)
return 0;
char *strings = (char *)img->strtab_start;
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)
return sym->st_value;
}
return 0;
}
ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
if (!_load_symtabs(img)) {
LOGE("Failed to load symtabs for linear lookup of %s", name);
return 0;
}
size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img);
if (valid_symtabs_amount == 0) {
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
return false;
}
for (size_t i = 0; i < valid_symtabs_amount; i++) {
if (strcmp(name, img->symtabs_[i].name) != 0) continue;
if (!img->symtabs_[i].name || strcmp(name, img->symtabs_[i].name) != 0)
continue;
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
continue;
return img->symtabs_[i].sym->st_value;
}
@@ -336,21 +637,31 @@ ElfW(Addr) LinearLookup(ElfImg *img, const char *restrict name) {
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;
ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *prefix) {
if (!_load_symtabs(img)) {
LOGE("Failed to load symtabs");
LOGE("Failed to load symtabs for linear lookup by prefix of %s", prefix);
return 0;
}
size_t valid_symtabs_amount = calculate_valid_symtabs_amount(img);
if (valid_symtabs_amount == 0) {
LOGW("No valid symbols (FUNC/OBJECT with size > 0) found in .symtab for %s", img->elf);
return false;
}
size_t prefix_len = strlen(prefix);
if (prefix_len == 0) return 0;
for (size_t i = 0; i < valid_symtabs_amount; i++) {
if (strlen(img->symtabs_[i].name) < strlen(name))
if (!img->symtabs_[i].name || strlen(img->symtabs_[i].name) < prefix_len)
continue;
if (strncmp(img->symtabs_[i].name, name, strlen(name)) != 0)
if (strncmp(img->symtabs_[i].name, prefix, prefix_len) != 0)
continue;
if (img->symtabs_[i].sym->st_shndx == SHN_UNDEF)
continue;
return img->symtabs_[i].sym->st_value;
@@ -360,14 +671,16 @@ ElfW(Addr) LinearLookupByPrefix(ElfImg *img, const char *name) {
}
ElfW(Addr) getSymbOffset(ElfImg *img, const char *name) {
ElfW(Addr) offset = GnuLookup(img, name, GnuHash(name));
if (offset > 0) return offset;
ElfW(Addr) offset = 0;
offset = GnuLookup(img, name, GnuHash(name));
if (offset != 0) return offset;
offset = ElfLookup(img, name, ElfHash(name));
if (offset > 0) return offset;
if (offset != 0) return offset;
offset = LinearLookup(img, name);
if (offset > 0) return offset;
if (offset != 0) return offset;
return 0;
}
@@ -377,15 +690,19 @@ ElfW(Addr) getSymbAddress(ElfImg *img, const char *name) {
if (offset == 0 || !img->base) return 0;
return ((uintptr_t)img->base + offset - img->bias);
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
return address;
}
ElfW(Addr) getSymbAddressByPrefix(ElfImg *img, const char *prefix) {
ElfW(Addr) offset = LinearLookupByPrefix(img, prefix);
if (offset < 0 || !img->base) return 0;
if (offset == 0 || !img->base) return 0;
return (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
ElfW(Addr) address = (ElfW(Addr))((uintptr_t)img->base + offset - img->bias);
return address;
}
void *getSymbValueByPrefix(ElfImg *img, const char *prefix) {

View File

@@ -16,23 +16,17 @@ struct symtabs {
typedef struct {
char *elf;
void *base;
char *buffer;
off_t size;
off_t bias;
ElfW(Ehdr) *header;
size_t size;
off_t bias;
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;
ElfW(Sym) *dynsym_start;
ElfW(Shdr) *strtab;
ElfW(Off) symstr_offset;
void *strtab_start;
uint32_t nbucket_;
uint32_t *bucket_;
@@ -46,12 +40,19 @@ typedef struct {
uint32_t *gnu_bucket_;
uint32_t *gnu_chain_;
ElfW(Shdr) *symtab;
ElfW(Off) symtab_offset;
size_t symtab_size;
size_t symtab_count;
ElfW(Sym) *symtab_start;
ElfW(Off) symstr_offset_for_symtab;
struct symtabs *symtabs_;
} ElfImg;
void ElfImg_destroy(ElfImg *img);
ElfImg *ElfImg_create(const char *elf);
ElfImg *ElfImg_create(const char *elf, void *base);
ElfW(Addr) ElfLookup(ElfImg *restrict img, const char *restrict name, uint32_t hash);

View File

@@ -66,7 +66,11 @@ 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");
#ifdef __LP64__
ElfImg *linker = ElfImg_create("/system/bin/linker64", NULL);
#else
ElfImg *linker = ElfImg_create("/system/bin/linker", NULL);
#endif
if (linker == NULL) {
LOGE("Failed to load linker");

View File

@@ -160,8 +160,25 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGD("libc return addr %p", libc_return_addr);
/* call dlopen */
void *dlopen_addr = find_func_addr(local_map, map, "libdl.so", "dlopen");
if (dlopen_addr == NULL) return false;
#ifdef __LP64__
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlopen");
#else
void *dlopen_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlopen");
#endif
if (dlopen_addr == NULL) {
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
LOGW("Failed to find dlopen from libdl.so, will load from linker");
dlopen_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlopen");
if (dlopen_addr == NULL) {
PLOGE("Find __dl_dlopen");
free_maps(local_map);
free_maps(map);
return false;
}
}
long *args = (long *)malloc(3 * sizeof(long));
if (args == NULL) {
@@ -181,13 +198,25 @@ bool inject_on_main(int pid, const char *lib_path) {
LOGE("handle is null");
/* call dlerror */
void *dlerror_addr = find_func_addr(local_map, map, "libdl.so", "dlerror");
#ifdef __LP64__
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlerror");
#else
void *dlerror_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlerror");
#endif
if (dlerror_addr == NULL) {
LOGE("find dlerror");
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
LOGW("Failed to find dlerror from libdl.so, will load from linker");
free(args);
dlerror_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlerror");
if (dlerror_addr == NULL) {
LOGE("Find __dl_dlerror");
return false;
free(args);
free_maps(local_map);
free_maps(map);
return false;
}
}
uintptr_t dlerror_str_addr = remote_call(pid, &regs, (uintptr_t)dlerror_addr, (uintptr_t)libc_return_addr, args, 0);
@@ -200,7 +229,11 @@ bool inject_on_main(int pid, const char *lib_path) {
return false;
}
void *strlen_addr = find_func_addr(local_map, map, "libc.so", "strlen");
#ifdef __LP64__
void *strlen_addr = find_func_addr(local_map, map, "/system/lib64/libc.so", "strlen");
#else
void *strlen_addr = find_func_addr(local_map, map, "/system/lib/libc.so", "strlen");
#endif
if (strlen_addr == NULL) {
LOGE("find strlen");
@@ -240,8 +273,26 @@ bool inject_on_main(int pid, const char *lib_path) {
}
/* call dlsym(handle, "entry") */
void *dlsym_addr = find_func_addr(local_map, map, "libdl.so", "dlsym");
if (dlsym_addr == NULL) return false;
#ifdef __LP64__
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib64/bionic/libdl.so", "dlsym");
#else
void *dlsym_addr = find_func_addr(local_map, map, "/apex/com.android.runtime/lib/bionic/libdl.so", "dlsym");
#endif
if (dlsym_addr == NULL) {
/* INFO: Android 7.1 and below doesn't have libdl.so loaded in Zygote */
LOGW("Failed to find dlsym from libdl.so, will load from linker");
dlsym_addr = find_func_addr(local_map, map, "/system/bin/linker", "__dl_dlsym");
if (dlsym_addr == NULL) {
LOGE("find __dl_dlsym");
free(args);
free_maps(local_map);
free_maps(map);
return false;
}
}
free_maps(local_map);

View File

@@ -6,6 +6,7 @@
#include <sys/sysmacros.h>
#include <sys/ptrace.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/auxv.h>
@@ -19,10 +20,9 @@
#include <unistd.h>
#include <linux/limits.h>
#include "utils.h"
#include "elf_util.h"
/* INFO: utils.h must be before logging.h so that it defined LOG_TAG first */
#include "logging.h"
#include "utils.h"
bool switch_mnt_ns(int pid, int *fd) {
int nsfd, old_nsfd = -1;
@@ -312,9 +312,15 @@ void *find_module_return_addr(struct maps *map, const char *suffix) {
return NULL;
}
void *find_module_base(struct maps *map, const char *suffix) {
void *find_module_base(struct maps *map, const char *file) {
const char *suffix = position_after(file, '/');
if (!suffix) {
LOGE("failed to find suffix in %s", file);
return NULL;
}
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, '/');
@@ -329,26 +335,6 @@ void *find_module_base(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 *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);
@@ -365,10 +351,30 @@ void *find_func_addr(struct maps *local_info, struct maps *remote_info, const ch
LOGD("found local base %p remote base %p", local_base, remote_base);
uint8_t *addr = (sym - local_base) + remote_base;
LOGD("addr %p", addr);
ElfImg *mod = ElfImg_create(module, local_base);
if (mod == NULL) {
LOGE("failed to create elf img %s", module);
return addr;
return NULL;
}
uint8_t *sym = (uint8_t *)getSymbAddress(mod, func);
if (sym == NULL) {
LOGE("failed to find symbol %s in %s", func, module);
ElfImg_destroy(mod);
return NULL;
}
LOGD("found symbol %s in %s: %p", func, module, sym);
uintptr_t addr = (uintptr_t)(sym - local_base) + (uintptr_t)remote_base;
LOGD("addr %p", (void *)addr);
ElfImg_destroy(mod);
return (void *)addr;
}
void align_stack(struct user_regs_struct *regs, long preserve) {