From 67fd7ca054b45d59f9ddc2c8b1588c61733e35e4 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Mon, 25 Oct 2021 00:38:30 +0800 Subject: [PATCH] [core] Cache symbol address on Zygisk (#1326) --- core/magisk_module/util_functions.sh | 16 +- core/src/main/cpp/main/api/riru_main.cpp | 2 + core/src/main/cpp/main/api/zygisk_main.cpp | 192 +++++++++++++++--- .../cpp/main/include/art/runtime/hidden_api.h | 10 +- core/src/main/cpp/main/src/context.cpp | 13 +- core/src/main/cpp/main/src/native_api.cpp | 6 +- core/src/main/cpp/main/src/native_hook.cpp | 4 +- core/src/main/cpp/main/src/service.cpp | 6 +- core/src/main/cpp/main/src/symbol_cache.cpp | 58 +++--- core/src/main/cpp/main/src/symbol_cache.h | 42 +++- 10 files changed, 264 insertions(+), 85 deletions(-) diff --git a/core/magisk_module/util_functions.sh b/core/magisk_module/util_functions.sh index ebfc3633..f03ae200 100644 --- a/core/magisk_module/util_functions.sh +++ b/core/magisk_module/util_functions.sh @@ -20,9 +20,21 @@ check_magisk_version() { ui_print "- Magisk version: $MAGISK_VER_CODE" - if [ "$MAGISK_VER_CODE" -lt 23000 ]; then + if [ "$FLAVOR" == "riru" ]; then + if [ "$MAGISK_VER_CODE" -lt 23000 ]; then + ui_print "*********************************************************" + ui_print "! Please install Magisk v23+" + abort "*********************************************************" + fi + elif [ "$FLAVOR" == "zygisk" ]; then + if [ "$MAGISK_VER_CODE" -lt 23010 ]; then + ui_print "*********************************************************" + ui_print "! Please install Magisk Lastest Canary (23010+)" + abort "*********************************************************" + fi + else ui_print "*********************************************************" - ui_print "! Please install Magisk v23+" + ui_print "! Unsupported flavor $FLAVOR" abort "*********************************************************" fi } diff --git a/core/src/main/cpp/main/api/riru_main.cpp b/core/src/main/cpp/main/api/riru_main.cpp index bc95c735..b51f3a40 100644 --- a/core/src/main/cpp/main/api/riru_main.cpp +++ b/core/src/main/cpp/main/api/riru_main.cpp @@ -25,6 +25,7 @@ #include "logging.h" #include "config.h" #include "context.h" +#include "symbol_cache.h" #define RIRU_MODULE #include @@ -40,6 +41,7 @@ namespace lspd { void onModuleLoaded() { LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode); + InitSymbolCache(nullptr); Context::GetInstance()->Init(); if constexpr (isDebug) { Context::GetInstance()->PreLoadDex("/system/" + kDexPath); diff --git a/core/src/main/cpp/main/api/zygisk_main.cpp b/core/src/main/cpp/main/api/zygisk_main.cpp index a9450300..4e4dc0ba 100644 --- a/core/src/main/cpp/main/api/zygisk_main.cpp +++ b/core/src/main/cpp/main/api/zygisk_main.cpp @@ -25,6 +25,7 @@ #include "logging.h" #include "context.h" #include "config.h" +#include "symbol_cache.h" namespace lspd { namespace { @@ -171,6 +172,107 @@ namespace lspd { int *allowUnload = &allow_unload; + class SharedMem { + inline static void *cutils = nullptr; + + inline static int (*ashmem_create_region)(const char *name, std::size_t size) = nullptr; + + inline static int (*ashmem_set_prot_region)(int fd, int prot) = nullptr; + + inline static bool init = false; + + static void Init() { + if (init) return; + cutils = dlopen("/system/lib" LP_SELECT("", "64") "/libcutils.so", 0); + ashmem_create_region = cutils ? reinterpret_cast( + dlsym(cutils, "ashmem_create_region")) : nullptr; + ashmem_set_prot_region = cutils ? reinterpret_cast( + dlsym(cutils, "ashmem_set_prot_region")) : nullptr; + init = true; + } + + int fd_ = -1; + std::size_t size_ = 0; + + class MappedMem { + void *addr_ = nullptr; + std::size_t size_ = 0; + + friend class SharedMem; + + MappedMem(int fd, std::size_t size, int prot, int flags, off_t offset) : addr_( + mmap(nullptr, size, prot, flags, fd, offset)), size_(size) { + if (addr_ == MAP_FAILED) { + PLOGE("failed to mmap"); + addr_ = nullptr; + size_ = 0; + } + } + + MappedMem(const MappedMem &) = delete; + + MappedMem &operator=(const MappedMem &other) = delete; + + public: + MappedMem(MappedMem &&other) : addr_(other.addr_), size_(other.size_) { + other.addr_ = nullptr; + other.size_ = 0; + } + + MappedMem &operator=(MappedMem &&other) { + new(this)MappedMem(std::move(other)); + return *this; + } + + constexpr operator bool() { return addr_; } + + ~MappedMem() { + if (addr_) { + munmap(addr_, size_); + } + } + + constexpr auto size() const { return size_; } + + constexpr auto get() const { return addr_; } + }; + + public: + MappedMem map(int prot, int flags, off_t offset) { + return {fd_, size_, prot, flags, offset}; + } + + constexpr bool ok() const { return fd_ > 0 && size_ > 0; } + + SharedMem(std::string_view name, std::size_t size) { + Init(); + if (ashmem_create_region && (fd_ = ashmem_create_region(name.data(), size)) > 0) { + size_ = size; + LOGD("using memfd"); + } else { + LOGD("using tmp file"); + auto *tmp = tmpfile(); + if (tmp) { + fd_ = fileno(tmp); + ftruncate(fd_, size); + size_ = size; + } + } + } + + void SetProt(int prot) { + ashmem_set_prot_region(fd_, prot); + } + + SharedMem() : fd_(-1), size_(0) { + Init(); + } + + constexpr auto get() const { return fd_; } + + constexpr auto size() const { return size_; } + }; + class ZygiskModule : public zygisk::ModuleBase { JNIEnv *env_; zygisk::Api *api_; @@ -193,6 +295,21 @@ namespace lspd { } else { LOGE("Failed to read dex fd"); } + if (int fd = -1, size = 0; (size = read_int(companion)) > 0 && + (fd = recv_fd(companion)) != -1) { + if (auto addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + addr && addr != MAP_FAILED) { + InitSymbolCache(reinterpret_cast(addr)); + msync(addr, size, MS_SYNC); + munmap(addr, size); + } else { + InitSymbolCache(nullptr); + } + close(fd); + } else { + LOGE("Failed to read symbol fd"); + InitSymbolCache(nullptr); + } close(companion); } @@ -217,50 +334,59 @@ namespace lspd { } }; - std::tuple InitCompanion() { + std::tuple InitCompanion() { LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: version v%s (%d)", versionName, versionCode); - int (*ashmem_create_region)(const char *name, std::size_t size) = nullptr; - int (*ashmem_set_prot_region)(int fd, int prot) = nullptr; - std::string path = "/data/adb/modules/"s + lspd::moduleName + "/" + kDexPath; - int fd = open(path.data(), O_RDONLY | O_CLOEXEC); - if (fd < 0) { - LOGE("Failed to load dex: %s", path.data()); - return {-1, 0}; + int dex_fd = open(path.data(), O_RDONLY | O_CLOEXEC); + if (dex_fd < 0) { + PLOGE("Failed to load dex: %s", path.data()); + return {{}, {}}; } - auto fsize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - auto *cutils = dlopen("/system/lib" LP_SELECT("", "64") "/libcutils.so", 0); - ashmem_create_region = cutils ? reinterpret_cast( - dlsym(cutils, "ashmem_create_region")) : nullptr; - ashmem_set_prot_region = cutils ? reinterpret_cast( - dlsym(cutils, "ashmem_set_prot_region")) : nullptr; - int tmp_fd = -1; - if (void *addr = nullptr; - ashmem_create_region && ashmem_set_prot_region && - (tmp_fd = ashmem_create_region("lspd.dex", fsize)) > 0 && - (addr = mmap(nullptr, fsize, PROT_WRITE, MAP_SHARED, tmp_fd, 0)) && - read(fd, addr, fsize) > 0 && munmap(addr, fsize) == 0) { - LOGD("using memfd"); - ashmem_set_prot_region(tmp_fd, PROT_READ); - std::swap(fd, tmp_fd); - } else { - LOGD("using raw fd"); + size_t dex_size = lseek(dex_fd, 0, SEEK_END); + lseek(dex_fd, 0, SEEK_SET); + + SharedMem dex{"lspd.dex", dex_size}; + SharedMem symbol{"symbol", sizeof(lspd::SymbolCache)}; + + if (!dex.ok() || !symbol.ok()) { + PLOGE("Failed to allocate shared mem"); + close(dex_fd); + return {{}, {}}; } - close(tmp_fd); - dlclose(cutils); - return {fd, fsize}; + if (auto dex_map = dex.map(PROT_WRITE, MAP_SHARED, 0); !dex_map || + read(dex_fd, dex_map.get(), + dex_map.size()) < 0) { + PLOGE("Failed to read dex %p", dex_map.get()); + close(dex_fd); + return {{}, {}}; + } + + dex.SetProt(PROT_READ); + + if (auto symbol_map = symbol.map(PROT_WRITE, MAP_SHARED, 0); symbol_map) { + memcpy(symbol_map.get(), lspd::symbol_cache.get(), symbol_map.size()); + } + + close(dex_fd); + return {std::move(dex), std::move(symbol)}; } void CompanionEntry(int client) { using namespace std::string_literals; - static auto[fd, size] = InitCompanion(); - if (fd > 0 && size > 0) { - write_int(client, size); - send_fd(client, fd); + static auto[dex, symbol] = InitCompanion(); + LOGD("Got dex with fd=%d size=%d; Got cache with fd=%d size=%d", dex.get(), + (int) dex.size(), + symbol.get(), (int) symbol.size()); + if (dex.ok()) { + write_int(client, dex.size()); + send_fd(client, dex.get()); + } else write_int(client, -1); + if (symbol.ok()) { + write_int(client, symbol.size()); + send_fd(client, symbol.get()); } else write_int(client, -1); close(client); } diff --git a/core/src/main/cpp/main/include/art/runtime/hidden_api.h b/core/src/main/cpp/main/include/art/runtime/hidden_api.h index fa3749f9..8d345d40 100644 --- a/core/src/main/cpp/main/include/art/runtime/hidden_api.h +++ b/core/src/main/cpp/main/include/art/runtime/hidden_api.h @@ -121,14 +121,14 @@ namespace art { if (api_level < __ANDROID_API_P__) { return; } - DexFile_setTrustedSym = reinterpret_cast(lspd::sym_setTrusted); - lspd::HookSymNoHandle(lspd::sym_openDexFileNative, DexFile_openDexFileNative); - lspd::HookSymNoHandle(lspd::sym_openInMemoryDexFilesNative, + DexFile_setTrustedSym = reinterpret_cast(lspd::symbol_cache->setTrusted); + lspd::HookSymNoHandle(lspd::symbol_cache->openDexFileNative, DexFile_openDexFileNative); + lspd::HookSymNoHandle(lspd::symbol_cache->openInMemoryDexFilesNative, DexFile_openInMemoryDexFilesNative); if (api_level == __ANDROID_API_P__) { - lspd::HookSymNoHandle(lspd::sym_createCookieWithArray, + lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithArray, DexFile_createCookieWithArray); - lspd::HookSymNoHandle(lspd::sym_createCookieWithDirectBuffer, + lspd::HookSymNoHandle(lspd::symbol_cache->createCookieWithDirectBuffer, DexFile_createCookieWithDirectBuffer); } }; diff --git a/core/src/main/cpp/main/src/context.cpp b/core/src/main/cpp/main/src/context.cpp index c2717eb7..454e36c2 100644 --- a/core/src/main/cpp/main/src/context.cpp +++ b/core/src/main/cpp/main/src/context.cpp @@ -121,7 +121,6 @@ namespace lspd { } void Context::Init() { - InitSymbolCache(); } void Context::Init(JNIEnv *env) { @@ -188,7 +187,10 @@ namespace lspd { void Context::OnNativeForkSystemServerPre(JNIEnv *env) { Service::instance()->InitService(env); - skip_ = !sym_initialized; + skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); + if (skip_) [[unlikely]] { + LOGW("skip system server due to symbol cache"); + } setAllowUnload(skip_); } @@ -204,6 +206,9 @@ namespace lspd { FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder); } else skip_ = true; } + if (skip_) [[unlikely]] { + LOGW("skipped system server"); + } setAllowUnload(skip_); } @@ -226,7 +231,7 @@ namespace lspd { Service::instance()->InitService(env); const auto app_id = uid % PER_USER_RANGE; JUTFString process_name(env, nice_name); - skip_ = !sym_initialized; + skip_ = !symbol_cache->initialized.test(std::memory_order_acquire); if (!skip_ && !app_data_dir) { LOGD("skip injecting into %s because it has no data dir", process_name.get()); skip_ = true; @@ -266,7 +271,7 @@ namespace lspd { } else { auto context = Context::ReleaseInstance(); auto service = Service::ReleaseInstance(); - art_img.reset(); + GetArt().reset(); LOGD("skipped %s", process_name.get()); setAllowUnload(true); } diff --git a/core/src/main/cpp/main/src/native_api.cpp b/core/src/main/cpp/main/src/native_api.cpp index 168680c5..e673f196 100644 --- a/core/src/main/cpp/main/src/native_api.cpp +++ b/core/src/main/cpp/main/src/native_api.cpp @@ -122,9 +122,9 @@ namespace lspd { }); bool InstallNativeAPI() { - LOGD("InstallNativeAPI: %p", sym_do_dlopen); - if (sym_do_dlopen) [[likely]] { - HookSymNoHandle(sym_do_dlopen, do_dlopen); + LOGD("InstallNativeAPI: %p", symbol_cache->do_dlopen); + if (symbol_cache->do_dlopen) [[likely]] { + HookSymNoHandle(symbol_cache->do_dlopen, do_dlopen); return true; } return false; diff --git a/core/src/main/cpp/main/src/native_hook.cpp b/core/src/main/cpp/main/src/native_hook.cpp index 7760fbcf..ac821132 100644 --- a/core/src/main/cpp/main/src/native_hook.cpp +++ b/core/src/main/cpp/main/src/native_hook.cpp @@ -47,7 +47,7 @@ namespace lspd { return; } LOGD("Start to install inline hooks"); - const auto &handle_libart = *art_img; + const auto &handle_libart = *GetArt(); if (!handle_libart.isValid()) { LOGE("Failed to fetch libart.so"); } @@ -62,7 +62,7 @@ namespace lspd { art::thread_list::ScopedSuspendAll::Setup(handle_libart); art::gc::ScopedGCCriticalSection::Setup(handle_libart); art::jit::jit_code_cache::Setup(handle_libart); - art_img.reset(); + GetArt().reset(); LOGD("Inline hooks installed"); } } // namespace lspd diff --git a/core/src/main/cpp/main/src/service.cpp b/core/src/main/cpp/main/src/service.cpp index cfe99d73..c7883b17 100644 --- a/core/src/main/cpp/main/src/service.cpp +++ b/core/src/main/cpp/main/src/service.cpp @@ -171,7 +171,7 @@ namespace lspd { auto binder_class = JNI_FindClass(env, "android/os/Binder"); exec_transact_backup_methodID_ = JNI_GetMethodID(env, binder_class, "execTransact", "(IJJI)Z"); - if (!sym_set_table_override) { + if (!symbol_cache->setTableOverride) { LOGE("set table override not found"); } memcpy(&native_interface_replace_, env->functions, sizeof(JNINativeInterface)); @@ -179,8 +179,8 @@ namespace lspd { call_boolean_method_va_backup_ = env->functions->CallBooleanMethodV; native_interface_replace_.CallBooleanMethodV = &call_boolean_method_va_replace; - if (sym_set_table_override != nullptr) { - reinterpret_cast(sym_set_table_override)( + if (symbol_cache->setTableOverride != nullptr) { + reinterpret_cast(symbol_cache->setTableOverride)( &native_interface_replace_); } if (auto activity_thread_class = JNI_FindClass(env, "android/app/IActivityManager$Stub")) { diff --git a/core/src/main/cpp/main/src/symbol_cache.cpp b/core/src/main/cpp/main/src/symbol_cache.cpp index e2b402fd..423d386d 100644 --- a/core/src/main/cpp/main/src/symbol_cache.cpp +++ b/core/src/main/cpp/main/src/symbol_cache.cpp @@ -31,48 +31,60 @@ #include namespace lspd { - bool sym_initialized = false; - void *sym_do_dlopen = nullptr; - void *sym_openInMemoryDexFilesNative = nullptr; - void *sym_createCookieWithArray = nullptr; - void *sym_createCookieWithDirectBuffer = nullptr; - void *sym_openDexFileNative = nullptr; - void *sym_setTrusted = nullptr; - void *sym_set_table_override = nullptr; - std::unique_ptr art_img = nullptr; + std::unique_ptr symbol_cache = std::make_unique(); + + std::unique_ptr &GetArt() { + static std::unique_ptr kArtImg = nullptr; + if (!kArtImg) { + kArtImg = std::make_unique(kLibArtName); + } + return kArtImg; + } + bool FindLibArt() { - art_img = std::make_unique(kLibArtName); - if (!art_img->isValid()) return false; + auto &art = GetArt(); + if (!art->isValid()) return false; auto api_level = GetAndroidApiLevel(); - return (sym_set_table_override = art_img->getSymbAddress( + return (symbol_cache->setTableOverride = art->getSymbAddress( "_ZN3art9JNIEnvExt16SetTableOverrideEPK18JNINativeInterface")) != nullptr && (api_level < __ANDROID_API_P__ || ( - (sym_openDexFileNative = art_img->getSymbAddress( + (symbol_cache->openDexFileNative = art->getSymbAddress( "_ZN3artL25DexFile_openDexFileNativeEP7_JNIEnvP7_jclassP8_jstringS5_iP8_jobjectP13_jobjectArray")) && ( - (sym_openInMemoryDexFilesNative = art_img->getSymbAddress( + (symbol_cache->openInMemoryDexFilesNative = art->getSymbAddress( "_ZN3artL34DexFile_openInMemoryDexFilesNativeEP7_JNIEnvP7_jclassP13_jobjectArrayS5_P10_jintArrayS7_P8_jobjectS5_")) || ( - (sym_createCookieWithArray = art_img->getSymbAddress( + (symbol_cache->createCookieWithArray = art->getSymbAddress( "_ZN3artL29DexFile_createCookieWithArrayEP7_JNIEnvP7_jclassP11_jbyteArrayii")) && - (sym_createCookieWithDirectBuffer = art_img->getSymbAddress( + (symbol_cache->createCookieWithDirectBuffer = art->getSymbAddress( "_ZN3artL36DexFile_createCookieWithDirectBufferEP7_JNIEnvP7_jclassP8_jobjectii")) ) ) && - (sym_setTrusted = art_img->getSymbAddress( + (symbol_cache->setTrusted = art->getSymbAddress( "_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject")))); } - void InitSymbolCache() { + void InitSymbolCache(SymbolCache *other) { LOGD("InitSymbolCache"); - sym_initialized = FindLibArt(); - sym_do_dlopen = SandHook::ElfImg("/linker").getSymbAddress( + if (other && other->initialized.test(std::memory_order_acquire)) { + LOGD("Already initialized"); + *symbol_cache = *other; + symbol_cache->initialized.test_and_set(std::memory_order_relaxed); + return; + } + auto ok = FindLibArt(); + symbol_cache->do_dlopen = SandHook::ElfImg("/linker").getSymbAddress( "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv"); - if (!sym_initialized) [[unlikely]] { - sym_initialized = false; - art_img.reset(); + if (!ok) [[unlikely]] { + GetArt().reset(); LOGE("Init symbol cache failed"); + } else { + symbol_cache->initialized.test_and_set(std::memory_order_relaxed); + if (other) { + *other = *symbol_cache; + other->initialized.test_and_set(std::memory_order_acq_rel); + } } } } // namespace lspd diff --git a/core/src/main/cpp/main/src/symbol_cache.h b/core/src/main/cpp/main/src/symbol_cache.h index d3ed209e..66493556 100644 --- a/core/src/main/cpp/main/src/symbol_cache.h +++ b/core/src/main/cpp/main/src/symbol_cache.h @@ -26,22 +26,44 @@ #define LSPOSED_SYMBOL_CACHE_H #include + namespace SandHook { class ElfImg; } namespace lspd { - extern bool sym_initialized; - extern std::unique_ptr art_img; - extern void *sym_do_dlopen; - extern void *sym_openInMemoryDexFilesNative; - extern void *sym_createCookieWithArray; - extern void *sym_createCookieWithDirectBuffer; - extern void *sym_openDexFileNative; - extern void *sym_setTrusted; - extern void *sym_set_table_override; + struct SymbolCache { + std::atomic_flag initialized{}; + void *do_dlopen; + void *openInMemoryDexFilesNative; + void *createCookieWithArray; + void *createCookieWithDirectBuffer; + void *openDexFileNative; + void *setTrusted; + void *setTableOverride; - void InitSymbolCache(); + SymbolCache() = default; + + SymbolCache(const SymbolCache &other) : + do_dlopen(other.do_dlopen), + openInMemoryDexFilesNative(other.openInMemoryDexFilesNative), + createCookieWithArray(other.createCookieWithArray), + createCookieWithDirectBuffer(other.createCookieWithDirectBuffer), + openDexFileNative(other.openDexFileNative), + setTrusted(other.setTrusted), + setTableOverride(other.setTableOverride) {} + + SymbolCache &operator=(const SymbolCache &other) { + new(this)SymbolCache(other); + return *this; + } + }; + + extern std::unique_ptr symbol_cache; + + void InitSymbolCache(SymbolCache *other); + + std::unique_ptr &GetArt(); } #endif //LSPOSED_SYMBOL_CACHE_H