diff --git a/lsplant/src/main/jni/art/runtime/art_method.hpp b/lsplant/src/main/jni/art/runtime/art_method.hpp index d6a0fc2..45d74d6 100644 --- a/lsplant/src/main/jni/art/runtime/art_method.hpp +++ b/lsplant/src/main/jni/art/runtime/art_method.hpp @@ -250,7 +250,7 @@ public: if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0; if (!RETRIEVE_FUNC_SYMBOL(GetMethodShorty, - "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID") && + "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID", true) && !RETRIEVE_FUNC_SYMBOL(GetMethodShorty, "_ZN3art15GetMethodShortyEP7_JNIEnvP10_jmethodID")) { LOGE("Failed to find GetMethodShorty"); diff --git a/lsplant/src/main/jni/art/runtime/dex_file.hpp b/lsplant/src/main/jni/art/runtime/dex_file.hpp index 514b6a6..0ca3858 100644 --- a/lsplant/src/main/jni/art/runtime/dex_file.hpp +++ b/lsplant/src/main/jni/art/runtime/dex_file.hpp @@ -103,9 +103,9 @@ public: static bool Init(JNIEnv* env, const HookHandler& handler) { auto sdk_int = GetAndroidApiLevel(); if (sdk_int >= __ANDROID_API_P__) [[likely]] { - if (!RETRIEVE_FUNC_SYMBOL( - DexFile_setTrusted, - "_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject")) { + if (!RETRIEVE_FUNC_SYMBOL(DexFile_setTrusted, + "_ZN3artL18DexFile_setTrustedEP7_JNIEnvP7_jclassP8_jobject", + true)) { return false; } } diff --git a/lsplant/src/main/jni/include/lsplant.hpp b/lsplant/src/main/jni/include/lsplant.hpp index 22e6ffc..23801f6 100644 --- a/lsplant/src/main/jni/include/lsplant.hpp +++ b/lsplant/src/main/jni/include/lsplant.hpp @@ -7,7 +7,7 @@ /// \namespace namespace of LSPlant namespace lsplant { -inline namespace v1 { +inline namespace v2 { /// \struct InitInfo /// \brief Information and configuration that are needed to call #Init() struct InitInfo { @@ -31,6 +31,14 @@ struct InitInfo { /// \note It should be able to resolve symbols from both .dynsym and .symtab. using ArtSymbolResolver = std::function; + /// \brief Type of prefix symbol resolver to \p libart.so. + /// In \ref std::function form so that user can use lambda expression with capture list.
+ /// \p symbol_prefix is the symbol prefix that needs to retrieve.
+ /// \p return is the first absolute address in the memory that points to the target symbol. + /// It should be null if the symbol cannot be found.
+ /// \note It should be able to resolve symbols from both .dynsym and .symtab. + using ArtSymbolPrefixResolver = std::function; + /// \brief The inline hooker function. Must not be null. InlineHookFunType inline_hooker; /// \brief The inline unhooker function. Must not be null. @@ -38,6 +46,9 @@ struct InitInfo { /// \brief The symbol resolver to \p libart.so. Must not be null. ArtSymbolResolver art_symbol_resolver; + /// \brief The symbol prefix resolver to \p libart.so. May be null. + ArtSymbolPrefixResolver art_symbol_prefix_resolver; + /// \brief The generated class name. Must not be empty. It contains a field and a method /// and they could be set by \p generated_field_name and \p generated_method_name respectively. std::string_view generated_class_name = "LSPHooker_"; diff --git a/lsplant/src/main/jni/include/utils/hook_helper.hpp b/lsplant/src/main/jni/include/utils/hook_helper.hpp index 41a8dae..d2c065a 100644 --- a/lsplant/src/main/jni/include/utils/hook_helper.hpp +++ b/lsplant/src/main/jni/include/utils/hook_helper.hpp @@ -77,8 +77,13 @@ inline constexpr auto operator+(const tstring &, const std::string &b) { return a + b; } -inline void *Dlsym(const HookHandler &handle, const char *name) { - return handle.art_symbol_resolver(name); +inline void *Dlsym(const HookHandler &handle, const char *name, bool match_prefix = false) { + if (auto match = handle.art_symbol_resolver(name); match) { + return match; + } else if (match_prefix && handle.art_symbol_prefix_resolver) { + return handle.art_symbol_prefix_resolver(name); + } + return nullptr; } template diff --git a/lsplant/src/main/jni/lsplant.cc b/lsplant/src/main/jni/lsplant.cc index f124254..6aafda2 100644 --- a/lsplant/src/main/jni/lsplant.cc +++ b/lsplant/src/main/jni/lsplant.cc @@ -527,7 +527,7 @@ bool DoUnHook(ArtMethod *target, ArtMethod *backup) { } // namespace -inline namespace v1 { +inline namespace v2 { using ::lsplant::IsHooked; @@ -712,7 +712,7 @@ using ::lsplant::IsHooked; if (!cookie) return false; return DexFile::SetTrusted(env, cookie); } -} // namespace v1 +} // namespace v2 } // namespace lsplant diff --git a/settings.gradle.kts b/settings.gradle.kts index f7d0132..b9e2a1d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,8 +5,8 @@ pluginManagement { mavenCentral() } plugins { - id("com.android.application") version "7.1.2" - id("com.android.library") version "7.1.2" + id("com.android.application") version "7.1.3" + id("com.android.library") version "7.1.3" } } dependencyResolutionManagement { diff --git a/test/src/main/jni/elf_util.cpp b/test/src/main/jni/elf_util.cpp index 903fb15..4870aed 100644 --- a/test/src/main/jni/elf_util.cpp +++ b/test/src/main/jni/elf_util.cpp @@ -166,9 +166,8 @@ ElfW(Addr) ElfImg::GnuLookup(std::string_view name, uint32_t hash) const { return 0; } -ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { +void ElfImg::MayInitLinearMap() const { if (symtabs_.empty()) { - symtabs_.reserve(symtab_count); if (symtab_start != nullptr && symstr_offset_for_symtab != 0) { for (ElfW(Off) i = 0; i < symtab_count; i++) { unsigned int st_type = ELF_ST_TYPE(symtab_start[i].st_info); @@ -180,6 +179,11 @@ ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { } } } +} + + +ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { + MayInitLinearMap(); if (auto i = symtabs_.find(name); i != symtabs_.end()) { return i->second->st_value; } else { @@ -187,6 +191,16 @@ ElfW(Addr) ElfImg::LinearLookup(std::string_view name) const { } } +ElfW(Addr) ElfImg::PrefixLookupFirst(std::string_view prefix) const { + MayInitLinearMap(); + if (auto i = symtabs_.lower_bound(prefix); i != symtabs_.end() && i->first.starts_with(prefix)) { + LOGD("found prefix %s of %s %p in %s in symtab by linear lookup", prefix.data(), + i->first.data(), reinterpret_cast(i->second->st_value), elf.data()); + return i->second->st_value; + } else { + return 0; + } +} ElfImg::~ElfImg() { //open elf file local diff --git a/test/src/main/jni/elf_util.h b/test/src/main/jni/elf_util.h index 7baa72f..edf96f8 100644 --- a/test/src/main/jni/elf_util.h +++ b/test/src/main/jni/elf_util.h @@ -21,7 +21,7 @@ #define SANDHOOK_ELF_UTIL_H #include -#include +#include #include #include #include @@ -35,23 +35,26 @@ namespace SandHook { ElfImg(std::string_view elf); - constexpr ElfW(Addr) getSymbOffset(std::string_view name) const { - return getSymbOffset(name, GnuHash(name), ElfHash(name)); - } - - constexpr ElfW(Addr) getSymbAddress(std::string_view name) const { - ElfW(Addr) offset = getSymbOffset(name); + template + requires(std::is_pointer_v) + constexpr const T getSymbAddress(std::string_view name) const { + auto offset = getSymbOffset(name, GnuHash(name), ElfHash(name)); if (offset > 0 && base != nullptr) { - return static_cast((uintptr_t) base + offset - bias); + return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); } else { - return 0; + return nullptr; } } - template + template requires(std::is_pointer_v) - constexpr T getSymbAddress(std::string_view name) const { - return reinterpret_cast(getSymbAddress(name)); + constexpr const T getSymbPrefixFirstOffset(std::string_view prefix) const { + auto offset = PrefixLookupFirst(prefix); + if (offset > 0 && base != nullptr) { + return reinterpret_cast(static_cast((uintptr_t) base + offset - bias)); + } else { + return nullptr; + } } bool isValid() const { @@ -73,12 +76,16 @@ namespace SandHook { ElfW(Addr) LinearLookup(std::string_view name) const; + ElfW(Addr) PrefixLookupFirst(std::string_view prefix) const; + constexpr static uint32_t ElfHash(std::string_view name); constexpr static uint32_t GnuHash(std::string_view name); bool findModuleBase(); + void MayInitLinearMap() const; + std::string elf; void *base = nullptr; char *buffer = nullptr; @@ -111,7 +118,7 @@ namespace SandHook { uint32_t *gnu_bucket_; uint32_t *gnu_chain_; - mutable std::unordered_map symtabs_; + mutable std::map symtabs_; }; constexpr uint32_t ElfImg::ElfHash(std::string_view name) { diff --git a/test/src/main/jni/test.cpp b/test/src/main/jni/test.cpp index 3841085..4e14a8d 100644 --- a/test/src/main/jni/test.cpp +++ b/test/src/main/jni/test.cpp @@ -60,9 +60,11 @@ JNI_OnLoad(JavaVM* vm, void* reserved) { .inline_hooker = InlineHooker, .inline_unhooker = InlineUnhooker, .art_symbol_resolver = [&art](std::string_view symbol) -> void* { - auto* out = reinterpret_cast(art.getSymbAddress(symbol)); - return out; - } + return art.getSymbAddress(symbol); + }, + .art_symbol_prefix_resolver = [&art](auto symbol) { + return art.getSymbPrefixFirstOffset(symbol); + }, }; init_result = lsplant::Init(env, initInfo); return JNI_VERSION_1_6;