From e3cb1fb64e8ac540d19a32b85b3040b0e6f672d3 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 29 Nov 2024 11:25:45 +0100 Subject: [PATCH] improve: drop soinfo records of loaded modules In Bionic linker, the `soinfo` structure has a field `next`, which points to the next loaded library in a linked list consisting of all loaded libraries. Hence, an injected process can easily find all loaded libraries. Previously in ReZygisk, module library records are hidden by setting the `pathname` field to be empty, which is futile but easier to detect. Current idea of dropping record can be found in the following commit: https://github.com/RikkaApps/Riru/commit/5d635e8c66a018b5086f28e083b18b0d0656022b --- loader/src/include/solist.hpp | 84 ++++++++++++++++++++++++++--------- loader/src/injector/hook.cpp | 2 +- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/loader/src/include/solist.hpp b/loader/src/include/solist.hpp index 73ff917f..2252b00e 100644 --- a/loader/src/include/solist.hpp +++ b/loader/src/include/solist.hpp @@ -36,23 +36,57 @@ namespace SoList { return ((std::string *) ((uintptr_t) this + solist_realpath_offset - sizeof(void *)))->c_str(); } - void nullify_name() { - const char **name = (const char**)get_soname_sym(this); + void set_next(SoInfo *si) { + *(SoInfo **) ((uintptr_t) this + solist_next_offset) = si; + } + }; - static const char *empty_string = ""; - *name = reinterpret_cast(&empty_string); + class ProtectedDataGuard { + public: + ProtectedDataGuard() { + if (ctor != nullptr) + (this->*ctor)(); } - void nullify_path() { - const char **name = (const char**)get_realpath_sym(this); + ~ProtectedDataGuard() { + if (dtor != nullptr) + (this->*dtor)(); + } - static const char *empty_string = ""; - *name = reinterpret_cast(&empty_string); + static bool setup(const SandHook::ElfImg &linker) { + ctor = MemFunc{.data = {.p = reinterpret_cast(linker.getSymbAddress( + "__dl__ZN18ProtectedDataGuardC2Ev")), .adj = 0}}.f; + dtor = MemFunc{.data = {.p = reinterpret_cast(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::*)(); + + static FuncType ctor; + static FuncType dtor; + + union MemFunc { + FuncType f; + + struct { + void *p; + std::ptrdiff_t adj; + } data; + }; }; + static SoInfo *solist = NULL; static SoInfo *somain = NULL; + static SoInfo **sonext = NULL; + ProtectedDataGuard::FuncType ProtectedDataGuard::ctor = NULL; + ProtectedDataGuard::FuncType ProtectedDataGuard::dtor = NULL; template inline T *getStaticPointer(const SandHook::ElfImg &linker, const char *name) { @@ -61,25 +95,24 @@ namespace SoList { 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 void DropSoPath(const char* target_path) { + SoInfo *prev = NULL; + for (auto iter = solist; iter; iter = iter->get_next()) { + if (prev != NULL && iter->get_name() && iter->get_path() && strstr(iter->get_path(), target_path)) { + SoList::ProtectedDataGuard guard; + prev->set_next(iter->get_next()); + if (iter == *sonext) { + *sonext = prev; + } + LOGI("Dropped solist record for %s loaded at %s", iter->get_name(), iter->get_path()); } + prev = iter; } } static bool Initialize() { SandHook::ElfImg linker("/linker"); + if (!ProtectedDataGuard::setup(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 @@ -107,13 +140,20 @@ namespace SoList { char somain_sym_name[sizeof("__dl__ZL6somain") + sizeof(llvm_sufix)]; snprintf(somain_sym_name, sizeof(somain_sym_name), "__dl__ZL6somain%s", llvm_sufix); + char sonext_sym_name[sizeof("__dl__ZL6sonext") + sizeof(llvm_sufix)]; + snprintf(sonext_sym_name, sizeof(somain_sym_name), "__dl__ZL6sonext%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); + sonext = linker.getSymbAddress(sonext_sym_name); + if (sonext == NULL) return false; + + SoInfo *vsdo = getStaticPointer(linker, vsdo_sym_name); + if (vsdo == NULL) return false; SoInfo::get_realpath_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo12get_realpathEv")); SoInfo::get_soname_sym = reinterpret_cast(linker.getSymbAddress("__dl__ZNK6soinfo10get_sonameEv")); diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 4f02f6bf..f9b18871 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -587,7 +587,7 @@ void ZygiskContext::run_modules_post() { if (!solist_res) { LOGE("Failed to initialize SoList"); } else { - SoList::NullifySoName("jit-cache"); + SoList::DropSoPath("jit-cache"); } // Remap as well to avoid checking of /memfd:jit-cache