diff --git a/loader/src/injector/entry.cpp b/loader/src/injector/entry.cpp index 543a374..c2e008e 100644 --- a/loader/src/injector/entry.cpp +++ b/loader/src/injector/entry.cpp @@ -22,5 +22,7 @@ void entry(void* addr, size_t size, const char* path) { LOGD("start plt hooking"); hook_functions(); - clean_trace(path, 1, 0, false); + + void *module_addrs[1] = { addr }; + clean_trace(path, module_addrs, 1, 1, 0, false); } diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index cd88f25..c4fbdf4 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -676,7 +676,19 @@ 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); + + /* INFO: While Variable Length Arrays (VLAs) aren't usually + recommended due to the ease of using too much of the + stack, this should be fine since it should not be + possible to exhaust the stack with only a few addresses. */ + void *module_addrs[modules.size() * sizeof(void *)]; + + size_t i = 0; + for (const auto &m : modules) { + module_addrs[i++] = m.getHandle(); + } + + clean_trace("/data/adb", module_addrs, modules.size(), modules.size(), modules_unloaded, false); } } @@ -866,19 +878,35 @@ static void hook_register(dev_t dev, ino_t inode, const char *symbol, void *new_ #define PLT_HOOK_REGISTER(DEV, INODE, NAME) \ PLT_HOOK_REGISTER_SYM(DEV, INODE, #NAME, NAME) -void clean_trace(const char* path, size_t load, size_t unload, bool spoof_maps) { +/* INFO: module_addrs_length is always the same as "load" */ +void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload, bool spoof_maps) { LOGD("cleaning trace for path %s", path); if (load > 0 || unload > 0) solist_reset_counters(load, unload); LOGD("Dropping solist record for %s", path); - bool path_found = solist_drop_so_path(path); - if (!path_found || !spoof_maps) return; + bool any_dropped = false; + for (size_t i = 0; i < module_addrs_length; i++) { + bool local_any_dropped = solist_drop_so_path(module_addrs[i]); + if (!local_any_dropped) continue; + + any_dropped = true; + + LOGD("Dropped solist record for %p", module_addrs[i]); + } + + if (!any_dropped || !spoof_maps) return; LOGD("spoofing virtual maps for %s", path); - // spoofing map names is futile in Android, we do it simply - // to avoid Zygisk detections based on string comparison + + /* INFO: Spoofing maps names is futile, after all it will + still show up in /proc/self/(s)maps but with a + different name, however still detectable by + checking the permissions. This, however, avoids + just checking for "zygisk". */ + + /* TODO: Use SoList to map through libraries to avoid open /proc/self/maps here */ for (auto &map : lsplt::MapInfo::Scan()) { if (strstr(map.path.c_str(), path) && strstr(map.path.c_str(), "libzygisk") == 0) { diff --git a/loader/src/injector/module.hpp b/loader/src/injector/module.hpp index f02bd7d..2fd4e20 100644 --- a/loader/src/injector/module.hpp +++ b/loader/src/injector/module.hpp @@ -213,6 +213,7 @@ case 5: \ bool tryUnload() const { return unload && dlclose(handle) == 0; }; void clearApi() { memset(&api, 0, sizeof(api)); } int getId() const { return id; } + void *getHandle() const { return handle; } ZygiskModule(int id, void *handle, void *entry); diff --git a/loader/src/injector/solist.c b/loader/src/injector/solist.c index a410218..e2f657e 100644 --- a/loader/src/injector/solist.c +++ b/loader/src/injector/solist.c @@ -2,27 +2,28 @@ #include #include +#include + +#include + #include "elf_util.h" #include "logging.h" #include "solist.h" +/* TODO: Is offset for realpath necessary? It seems to have the function + available anywhere. */ #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 SoInfo *(*find_containing_library)(const void *p) = NULL; static inline const char *get_path(SoInfo *self) { if (get_realpath_sym) @@ -35,11 +36,7 @@ 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; +struct pdg ppdg = { 0 }; static bool pdg_setup(ElfImg *img) { ppdg.ctor = (void *(*)())getSymbAddress(img, "__dl__ZN18ProtectedDataGuardC2Ev"); @@ -48,22 +45,20 @@ static bool pdg_setup(ElfImg *img) { return ppdg.ctor != NULL && ppdg.dtor != NULL; } -static void pdg_protect() { - if (ppdg.ctor != NULL) - (*(ppdg.ctor))(); -} - +/* INFO: Allow data to be written to the areas. */ static void pdg_unprotect() { - if (ppdg.dtor != NULL) - (*(ppdg.dtor))(); + (*ppdg.ctor)(); } -static SoInfo *solist = NULL; -static SoInfo *somain = NULL; -static SoInfo **sonext = NULL; +/* INFO: Block write and only allow read access to the areas. */ +static void pdg_protect() { + (*ppdg.dtor)(); +} -static uint64_t *g_module_load_counter = NULL; -static uint64_t *g_module_unload_counter = NULL; +static SoInfo *somain = NULL; + +static size_t *g_module_load_counter = NULL; +static size_t *g_module_unload_counter = NULL; static bool solist_init() { #ifdef __LP64__ @@ -77,10 +72,6 @@ static bool solist_init() { return false; } - ppdg = (struct pdg) { - .ctor = NULL, - .dtor = NULL - }; if (!pdg_setup(linker)) { LOGE("Failed to setup pdg"); @@ -96,17 +87,6 @@ static bool solist_init() { See #63 for more information. */ - solist = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6solist"); - if ((void *)solist == NULL) { - LOGE("Failed to find solist __dl__ZL6solist*"); - - ElfImg_destroy(linker); - - return false; - } - - LOGD("%p is solist", (void *)solist); - somain = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL6somain"); if (somain == NULL) { LOGE("Failed to find somain __dl__ZL6somain*"); @@ -118,20 +98,6 @@ static bool solist_init() { LOGD("%p is somain", (void *)somain); - sonext = (SoInfo **)getSymbAddressByPrefix(linker, "__dl__ZL6sonext"); - if (sonext == NULL) { - LOGE("Failed to find sonext __dl__ZL6sonext*"); - - ElfImg_destroy(linker); - - return false; - } - - LOGD("%p is sonext", (void *)sonext); - - SoInfo *vdso = (SoInfo *)getSymbValueByPrefix(linker, "__dl__ZL4vdso"); - if (vdso != NULL) LOGD("%p is vdso", (void *)vdso); - 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"); @@ -154,25 +120,28 @@ static bool solist_init() { LOGD("%p is soinfo_free", (void *)soinfo_free); - g_module_load_counter = (uint64_t *)getSymbAddress(linker, "__dl__ZL21g_module_load_counter"); + find_containing_library = (SoInfo *(*)(const void *))getSymbAddress(linker, "__dl__Z23find_containing_libraryPKv"); + if (find_containing_library != NULL) { + LOGE("Failed to find find_containing_library __dl__Z23find_containing_libraryPKv"); + + ElfImg_destroy(linker); + + return false; + } + + g_module_load_counter = (size_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"); + g_module_unload_counter = (size_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; } @@ -183,36 +152,49 @@ static bool solist_init() { return true; } -bool solist_drop_so_path(const char *target_path) { - if (solist == NULL && !solist_init()) { +/* INFO: find_containing_library returns the SoInfo for the library that contains + that memory inside its limits, hence why named "lib_memory" in ReZygisk. */ +bool solist_drop_so_path(void *lib_memory) { + if (somain == 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(); + SoInfo *found = (*find_containing_library)(lib_memory); + if (found == NULL) { + LOGD("Could not find containing library for %p", lib_memory); - 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; } - return false; + LOGD("Found so path for %p: %s", lib_memory, get_path(found)); + + char path[PATH_MAX]; + if (get_path(found) == NULL) { + LOGE("Failed to get path for %p", found); + + return false; + } + strcpy(path, get_path(found)); + + pdg_unprotect(); + + set_size(found, 0); + soinfo_free(found); + + pdg_protect(); + + LOGD("Successfully dropped so path for: %s", path); + + /* INFO: Let's avoid trouble regarding detections */ + memset(path, strlen(path), 0); + + return true; } void solist_reset_counters(size_t load, size_t unload) { - if (solist == NULL && !solist_init()) { + if (somain == NULL && !solist_init()) { LOGE("Failed to initialize solist"); return; @@ -224,18 +206,18 @@ void solist_reset_counters(size_t load, size_t unload) { return; } - uint64_t loaded_modules = *g_module_load_counter; - uint64_t unloaded_modules = *g_module_unload_counter; + size_t loaded_modules = *g_module_load_counter; + size_t unloaded_modules = *g_module_unload_counter; if (loaded_modules >= load) { - *g_module_load_counter = loaded_modules - load; + *g_module_load_counter -= load; - LOGD("reset g_module_load_counter to %zu", (size_t) *g_module_load_counter); + LOGD("reset g_module_load_counter to %zu", *g_module_load_counter); } if (unloaded_modules >= unload) { - *g_module_unload_counter = unloaded_modules - unload; + *g_module_unload_counter -= unload; - LOGD("reset g_module_unload_counter to %zu", (size_t) *g_module_unload_counter); + LOGD("reset g_module_unload_counter to %zu", *g_module_unload_counter); } } diff --git a/loader/src/injector/solist.h b/loader/src/injector/solist.h index 5c33e73..15a2a6c 100644 --- a/loader/src/injector/solist.h +++ b/loader/src/injector/solist.h @@ -35,7 +35,7 @@ struct pdg { SOURCES: - https://android.googlesource.com/platform/bionic/+/refs/heads/android15-release/linker/linker.cpp#1712 */ -bool solist_drop_so_path(const char *target_path); +bool solist_drop_so_path(void *lib_memory); /* INFO: When dlopen'ing a library, the system will increment 1 to a global diff --git a/loader/src/injector/zygisk.hpp b/loader/src/injector/zygisk.hpp index d171890..8051de6 100644 --- a/loader/src/injector/zygisk.hpp +++ b/loader/src/injector/zygisk.hpp @@ -7,4 +7,4 @@ extern size_t block_size; void hook_functions(); -void clean_trace(const char* path, size_t load = 1, size_t unload = 0, bool spoof_maps = false); +void clean_trace(const char *path, void **module_addrs, size_t module_addrs_length, size_t load, size_t unload, bool spoof_maps);