From 5e072bd919d6c2b38d31359762c500a05c48f87b Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Sun, 15 Dec 2024 01:05:23 +0100 Subject: [PATCH] improve: cache scanned virtual maps Reading the file `/proc/self/maps` is detectable by the target process. Hence, we should cache scanned virtual maps after `libart.so` is loaded for later plt hooks in the target process. --- .gitmodules | 2 +- loader/src/external/lsplt | 2 +- loader/src/injector/hook.cpp | 41 +++++++++++++++++++++++------------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.gitmodules b/.gitmodules index 340c732..e46b92c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "loader/src/external/lsplt"] path = loader/src/external/lsplt - url = https://github.com/LSPosed/lsplt + url = https://github.com/JingMatrix/LSPlt diff --git a/loader/src/external/lsplt b/loader/src/external/lsplt index 5d2b820..9848042 160000 --- a/loader/src/external/lsplt +++ b/loader/src/external/lsplt @@ -1 +1 @@ -Subproject commit 5d2b820cf968fcd8162697d208ad406805b6db25 +Subproject commit 984804293e6f7f87604479fefd57976a2c222a89 diff --git a/loader/src/injector/hook.cpp b/loader/src/injector/hook.cpp index 0c86588..ef0ad6a 100644 --- a/loader/src/injector/hook.cpp +++ b/loader/src/injector/hook.cpp @@ -122,6 +122,7 @@ struct ZygiskContext { vector> *plt_hook_list; map, StringCmp> *jni_hook_list; bool should_unmap_zygisk = false; +std::vector cached_map_infos = {}; } // namespace @@ -187,6 +188,7 @@ DCL_HOOK_FUNC(int, pthread_attr_setstacksize, void *target, size_t size) { if (should_unmap_zygisk) { unhook_functions(); + cached_map_infos.clear(); if (should_unmap_zygisk) { // Because both `pthread_attr_setstacksize` and `dlclose` have the same function signature, // we can use `musttail` to let the compiler reuse our stack frame and thus @@ -205,6 +207,8 @@ DCL_HOOK_FUNC(char *, strdup, const char *s) { if (strcmp(s, "com.android.internal.os.ZygoteInit") == 0) { LOGV("strdup %s", s); initialize_jni_hook(); + cached_map_infos = lsplt::MapInfo::Scan(); + LOGD("cached_map_infos updated"); } return old_strdup(s); @@ -268,7 +272,7 @@ void initialize_jni_hook() { auto get_created_java_vms = reinterpret_cast( dlsym(RTLD_DEFAULT, "JNI_GetCreatedJavaVMs")); if (!get_created_java_vms) { - for (auto &map: lsplt::MapInfo::Scan()) { + for (auto &map: cached_map_infos) { if (!map.path.ends_with("/libnativehelper.so")) continue; void *h = dlopen(map.path.data(), RTLD_LAZY); if (!h) { @@ -349,7 +353,7 @@ bool ZygiskModule::RegisterModuleImpl(ApiTable *api, long *module) { api->v2.getFlags = [](auto) { return ZygiskModule::getFlags(); }; } if (api_version >= 4) { - api->v4.pltHookCommit = lsplt::CommitHook; + api->v4.pltHookCommit = []() { return lsplt::CommitHook(cached_map_infos); }; api->v4.pltHookRegister = [](dev_t dev, ino_t inode, const char *symbol, void *fn, void **backup) { if (dev == 0 || inode == 0 || symbol == nullptr || fn == nullptr) return; @@ -383,7 +387,7 @@ void ZygiskContext::plt_hook_exclude(const char *regex, const char *symbol) { void ZygiskContext::plt_hook_process_regex() { if (register_info.empty()) return; - for (auto &map : lsplt::MapInfo::Scan()) { + for (auto &map : cached_map_infos) { if (map.offset != 0 || !map.is_private || !(map.perms & PROT_READ)) continue; for (auto ®: register_info) { if (regexec(®.regex, map.path.data(), 0, nullptr, 0) != 0) @@ -411,7 +415,7 @@ bool ZygiskContext::plt_hook_commit() { register_info.clear(); ignore_info.clear(); } - return lsplt::CommitHook(); + return lsplt::CommitHook(cached_map_infos); } @@ -719,8 +723,8 @@ ZygiskContext::~ZygiskContext() { } // namespace -static bool hook_commit() { - if (lsplt::CommitHook()) { +static bool hook_commit(std::vector &map_infos = cached_map_infos) { + if (lsplt::CommitHook(map_infos)) { return true; } else { LOGE("plt_hook failed"); @@ -752,23 +756,23 @@ void clean_trace(const char* path, bool spoof_maps) { 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 - for (auto &info : lsplt::MapInfo::Scan()) { - if (strstr(info.path.c_str(), path)) + for (auto &map : lsplt::MapInfo::Scan()) { + if (strstr(map.path.c_str(), path)) { - void *addr = (void *)info.start; - size_t size = info.end - info.start; + void *addr = (void *)map.start; + size_t size = map.end - map.start; void *copy = mmap(nullptr, size, PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); if (copy == MAP_FAILED) { - LOGE("failed to backup block %s [%p, %p]", info.path.c_str(), addr, (void*)info.end); + LOGE("failed to backup block %s [%p, %p]", map.path.c_str(), addr, (void*)map.end); continue; } - if ((info.perms & PROT_READ) == 0) { + if ((map.perms & PROT_READ) == 0) { mprotect(addr, size, PROT_READ); } memcpy(copy, addr, size); mremap(copy, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, addr); - mprotect(addr, size, info.perms); + mprotect(addr, size, map.perms); } } } @@ -783,7 +787,8 @@ void hook_functions() { // ino_t native_bridge_inode = 0; // dev_t native_bridge_dev = 0; - for (auto &map : lsplt::MapInfo::Scan()) { + cached_map_infos = lsplt::MapInfo::Scan(); + for (auto &map : cached_map_infos) { if (map.path.ends_with("libandroid_runtime.so")) { android_runtime_inode = map.inode; android_runtime_dev = map.dev; @@ -809,7 +814,7 @@ static void hook_unloader() { ino_t art_inode = 0; dev_t art_dev = 0; - for (auto &map : lsplt::MapInfo::Scan()) { + for (auto &map : cached_map_infos) { if (map.path.ends_with("/libart.so")) { art_inode = map.inode; art_dev = map.dev; @@ -817,6 +822,12 @@ static void hook_unloader() { } } + if (art_dev == 0 || art_inode == 0) { + LOGE("virtual map for libart.so is not cached"); + return; + } else { + LOGD("hook_unloader called with libart.so [%zu:%lu]", art_dev, art_inode); + } PLT_HOOK_REGISTER(art_dev, art_inode, pthread_attr_setstacksize); hook_commit(); }