From f3e9fd6b8f5be685e5f751cc877efeddc7545fb4 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Sun, 28 Mar 2021 22:28:46 +0800 Subject: [PATCH] [core] Hash for looking up symbols (#424) --- core/src/main/cpp/CMakeLists.txt | 2 - core/src/main/cpp/external/DexBuilder | 2 +- core/src/main/cpp/external/libcxx | 2 +- .../main/include/art/runtime/class_linker.h | 2 +- core/src/main/cpp/main/include/utils.h | 4 +- core/src/main/cpp/main/src/elf_util.cpp | 222 ++++++++++++------ core/src/main/cpp/main/src/elf_util.h | 149 ++++++++---- core/src/main/cpp/main/src/main.cpp | 3 +- core/src/main/cpp/main/src/symbol_cache.cpp | 4 +- 9 files changed, 255 insertions(+), 135 deletions(-) diff --git a/core/src/main/cpp/CMakeLists.txt b/core/src/main/cpp/CMakeLists.txt index b10257f5..7e089fd2 100644 --- a/core/src/main/cpp/CMakeLists.txt +++ b/core/src/main/cpp/CMakeLists.txt @@ -25,8 +25,6 @@ find_program(CCACHE ccache) if (CCACHE) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) -else () - message(WARNING "Could not find CCache program.") endif () include_directories(external/libcxx/include) diff --git a/core/src/main/cpp/external/DexBuilder b/core/src/main/cpp/external/DexBuilder index 1dfc6126..5c1ccd55 160000 --- a/core/src/main/cpp/external/DexBuilder +++ b/core/src/main/cpp/external/DexBuilder @@ -1 +1 @@ -Subproject commit 1dfc61263314ab10369d45f7ca10cf38b6546d68 +Subproject commit 5c1ccd55836e4bf43cdb6adbad40443d18cd43db diff --git a/core/src/main/cpp/external/libcxx b/core/src/main/cpp/external/libcxx index cca5298b..f61fcee0 160000 --- a/core/src/main/cpp/external/libcxx +++ b/core/src/main/cpp/external/libcxx @@ -1 +1 @@ -Subproject commit cca5298bc3fbb19b607008925b10acd0ee06e03d +Subproject commit f61fcee091736dd0ae38670876157e9862ac6ff8 diff --git a/core/src/main/cpp/main/include/art/runtime/class_linker.h b/core/src/main/cpp/main/include/art/runtime/class_linker.h index 3d5aa816..7cfb15e7 100644 --- a/core/src/main/cpp/main/include/art/runtime/class_linker.h +++ b/core/src/main/cpp/main/include/art/runtime/class_linker.h @@ -148,7 +148,7 @@ namespace art { } void *thiz = *reinterpret_cast( - reinterpret_cast(Runtime::Current()->Get()) + OFFSET_classlinker); + reinterpret_cast(Runtime::Current()->Get()) + OFFSET_classlinker); // ClassLinker* GetClassLinker() but inlined LOGD("Classlinker object: %p", thiz); instance_ = new ClassLinker(thiz); diff --git a/core/src/main/cpp/main/include/utils.h b/core/src/main/cpp/main/include/utils.h index 33bc9f1b..4f4958f1 100644 --- a/core/src/main/cpp/main/include/utils.h +++ b/core/src/main/cpp/main/include/utils.h @@ -42,11 +42,11 @@ namespace lspd { template struct tstring : public std::integer_sequence { - const char *c_str() const { + constexpr const char *c_str() const { return str_; } - operator std::string_view() const { + constexpr operator std::string_view() const { return c_str(); } diff --git a/core/src/main/cpp/main/src/elf_util.cpp b/core/src/main/cpp/main/src/elf_util.cpp index 783b4faf..7512c527 100644 --- a/core/src/main/cpp/main/src/elf_util.cpp +++ b/core/src/main/cpp/main/src/elf_util.cpp @@ -1,7 +1,22 @@ -// From https://github.com/ganyao114/SandHook/blob/master/hooklib/src/main/cpp/utils/elf_util.cpp -// -// Created by Swift Gan on 2019/3/14. -// +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2019 Swift Gan + * Copyright (C) 2021 LSPosed Contributors + */ #include #include #include @@ -16,69 +31,96 @@ using namespace SandHook; namespace fs = std::filesystem; -ElfImg::ElfImg(const char *elf) { - this->elf = elf; +template +inline auto offsetOf(ElfW(Ehdr) *head, ElfW(Off) off) { + return reinterpret_cast, T, T *>>( + reinterpret_cast(head) + off); +} + +ElfImg::ElfImg(std::string_view elf) : elf(elf) { //load elf - int fd = open(elf, O_RDONLY); + int fd = open(elf.data(), O_RDONLY); if (fd < 0) { - LOGE("failed to open %s", elf); + LOGE("failed to open %s", elf.data()); return; } size = lseek(fd, 0, SEEK_END); if (size <= 0) { - LOGE("lseek() failed for %s", elf); + LOGE("lseek() failed for %s", elf.data()); } - header = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); + header = reinterpret_cast(mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0)); close(fd); - section_header = reinterpret_cast(((size_t) header) + header->e_shoff); + section_header = offsetOf(header, header->e_shoff); - auto shoff = reinterpret_cast(section_header); - char *section_str = reinterpret_cast(section_header[header->e_shstrndx].sh_offset + - ((size_t) header)); + auto shoff = reinterpret_cast(section_header); + char *section_str = offsetOf(header, section_header[header->e_shstrndx].sh_offset); for (int i = 0; i < header->e_shnum; i++, shoff += header->e_shentsize) { - auto *section_h = (Elf_Shdr *) shoff; + auto *section_h = (ElfW(Shdr) *) shoff; char *sname = section_h->sh_name + section_str; - Elf_Off entsize = section_h->sh_entsize; + auto entsize = section_h->sh_entsize; switch (section_h->sh_type) { - case SHT_DYNSYM: + case SHT_DYNSYM: { if (bias == -4396) { dynsym = section_h; dynsym_offset = section_h->sh_offset; dynsym_size = section_h->sh_size; - dynsym_count = dynsym_size / entsize; - dynsym_start = reinterpret_cast(((size_t) header) + dynsym_offset); + dynsym_start = offsetOf(header, dynsym_offset); } break; - case SHT_SYMTAB: + } + 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 = reinterpret_cast(((size_t) header) + symtab_offset); + symtab_start = offsetOf(header, symtab_offset); } break; - case SHT_STRTAB: + } + case SHT_STRTAB: { if (bias == -4396) { strtab = section_h; symstr_offset = section_h->sh_offset; - strtab_start = reinterpret_cast(((size_t) header) + symstr_offset); + strtab_start = offsetOf(header, symstr_offset); } if (strcmp(sname, ".strtab") == 0) { symstr_offset_for_symtab = section_h->sh_offset; } break; - case SHT_PROGBITS: + } + case SHT_PROGBITS: { if (strtab == nullptr || dynsym == nullptr) break; if (bias == -4396) { bias = (off_t) section_h->sh_addr - (off_t) section_h->sh_offset; } break; + } + case SHT_HASH: { + auto *d_un = offsetOf(header, section_h->sh_offset); + nbucket_ = d_un[0]; + bucket_ = d_un + 2; + chain_ = bucket_ + nbucket_; + break; + } + case SHT_GNU_HASH: { + auto *d_buf = reinterpret_cast(((size_t) header) + + section_h->sh_offset); + gnu_nbucket_ = d_buf[0]; + gnu_symndx_ = d_buf[1]; + gnu_bloom_size_ = d_buf[2]; + gnu_shift2_ = d_buf[3]; + gnu_bloom_filter_ = reinterpret_cast(d_buf + 4); + gnu_bucket_ = d_buf + 4 + gnu_bloom_size_ * + (header->e_ident[EI_CLASS] == ELFCLASS64 ? 2 : 1); + gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - gnu_symndx_; + break; + } } } @@ -86,6 +128,67 @@ ElfImg::ElfImg(const char *elf) { base = getModuleBase(); } +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 (int i = 0; i < symtab_count; i++) { + unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); + const char *st_name = offsetOf(header, symstr_offset_for_symtab + + symtab_start[i].st_name); + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) { + symtabs_.emplace(st_name, &symtab_start[i]); + } + } + } + } + if (auto i = symtabs_.find(name); i != symtabs_.end()) { + return i->second->st_value; + } else { + return 0; + } +} + + ElfImg::~ElfImg() { //open elf file local if (buffer) { @@ -98,54 +201,23 @@ ElfImg::~ElfImg() { } } -Elf_Addr ElfImg::getSymbOffset(const char *name) const { - Elf_Addr _offset; - - //search dynmtab - if (dynsym_start != nullptr && strtab_start != nullptr) { - Elf_Sym *sym = dynsym_start; - char *strings = (char *) strtab_start; - int k; - for (k = 0; k < dynsym_count; k++, sym++) - if (strcmp(strings + sym->st_name, name) == 0) { - _offset = sym->st_value; - LOGD("find %s: %p", elf, reinterpret_cast(_offset)); - return _offset; - } - } - - //search symtab - if (symtab_start != nullptr && symstr_offset_for_symtab != 0) { - for (int i = 0; i < symtab_count; i++) { - unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); - char *st_name = reinterpret_cast(((size_t) header) + symstr_offset_for_symtab + - symtab_start[i].st_name); - if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) { - if (strcmp(st_name, name) == 0) { - _offset = symtab_start[i].st_value; - LOGD("find %s: %p", elf, reinterpret_cast(_offset)); - return _offset; - } - } - } - } - return 0; -} - -Elf_Addr ElfImg::getSymbAddress(const char *name) const { - Elf_Addr offset = getSymbOffset(name); - Elf_Addr res; - if (offset > 0 && base != nullptr) { - res = static_cast((size_t) base + offset - bias); +ElfW(Addr) ElfImg::getSymbOffset(std::string_view name, uint32_t gnu_hash, uint32_t elf_hash) const { + if (auto offset = GnuLookup(name, gnu_hash); offset > 0) { + LOGD("found %s %p in %s in dynsym by gnuhash", name.data(), + reinterpret_cast(offset), elf.data()); + return offset; + } else if (offset = ElfLookup(name, elf_hash); offset > 0) { + LOGD("found %s %p in %s in dynsym by elfhash", name.data(), + reinterpret_cast(offset), elf.data()); + return offset; + } else if (offset = LinearLookup(name); offset > 0) { + LOGD("found %s %p in %s in symtab by linear lookup", name.data(), + reinterpret_cast(offset), elf.data()); + return offset; } else { - res = 0; + return 0; } - if (res == 0) { - LOGE("fail to get symbol %s from %s ", name, elf); - } else { - LOGD("got symbol %s form %s with %p", name, elf, (void *) res); - } - return res; + } void *ElfImg::getModuleBase() const { @@ -156,7 +228,7 @@ void *ElfImg::getModuleBase() const { char name[PATH_MAX] = {'\0'}; - strncpy(name, elf, PATH_MAX); + strncpy(name, elf.data(), PATH_MAX); { struct stat buf{}; while (lstat(name, &buf) == 0 && S_ISLNK(buf.st_mode)) { @@ -170,8 +242,6 @@ void *ElfImg::getModuleBase() const { } } - - // fs::path name(elf); // std::error_code ec; // while(fs::is_symlink(name, ec) && !ec) { @@ -192,12 +262,14 @@ void *ElfImg::getModuleBase() const { return nullptr; } - if (sscanf(buff, "%lx", &load_addr) != 1) + if (char *next = buff; load_addr = strtoul(buff, &next, 16), next == buff) { LOGE("failed to read load address for %s", name); + } fclose(maps); - LOGD("get module base %s: %lu", name, load_addr); + LOGD("get module base %s: %lx", name, load_addr); return reinterpret_cast(load_addr); } + diff --git a/core/src/main/cpp/main/src/elf_util.h b/core/src/main/cpp/main/src/elf_util.h index 9159bcc4..6fa3cf3a 100644 --- a/core/src/main/cpp/main/src/elf_util.h +++ b/core/src/main/cpp/main/src/elf_util.h @@ -1,74 +1,125 @@ -// From https://github.com/ganyao114/SandHook/blob/master/hooklib/src/main/cpp/includes/elf_util.h -// -// Created by Swift Gan on 2019/3/14. -// +/* + * This file is part of LSPosed. + * + * LSPosed is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * LSPosed is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LSPosed. If not, see . + * + * Copyright (C) 2019 Swift Gan + * Copyright (C) 2021 LSPosed Contributors + */ #ifndef SANDHOOK_ELF_UTIL_H #define SANDHOOK_ELF_UTIL_H +#include +#include #include #include +#include +#include "config.h" -#if defined(__LP64__) -typedef Elf64_Ehdr Elf_Ehdr; -typedef Elf64_Shdr Elf_Shdr; -typedef Elf64_Addr Elf_Addr; -typedef Elf64_Dyn Elf_Dyn; -typedef Elf64_Rela Elf_Rela; -typedef Elf64_Sym Elf_Sym; -typedef Elf64_Off Elf_Off; - -#define ELF_R_SYM(i) ELF64_R_SYM(i) -#else -typedef Elf32_Ehdr Elf_Ehdr; -typedef Elf32_Shdr Elf_Shdr; -typedef Elf32_Addr Elf_Addr; -typedef Elf32_Dyn Elf_Dyn; -typedef Elf32_Rel Elf_Rela; -typedef Elf32_Sym Elf_Sym; -typedef Elf32_Off Elf_Off; - -#define ELF_R_SYM(i) ELF32_R_SYM(i) -#endif +#define SHT_GNU_HASH 0x6ffffff6 namespace SandHook { - class ElfImg { public: - ElfImg(const char* elf); + ElfImg(std::string_view elf); - Elf_Addr getSymbOffset(const char* name) const; + constexpr ElfW(Addr) getSymbOffset(std::string_view name) const { + return getSymbOffset(name, GnuHash(name), ElfHash(name)); + } - void* getModuleBase() const; + void *getModuleBase() const; - Elf_Addr getSymbAddress(const char* name) const; + constexpr ElfW(Addr) getSymbAddress(std::string_view name) const { + ElfW(Addr) offset = getSymbOffset(name); + if (offset > 0 && base != nullptr) { + return static_cast((uintptr_t) base + offset - bias); + } else { + LOGE("fail to get symbol %s from %s ", name.data(), elf.data()); + return 0; + } + } ~ElfImg(); private: - const char* elf = nullptr; - void* base = nullptr; - char* buffer = nullptr; + ElfW(Addr) getSymbOffset(std::string_view name, uint32_t gnu_hash, uint32_t elf_hash) const; + + ElfW(Addr) ElfLookup(std::string_view name, uint32_t hash) const; + + ElfW(Addr) GnuLookup(std::string_view name, uint32_t hash) const; + + ElfW(Addr) LinearLookup(std::string_view name) const; + + constexpr static uint32_t ElfHash(std::string_view name); + + constexpr static uint32_t GnuHash(std::string_view name); + + std::string_view elf; + void *base = nullptr; + char *buffer = nullptr; off_t size = 0; off_t bias = -4396; - Elf_Ehdr* header = nullptr; - Elf_Shdr* section_header = nullptr; - Elf_Shdr* symtab = nullptr; - Elf_Shdr* strtab = nullptr; - Elf_Shdr* dynsym = nullptr; - Elf_Off dynsym_count = 0; - Elf_Sym* symtab_start = nullptr; - Elf_Sym* dynsym_start = nullptr; - Elf_Sym* strtab_start = nullptr; - Elf_Off symtab_count = 0; - Elf_Off symstr_offset = 0; - Elf_Off symstr_offset_for_symtab = 0; - Elf_Off symtab_offset = 0; - Elf_Off dynsym_offset = 0; - Elf_Off symtab_size = 0; - Elf_Off dynsym_size = 0; + 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; + ElfW(Off) dynsym_size = 0; + + uint32_t nbucket_{}; + uint32_t *bucket_ = nullptr; + uint32_t *chain_ = nullptr; + + uint32_t gnu_nbucket_{}; + uint32_t gnu_symndx_{}; + uint32_t gnu_bloom_size_; + uint32_t gnu_shift2_; + uintptr_t *gnu_bloom_filter_; + uint32_t *gnu_bucket_; + uint32_t *gnu_chain_; + + mutable std::unordered_map symtabs_; }; + constexpr uint32_t ElfImg::ElfHash(std::string_view name) { + uint32_t h = 0, g; + 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 diff --git a/core/src/main/cpp/main/src/main.cpp b/core/src/main/cpp/main/src/main.cpp index 210c81fe..57085948 100644 --- a/core/src/main/cpp/main/src/main.cpp +++ b/core/src/main/cpp/main/src/main.cpp @@ -112,7 +112,7 @@ namespace lspd { }; } -__attribute__((noinline)) RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) { +RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) { LOGD("using riru %d", riru->riruApiVersion); LOGD("module path: %s", riru->magiskModulePath); lspd::magiskPath = riru->magiskModulePath; @@ -125,5 +125,4 @@ __attribute__((noinline)) RIRU_EXPORT RiruVersionedModuleInfo *init(Riru *riru) } int main() { - init(nullptr); } diff --git a/core/src/main/cpp/main/src/symbol_cache.cpp b/core/src/main/cpp/main/src/symbol_cache.cpp index 73290722..6f2f23cf 100644 --- a/core/src/main/cpp/main/src/symbol_cache.cpp +++ b/core/src/main/cpp/main/src/symbol_cache.cpp @@ -43,8 +43,8 @@ namespace lspd { soinfo *somain = nullptr; template - inline T *getStaticVariable(const SandHook::ElfImg &linker, std::string_view name) { - auto *addr = reinterpret_cast(linker.getSymbAddress(name.data())); + constexpr inline T *getStaticVariable(const SandHook::ElfImg &linker, std::string_view name) { + auto *addr = reinterpret_cast(linker.getSymbAddress(name)); return addr == nullptr ? nullptr : *addr; }