You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
improve: ELF utils and SoList code
This commit improves the code related to ELF and SoList, porting them to C.
This commit is contained in:
396
loader/src/common/elf_util.c
Normal file
396
loader/src/common/elf_util.c
Normal file
@@ -0,0 +1,396 @@
|
||||
/* INFO: This file is written in C99 */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2019 Swift Gan
|
||||
* Copyright (C) 2021 LSPosed Contributors
|
||||
*/
|
||||
#include <malloc.h>
|
||||
#include <cstring>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <sys/stat.h>
|
||||
#include "elf_util.h"
|
||||
|
||||
using namespace SandHook;
|
||||
|
||||
template<typename T>
|
||||
inline constexpr auto offsetOf(ElfW(Ehdr) *head, ElfW(Off) off) {
|
||||
return reinterpret_cast<std::conditional_t<std::is_pointer_v<T>, T, T *>>(
|
||||
reinterpret_cast<uintptr_t>(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<decltype(header)>(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0));
|
||||
|
||||
close(fd);
|
||||
|
||||
section_header = offsetOf<decltype(section_header)>(header, header->e_shoff);
|
||||
|
||||
auto shoff = reinterpret_cast<uintptr_t>(section_header);
|
||||
char *section_str = offsetOf<char *>(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<decltype(dynsym_start)>(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<decltype(symtab_start)>(header, symtab_offset);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SHT_STRTAB: {
|
||||
if (bias == -4396) {
|
||||
strtab = section_h;
|
||||
symstr_offset = section_h->sh_offset;
|
||||
strtab_start = offsetOf<decltype(strtab_start)>(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<ElfW(Word)>(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<ElfW(Word) *>(((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<decltype(gnu_bloom_filter_)>(d_buf + 4);
|
||||
gnu_bucket_ = reinterpret_cast<decltype(gnu_bucket_)>(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<const char *>(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<const char *>(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<void *>(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<void *>(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<void *>(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<ElfImg *>(data);
|
||||
if (strstr(info->dlpi_name, self->elf.data())) {
|
||||
self->elf = info->dlpi_name;
|
||||
self->base = reinterpret_cast<void *>(info->dlpi_addr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}, this);
|
||||
return base != 0;
|
||||
}
|
||||
@@ -1,152 +1,76 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <string_view>
|
||||
#include <unordered_map>
|
||||
#include <string.h>
|
||||
#include <link.h>
|
||||
#include <linux/elf.h>
|
||||
#include <sys/types.h>
|
||||
#include <link.h>
|
||||
#include <string>
|
||||
|
||||
#define restrict /* INFO: Temporary measure */
|
||||
|
||||
#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<ElfW(Addr)>((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<ElfW(Addr)>((uintptr_t) base + offset - bias);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
struct symtabs *symtabs_;
|
||||
} ElfImg;
|
||||
|
||||
template<typename T>
|
||||
constexpr T getSymbAddress(std::string_view name) const {
|
||||
return reinterpret_cast<T>(getSymbAddress(name));
|
||||
}
|
||||
void ElfImg_destroy(ElfImg *img);
|
||||
|
||||
template<typename T>
|
||||
constexpr T getSymbAddressByPrefix(std::string_view prefix) const {
|
||||
return reinterpret_cast<T>(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<std::string_view, ElfW(Sym) *> 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
|
||||
#endif /* ELF_UTIL_H */
|
||||
|
||||
66
loader/src/include/solist.h
Normal file
66
loader/src/include/solist.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifndef SOLIST_H
|
||||
#define SOLIST_H
|
||||
|
||||
/* INFO: Temporary */
|
||||
#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 */
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// Original from https://github.com/LSPosed/NativeDetector/blob/master/app/src/main/jni/solist.cpp
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#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<void *>(linker.getSymbAddress(
|
||||
"__dl__ZN18ProtectedDataGuardC2Ev")), .adj = 0}}.f;
|
||||
dtor = MemFunc{.data = {.p = reinterpret_cast<void *>(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<typename T>
|
||||
inline T *getStaticPointer(const SandHook::ElfImg &linker, const char *name) {
|
||||
auto *addr = reinterpret_cast<T **>(linker.getSymbAddress(name));
|
||||
|
||||
return addr == NULL ? NULL : *addr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T *getStaticPointerByPrefix(const SandHook::ElfImg &linker, const char *name) {
|
||||
auto *addr = reinterpret_cast<T **>(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<SoInfo>(linker, "__dl__ZL6solist");
|
||||
if (solist == NULL) return false;
|
||||
LOGD("found symbol solist");
|
||||
|
||||
somain = getStaticPointerByPrefix<SoInfo>(linker, "__dl__ZL6somain");
|
||||
if (somain == NULL) return false;
|
||||
LOGD("found symbol somain");
|
||||
|
||||
sonext = linker.getSymbAddressByPrefix<SoInfo **>("__dl__ZL6sonext");
|
||||
if (sonext == NULL) return false;
|
||||
LOGD("found symbol sonext");
|
||||
|
||||
SoInfo *vdso = getStaticPointerByPrefix<SoInfo>(linker, "__dl__ZL4vdso");
|
||||
if (vdso != NULL) LOGD("found symbol vdso");
|
||||
|
||||
SoInfo::get_realpath_sym = reinterpret_cast<decltype(SoInfo::get_realpath_sym)>(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv"));
|
||||
if (SoInfo::get_realpath_sym == NULL) return false;
|
||||
LOGD("found symbol get_realpath_sym");
|
||||
|
||||
SoInfo::soinfo_free = reinterpret_cast<decltype(SoInfo::soinfo_free)>(linker.getSymbAddressByPrefix("__dl__ZL11soinfo_freeP6soinfo"));
|
||||
if (SoInfo::soinfo_free == NULL) return false;
|
||||
LOGD("found symbol soinfo_free");
|
||||
|
||||
g_module_load_counter = reinterpret_cast<decltype(g_module_load_counter)>(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<decltype(g_module_unload_counter)>(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;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "files.hpp"
|
||||
#include "misc.hpp"
|
||||
|
||||
#include "solist.hpp"
|
||||
#include "solist.h"
|
||||
|
||||
#include "art_method.hpp"
|
||||
|
||||
@@ -616,7 +616,7 @@ void ZygiskContext::run_modules_post() {
|
||||
|
||||
if (modules.size() > 0) {
|
||||
LOGD("modules unloaded: %zu/%zu", modules_unloaded, modules.size());
|
||||
clean_trace("/data/adb", modules.size(), modules_unloaded, true);
|
||||
clean_trace("/data/adb/rezygisk", modules.size(), modules_unloaded, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,8 +778,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);
|
||||
|
||||
LOGI("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);
|
||||
|
||||
236
loader/src/injector/solist.c
Normal file
236
loader/src/injector/solist.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/* INFO: This file is written in C99 */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user