You've already forked ReZygisk
mirror of
https://github.com/PerformanC/ReZygisk.git
synced 2025-09-06 06:37:01 +00:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user