From 534e3930ae517c0a714c73cbd7883f14f898b7b4 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Mon, 23 Sep 2024 08:36:53 +0200 Subject: [PATCH] Use PLT hook from Zygisk API(v4) --- core/src/main/jni/include/native_util.h | 53 -------- magisk-loader/src/main/jni/api/zygisk.hpp | 1 + .../src/main/jni/api/zygisk_main.cpp | 92 ++++++------- .../src/main/jni/src/magisk_loader.cpp | 127 ++++++++++++++---- .../src/main/jni/src/magisk_loader.h | 76 ++++++++--- 5 files changed, 206 insertions(+), 143 deletions(-) diff --git a/core/src/main/jni/include/native_util.h b/core/src/main/jni/include/native_util.h index 2e669d79..5a879a97 100644 --- a/core/src/main/jni/include/native_util.h +++ b/core/src/main/jni/include/native_util.h @@ -76,59 +76,6 @@ inline bool RegisterNativeMethodsInternal(JNIEnv *env, std::string_view class_na RegisterNativeMethodsInternal(env, GetNativeBridgeSignature() + #class_name, gMethods, \ arraysize(gMethods)) -static dev_t dev = 0; -static ino_t inode = 0; -static std::vector> plt_hook_saved = {}; - -inline int HookPLT(void *art_symbol, void *callback, void **backup, bool save = true) { - auto symbol = reinterpret_cast(art_symbol); - - if (GetArt()->isStripped()) { - if (dev == 0 || inode == 0) { - auto libart_path = GetArt()->name(); - for (auto map : lsplt::MapInfo::Scan()) { - if (map.path == libart_path) { - inode = map.inode; - dev = map.dev; - break; - } - } - } - - auto result = - lsplt::RegisterHook(dev, inode, symbol, callback, backup) && lsplt::CommitHook(); - if (result && *backup != nullptr) { - if (save) plt_hook_saved.emplace_back(symbol, backup); - return 0; - } - } - - if (auto addr = GetArt()->getSymbAddress(symbol); addr) { - HookInline(addr, callback, backup); - } else if (*backup == nullptr && isDebug) { - LOGW("Failed to {} Art symbol {}", save ? "hook" : "unhook", symbol); - } - return *backup == nullptr; -} - -inline int UnhookPLT(void *original) { - Dl_info info; - - if (!dladdr(original, &info) || info.dli_sname != nullptr) return 1; - if (!GetArt()->isStripped()) return UnhookInline(original); - - auto hook_iter = - std::find_if(plt_hook_saved.begin(), plt_hook_saved.end(), - [info](auto record) { return strcmp(record.first, info.dli_sname) == 0; }); - void *stub = nullptr; - if (hook_iter != plt_hook_saved.end() && - HookPLT(original, *(hook_iter->second), &stub, false)) { - plt_hook_saved.erase(hook_iter); - return 0; - } - return 1; -} - inline std::string GetNativeBridgeSignature() { const auto &obfs_map = ConfigBridge::GetInstance()->obfuscation_map(); static auto signature = obfs_map.at("org.lsposed.lspd.nativebridge."); diff --git a/magisk-loader/src/main/jni/api/zygisk.hpp b/magisk-loader/src/main/jni/api/zygisk.hpp index 7c861adc..6bd6c6f6 100644 --- a/magisk-loader/src/main/jni/api/zygisk.hpp +++ b/magisk-loader/src/main/jni/api/zygisk.hpp @@ -18,6 +18,7 @@ #pragma once #include +#include #define ZYGISK_API_VERSION 4 diff --git a/magisk-loader/src/main/jni/api/zygisk_main.cpp b/magisk-loader/src/main/jni/api/zygisk_main.cpp index 786aecf1..39ca59ec 100644 --- a/magisk-loader/src/main/jni/api/zygisk_main.cpp +++ b/magisk-loader/src/main/jni/api/zygisk_main.cpp @@ -17,62 +17,62 @@ * Copyright (C) 2021 - 2022 LSPosed Contributors */ -#include -#include #include +#include #include +#include -#include "zygisk.hpp" -#include "logging.h" -#include "loader.h" #include "config_impl.h" +#include "loader.h" +#include "logging.h" #include "magisk_loader.h" #include "symbol_cache.h" +#include "zygisk.hpp" namespace lspd { - int allow_unload = 0; - int *allowUnload = &allow_unload; +int allow_unload = 0; +int *allowUnload = &allow_unload; - class ZygiskModule : public zygisk::ModuleBase { - JNIEnv *env_; - zygisk::Api *api_; +class ZygiskModule : public zygisk::ModuleBase { + JNIEnv *env_; + zygisk::Api *api_; - void onLoad(zygisk::Api *api, JNIEnv *env) override { - env_ = env; - api_ = api; - MagiskLoader::Init(); - ConfigImpl::Init(); + void onLoad(zygisk::Api *api, JNIEnv *env) override { + env_ = env; + api_ = api; + MagiskLoader::Init(api); + ConfigImpl::Init(); + } + + void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre( + env_, args->uid, args->gids, args->nice_name, + args->is_child_zygote ? *args->is_child_zygote : false, args->app_data_dir); + } + + void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { + MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name, + args->app_data_dir); + if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + } + + void preServerSpecialize([[maybe_unused]] zygisk::ServerSpecializeArgs *args) override { + MagiskLoader::GetInstance()->OnNativeForkSystemServerPre(env_); + } + + void postServerSpecialize([[maybe_unused]] const zygisk::ServerSpecializeArgs *args) override { + if (__system_property_find("ro.vendor.product.ztename")) { + auto *process = env_->FindClass("android/os/Process"); + auto *set_argv0 = env_->GetStaticMethodID(process, "setArgV0", "(Ljava/lang/String;)V"); + auto *name = env_->NewStringUTF("system_server"); + env_->CallStaticVoidMethod(process, set_argv0, name); + env_->DeleteLocalRef(name); + env_->DeleteLocalRef(process); } - - void preAppSpecialize(zygisk::AppSpecializeArgs *args) override { - MagiskLoader::GetInstance()->OnNativeForkAndSpecializePre( - env_, args->uid, args->gids, args->nice_name, - args->is_child_zygote ? *args->is_child_zygote : false, args->app_data_dir); - } - - void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { - MagiskLoader::GetInstance()->OnNativeForkAndSpecializePost(env_, args->nice_name, args->app_data_dir); - if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - } - - void preServerSpecialize([[maybe_unused]] zygisk::ServerSpecializeArgs *args) override { - MagiskLoader::GetInstance()->OnNativeForkSystemServerPre(env_); - } - - void postServerSpecialize([[maybe_unused]] const zygisk::ServerSpecializeArgs *args) override { - if (__system_property_find("ro.vendor.product.ztename")) { - auto *process = env_->FindClass("android/os/Process"); - auto *set_argv0 = env_->GetStaticMethodID(process, "setArgV0", - "(Ljava/lang/String;)V"); - auto *name = env_->NewStringUTF("system_server"); - env_->CallStaticVoidMethod(process, set_argv0, name); - env_->DeleteLocalRef(name); - env_->DeleteLocalRef(process); - } - MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env_); - if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); - } - }; -} //namespace lspd + MagiskLoader::GetInstance()->OnNativeForkSystemServerPost(env_); + if (*allowUnload) api_->setOption(zygisk::DLCLOSE_MODULE_LIBRARY); + } +}; +} // namespace lspd REGISTER_ZYGISK_MODULE(lspd::ZygiskModule); diff --git a/magisk-loader/src/main/jni/src/magisk_loader.cpp b/magisk-loader/src/main/jni/src/magisk_loader.cpp index 13c1c221..2b32966a 100644 --- a/magisk-loader/src/main/jni/src/magisk_loader.cpp +++ b/magisk-loader/src/main/jni/src/magisk_loader.cpp @@ -23,11 +23,15 @@ #include #include #include +#include +#include +#include + +#include "../src/native_api.h" #include "config_impl.h" #include "elf_util.h" #include "loader.h" -#include "native_util.h" #include "service.h" #include "symbol_cache.h" #include "utils/jni_helper.hpp" @@ -50,6 +54,105 @@ constexpr int PER_USER_RANGE = 100000; static constexpr uid_t kAidInjected = INJECTED_AID; static constexpr uid_t kAidInet = 3003; +std::vector MapInfo::Scan(std::string_view pid) { + constexpr static auto kPermLength = 5; + constexpr static auto kMapEntry = 7; + std::vector info; + auto path = "/proc/" + std::string{pid} + "/maps"; + auto maps = std::unique_ptr{fopen(path.c_str(), "r"), &fclose}; + if (maps) { + char *line = nullptr; + size_t len = 0; + ssize_t read; + while ((read = getline(&line, &len, maps.get())) > 0) { + line[read - 1] = '\0'; + uintptr_t start = 0; + uintptr_t end = 0; + uintptr_t off = 0; + ino_t inode = 0; + unsigned int dev_major = 0; + unsigned int dev_minor = 0; + std::array perm{'\0'}; + int path_off; + if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n%*s", &start, + &end, perm.data(), &off, &dev_major, &dev_minor, &inode, + &path_off) != kMapEntry) { + continue; + } + while (path_off < read && isspace(line[path_off])) path_off++; + auto &ref = info.emplace_back(MapInfo{start, end, 0, perm[3] == 'p', off, + static_cast(makedev(dev_major, dev_minor)), + inode, line + path_off}); + if (perm[0] == 'r') ref.perms |= PROT_READ; + if (perm[1] == 'w') ref.perms |= PROT_WRITE; + if (perm[2] == 'x') ref.perms |= PROT_EXEC; + } + free(line); + } + return info; +} + +void MagiskLoader::InitializeZygiskApi(zygisk::Api *api) { + std::vector> plt_hook_saved = {}; + + const std::string libArtPath = GetArt()->name(); + const auto maps = MapInfo::Scan(); + const auto libArtMap = std::find_if(maps.begin(), maps.end(), + [libArtPath](auto it) { return it.path == libArtPath; }); + const dev_t dev = libArtMap->inode; + const ino_t inode = libArtMap->dev; + + auto HookPLT = [dev, inode, &plt_hook_saved, api](void *art_symbol, void *callback, + void **backup, bool save = true) { + auto symbol = reinterpret_cast(art_symbol); + + if (GetArt()->isStripped()) { + api->pltHookRegister(dev, inode, symbol, callback, backup); + if (api->pltHookCommit() && *backup != nullptr) { + if (save) plt_hook_saved.emplace_back(symbol, backup); + return 0; + } + } + + if (auto addr = GetArt()->getSymbAddress(symbol); addr) { + HookInline(addr, callback, backup); + } else if (*backup == nullptr && isDebug) { + LOGW("Failed to {} Art symbol {}", save ? "hook" : "unhook", symbol); + } + return (int)(*backup == nullptr); + }; + + auto UnhookPLT = [HookPLT, &plt_hook_saved](void *original) { + Dl_info info; + + if (!dladdr(original, &info) || info.dli_sname != nullptr) return 1; + if (!GetArt()->isStripped()) return UnhookInline(original); + + auto hook_iter = + std::find_if(plt_hook_saved.begin(), plt_hook_saved.end(), + [info](auto record) { return strcmp(record.first, info.dli_sname) == 0; }); + void *stub = nullptr; + if (hook_iter != plt_hook_saved.end() && + HookPLT(original, *(hook_iter->second), &stub, false)) { + plt_hook_saved.erase(hook_iter); + return 0; + } + return 1; + }; + + initInfo = lsplant::InitInfo{ + .inline_hooker = + [HookPLT](auto t, auto r) { + void *bk = nullptr; + return HookPLT(t, r, &bk) == 0 ? bk : nullptr; + }, + .inline_unhooker = [UnhookPLT](auto t) { return UnhookPLT(t) == 0; }, + .art_symbol_resolver = [](auto symbol) { return GetArt()->getSymbAddress(symbol); }, + .art_symbol_prefix_resolver = + [](auto symbol) { return GetArt()->getSymbPrefixFirstAddress(symbol); }, + .is_plt_hook = true}; +} + void MagiskLoader::LoadDex(JNIEnv *env, PreloadedDex &&dex) { auto classloader = JNI_FindClass(env, "java/lang/ClassLoader"); auto getsyscl_mid = JNI_GetStaticMethodID(env, classloader, "getSystemClassLoader", @@ -115,17 +218,6 @@ void MagiskLoader::OnNativeForkSystemServerPost(JNIEnv *env) { instance->HookBridge(*this, env); // always inject into system server - lsplant::InitInfo initInfo{ - .inline_hooker = - [](auto t, auto r) { - void *bk = nullptr; - return HookPLT(t, r, &bk) == 0 ? bk : nullptr; - }, - .inline_unhooker = [](auto t) { return UnhookPLT(t) == 0; }, - .art_symbol_resolver = [](auto symbol) { return GetArt()->getSymbAddress(symbol); }, - .art_symbol_prefix_resolver = - [](auto symbol) { return GetArt()->getSymbPrefixFirstAddress(symbol); }, - .is_plt_hook = true}; InitArtHooker(env, initInfo); InitHooks(env); SetupEntryClass(env); @@ -187,17 +279,6 @@ void MagiskLoader::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, auto binder = skip_ ? ScopedLocalRef{env, nullptr} : instance->RequestBinder(env, nice_name); if (binder) { - lsplant::InitInfo initInfo{ - .inline_hooker = - [](auto t, auto r) { - void *bk = nullptr; - return HookPLT(t, r, &bk) == 0 ? bk : nullptr; - }, - .inline_unhooker = [](auto t) { return UnhookPLT(t) == 0; }, - .art_symbol_resolver = [](auto symbol) { return GetArt()->getSymbAddress(symbol); }, - .art_symbol_prefix_resolver = - [](auto symbol) { return GetArt()->getSymbPrefixFirstAddress(symbol); }, - .is_plt_hook = true}; auto [dex_fd, size] = instance->RequestLSPDex(env, binder); auto obfs_map = instance->RequestObfuscationMap(env, binder); ConfigBridge::GetInstance()->obfuscation_map(std::move(obfs_map)); diff --git a/magisk-loader/src/main/jni/src/magisk_loader.h b/magisk-loader/src/main/jni/src/magisk_loader.h index 51600f37..2c8b9cff 100644 --- a/magisk-loader/src/main/jni/src/magisk_loader.h +++ b/magisk-loader/src/main/jni/src/magisk_loader.h @@ -23,36 +23,70 @@ #pragma once +#include "../api/zygisk.hpp" #include "context.h" namespace lspd { - class MagiskLoader : public Context { - public: - inline static void Init() { - instance_ = std::make_unique(); - } +class MagiskLoader : public Context { +public: + inline static void Init(zygisk::Api *api) { + instance_ = std::make_unique(); + GetInstance()->InitializeZygiskApi(api); + } - inline static MagiskLoader *GetInstance() { - return static_cast(instance_.get()); - } + inline static MagiskLoader *GetInstance() { + return static_cast(instance_.get()); + } - void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring &nice_name, - jboolean is_child_zygote, jstring app_data_dir); + void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring &nice_name, + jboolean is_child_zygote, jstring app_data_dir); - void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir); + void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir); - void OnNativeForkSystemServerPost(JNIEnv *env); + void OnNativeForkSystemServerPost(JNIEnv *env); - void OnNativeForkSystemServerPre(JNIEnv *env); + void OnNativeForkSystemServerPre(JNIEnv *env); - protected: - void LoadDex(JNIEnv *env, PreloadedDex &&dex) override; +protected: + void LoadDex(JNIEnv *env, PreloadedDex &&dex) override; - void SetupEntryClass(JNIEnv *env) override; + void SetupEntryClass(JNIEnv *env) override; - private: - bool skip_ = false; +private: + bool skip_ = false; + lsplant::InitInfo initInfo; - static void setAllowUnload(bool unload); - }; -} // namespace lspd + void InitializeZygiskApi(zygisk::Api *api); + static void setAllowUnload(bool unload); +}; + +struct MapInfo { + /// \brief The start address of the memory region. + uintptr_t start; + /// \brief The end address of the memory region. + uintptr_t end; + /// \brief The permissions of the memory region. This is a bit mask of the following values: + /// - PROT_READ + /// - PROT_WRITE + /// - PROT_EXEC + uint8_t perms; + /// \brief Whether the memory region is private. + bool is_private; + /// \brief The offset of the memory region. + uintptr_t offset; + /// \brief The device number of the memory region. + /// Major can be obtained by #major() + /// Minor can be obtained by #minor() + dev_t dev; + /// \brief The inode number of the memory region. + ino_t inode; + /// \brief The path of the memory region. + std::string path; + + /// \brief Scans /proc/self/maps and returns a list of \ref MapInfo entries. + /// This is useful to find out the inode of the library to hook. + /// \param[in] pid The process id to scan. This is "self" by default. + /// \return A list of \ref MapInfo entries. + static std::vector Scan(std::string_view pid = "self"); +}; +} // namespace lspd