diff --git a/loader/src/common/elf_util.cpp b/loader/src/common/elf_util.cpp index 4ea0732a..d265b3c6 100644 --- a/loader/src/common/elf_util.cpp +++ b/loader/src/common/elf_util.cpp @@ -179,6 +179,7 @@ ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { } } } + if (auto i = symtabs_.find(name); i != symtabs_.end()) { return i->second->st_value; } else { @@ -186,6 +187,33 @@ ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { } } +std::string_view ElfImg::LinearLookupByPrefix(std::string_view name) const { + if (symtabs_.empty()) { + symtabs_.reserve(symtab_count); + if (symtab_start != nullptr && symstr_offset_for_symtab != 0) { + for (ElfW(Off) i = 0; i < symtab_count; i++) { + unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); + const char *st_name = offsetOf(header, symstr_offset_for_symtab + + symtab_start[i].st_name); + if ((st_type == STT_FUNC || st_type == STT_OBJECT) && symtab_start[i].st_size) { + symtabs_.emplace(st_name, &symtab_start[i]); + } + } + } + } + + auto size = name.size(); + for (auto symtab : symtabs_) { + if (symtab.first.size() < size) continue; + + if (symtab.first.substr(0, size) == name) { + return symtab.first; + } + } + + return ""; +} + ElfImg::~ElfImg() { //open elf file local diff --git a/loader/src/include/elf_util.h b/loader/src/include/elf_util.h index 95559f8e..1b8c0b73 100644 --- a/loader/src/include/elf_util.h +++ b/loader/src/include/elf_util.h @@ -48,6 +48,10 @@ namespace SandHook { } } + std::string_view findSymbolNameByPrefix(std::string_view prefix) const { + return LinearLookupByPrefix(prefix); + } + template constexpr T getSymbAddress(std::string_view name) const { return reinterpret_cast(getSymbAddress(name)); @@ -72,6 +76,8 @@ namespace SandHook { ElfW(Addr) LinearLookup(std::string_view name) const; + std::string_view LinearLookupByPrefix(std::string_view name) const; + constexpr static uint32_t ElfHash(std::string_view name); constexpr static uint32_t GnuHash(std::string_view name); diff --git a/loader/src/include/solist.hpp b/loader/src/include/solist.hpp index 5feb94d3..73ff917f 100644 --- a/loader/src/include/solist.hpp +++ b/loader/src/include/solist.hpp @@ -6,98 +6,127 @@ #include #include "elf_util.h" -namespace SoList -{ - class SoInfo { - public: -#ifdef __LP64__ +namespace SoList { + class SoInfo { + public: + #ifdef __LP64__ inline static size_t solist_next_offset = 0x30; constexpr static size_t solist_realpath_offset = 0x1a8; -#else + #else inline static size_t solist_next_offset = 0xa4; constexpr static size_t solist_realpath_offset = 0x174; -#endif + #endif - inline static const char *(*get_realpath_sym)(SoInfo *) = nullptr; - inline static const char *(*get_soname_sym)(SoInfo *) = nullptr; + inline static const char *(*get_realpath_sym)(SoInfo *) = NULL; + inline static const char *(*get_soname_sym)(SoInfo *) = NULL; - inline SoInfo *get_next() { - return *(SoInfo **) ((uintptr_t) this + solist_next_offset); - } + inline SoInfo *get_next() { + return *(SoInfo **) ((uintptr_t) this + solist_next_offset); + } - inline const char *get_path() { - return get_realpath_sym ? get_realpath_sym(this) : ((std::string *) ((uintptr_t) this + solist_realpath_offset))->c_str(); - } + inline const char *get_path() { + if (get_realpath_sym) return get_realpath_sym(this); - inline const char *get_name() { - return get_soname_sym ? get_soname_sym(this) : *((const char **) ((uintptr_t) this + solist_realpath_offset - sizeof(void *))); - } + return ((std::string *) ((uintptr_t) this + solist_realpath_offset))->c_str(); + } - void nullify_name() { - const char** name = (const char**)get_soname_sym(this); + inline const char *get_name() { + if (get_soname_sym) return get_soname_sym(this); - static const char* empty_string = ""; - *name = reinterpret_cast(&empty_string); - } + return ((std::string *) ((uintptr_t) this + solist_realpath_offset - sizeof(void *)))->c_str(); + } - void nullify_path() { - const char** name = (const char**)get_realpath_sym(this); + void nullify_name() { + const char **name = (const char**)get_soname_sym(this); - static const char* empty_string = ""; - *name = reinterpret_cast(&empty_string); - } - }; + static const char *empty_string = ""; + *name = reinterpret_cast(&empty_string); + } - static SoInfo *solist = nullptr; - static SoInfo *somain = nullptr; + void nullify_path() { + const char **name = (const char**)get_realpath_sym(this); - template - inline T *getStaticPointer(const SandHook::ElfImg &linker, const char* name) - { - auto *addr = reinterpret_cast(linker.getSymbAddress(name)); - return addr == nullptr ? nullptr : *addr; + static const char *empty_string = ""; + *name = reinterpret_cast(&empty_string); + } + }; + + static SoInfo *solist = NULL; + static SoInfo *somain = NULL; + + template + inline T *getStaticPointer(const SandHook::ElfImg &linker, const char *name) { + auto *addr = reinterpret_cast(linker.getSymbAddress(name)); + + return addr == NULL ? NULL : *addr; + } + + static void NullifySoName(const char* target_name) { + for (auto *iter = solist; iter; iter = iter->get_next()) { + if (iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_name)) { + iter->nullify_path(); + LOGI("Cleared SOList entry for %s", target_name); + } + } + + for (auto *iter = somain; iter; iter = iter->get_next()) { + if (iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_name)) { + iter->nullify_path(); + + break; + } } + } + + static bool Initialize() { + SandHook::ElfImg linker("/linker"); + + /* 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. + */ - static void NullifySoName(const char* target_name) { - for (auto *iter = solist; iter; iter = iter->get_next()) { - if (iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_name)) { - iter->nullify_path(); - LOGI("Cleared SOList entry for %s", target_name); - } - } - - for (auto *iter = somain; iter; iter = iter->get_next()) { - if (iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_name)) { - iter->nullify_path(); - break; - } - } + std::string_view solist_sym_name = linker.findSymbolNameByPrefix("__dl__ZL6solist"); + if (solist_sym_name.empty()) return false; + + /* INFO: The size isn't a magic number, it's the size for the string: .llvm.7690929523238822858 */ + char llvm_sufix[25 + 1]; + + if (solist_sym_name.length() != strlen("__dl__ZL6solist")) { + strncpy(llvm_sufix, solist_sym_name.data() + strlen("__dl__ZL6solist"), sizeof(llvm_sufix)); + } else { + llvm_sufix[0] = '\0'; } - static bool Initialize() { - SandHook::ElfImg linker("/linker"); - solist = getStaticPointer(linker, "__dl__ZL6solist"); - somain = getStaticPointer(linker, "__dl__ZL6somain"); - - if (solist != nullptr && somain != nullptr) - { - SoInfo::get_realpath_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv")); - SoInfo::get_soname_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo10get_sonameEv")); - auto vsdo = getStaticPointer(linker, "__dl__ZL4vdso"); - - for (size_t i = 0; i < 1024 / sizeof(void *); i++) - { - auto *possible_next = *(void **) ((uintptr_t) solist + i * sizeof(void *)); - if (possible_next == somain || (vsdo != nullptr && possible_next == vsdo)) - { - SoInfo::solist_next_offset = i * sizeof(void *); - break; - } - } - - return (SoInfo::get_realpath_sym != nullptr && SoInfo::get_soname_sym != nullptr); - } - - return false; + solist = getStaticPointer(linker, solist_sym_name.data()); + if (solist == NULL) return false; + + char somain_sym_name[sizeof("__dl__ZL6somain") + sizeof(llvm_sufix)]; + snprintf(somain_sym_name, sizeof(somain_sym_name), "__dl__ZL6somain%s", llvm_sufix); + + char vsdo_sym_name[sizeof("__dl__ZL4vdso") + sizeof(llvm_sufix)]; + snprintf(vsdo_sym_name, sizeof(vsdo_sym_name), "__dl__ZL4vdso%s", llvm_sufix); + + somain = getStaticPointer(linker, somain_sym_name); + if (somain == NULL) return false; + + auto vsdo = getStaticPointer(linker, vsdo_sym_name); + + SoInfo::get_realpath_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv")); + SoInfo::get_soname_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo10get_sonameEv")); + + for (size_t i = 0; i < 1024 / sizeof(void *); i++) { + auto *possible_next = *(void **) ((uintptr_t) solist + i * sizeof(void *)); + if (possible_next == somain || (vsdo != NULL && possible_next == vsdo)) { + SoInfo::solist_next_offset = i * sizeof(void *); + + break; + } } -} \ No newline at end of file + + return (SoInfo::get_realpath_sym != NULL && SoInfo::get_soname_sym != NULL); + } +}