improve: SoInfo hiding code compatibility

This commit improves the compatibility of SoInfo hiding code with more Android versions, like Android 16 QPR1 Beta 1 and newer versions, and also reduces complexity of the code.
This commit is contained in:
ThePedroo
2025-06-08 00:31:10 -03:00
parent ec705fb260
commit 34643c794f
6 changed files with 107 additions and 94 deletions

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -2,27 +2,28 @@
#include <stdbool.h>
#include <string.h>
#include <android/dlext.h>
#include <linux/limits.h>
#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);
}
}

View File

@@ -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

View File

@@ -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);