[core] native api (#108)

* [core] Propose the LSPosed native API

* update native api specification

* add symbol cache

* [core] Native API implementation

* Fix typo

* Allow modules to load their own so

* bug fixes

* Fix crash in InstallRiruHooks

* Ignore failed dlopen
This commit is contained in:
双草酸酯 2021-02-09 20:36:38 +08:00 committed by GitHub
parent a4e69792d5
commit c9b73630ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 299 additions and 67 deletions

View File

@ -22,7 +22,7 @@ namespace art {
return PrettyMethod(thiz, true); return PrettyMethod(thiz, true);
} }
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
LOGD("art_method hook setup, handle=%p", handle); LOGD("art_method hook setup, handle=%p", handle);
RETRIEVE_MEM_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEb"); RETRIEVE_MEM_FUNC_SYMBOL(PrettyMethod, "_ZN3art9ArtMethod12PrettyMethodEb");
} }

View File

@ -79,7 +79,7 @@ namespace art {
} }
// @ApiSensitive(Level.MIDDLE) // @ApiSensitive(Level.MIDDLE)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
LOGD("Classlinker hook setup, handle=%p", handle); LOGD("Classlinker hook setup, handle=%p", handle);
int api_level = lspd::GetAndroidApiLevel(); int api_level = lspd::GetAndroidApiLevel();
size_t OFFSET_classlinker; // Get offset from art::Runtime::RunRootClinits() call in IDA size_t OFFSET_classlinker; // Get offset from art::Runtime::RunRootClinits() call in IDA
@ -128,7 +128,7 @@ namespace art {
RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter, RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter,
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"); "_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE");
lspd::HookSyms(handle, hook_func, ShouldUseInterpreterEntrypoint); lspd::HookSyms(handle, ShouldUseInterpreterEntrypoint);
if (api_level >= __ANDROID_API_R__) { if (api_level >= __ANDROID_API_R__) {
// In android R, FixupStaticTrampolines won't be called unless it's marking it as // In android R, FixupStaticTrampolines won't be called unless it's marking it as
@ -136,9 +136,9 @@ namespace art {
// So we miss some calls between initialized and visiblyInitialized. // So we miss some calls between initialized and visiblyInitialized.
// Therefore we hook the new introduced MarkClassInitialized instead // Therefore we hook the new introduced MarkClassInitialized instead
// This only happens on non-x86 devices // This only happens on non-x86 devices
lspd::HookSyms(handle, hook_func, MarkClassInitialized); lspd::HookSyms(handle, MarkClassInitialized);
} else { } else {
lspd::HookSyms(handle, hook_func, FixupStaticTrampolines); lspd::HookSyms(handle, FixupStaticTrampolines);
} }
// MakeInitializedClassesVisiblyInitialized will cause deadlock // MakeInitializedClassesVisiblyInitialized will cause deadlock

View File

@ -31,7 +31,7 @@ namespace art {
} }
// @ApiSensitive(Level.MIDDLE) // @ApiSensitive(Level.MIDDLE)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
int api_level = lspd::GetAndroidApiLevel(); int api_level = lspd::GetAndroidApiLevel();
size_t OFFSET_heap; // Get offset from art::Runtime::RunRootClinits() call in IDA size_t OFFSET_heap; // Get offset from art::Runtime::RunRootClinits() call in IDA
switch (api_level) { switch (api_level) {

View File

@ -39,17 +39,17 @@ namespace art {
}); });
// @ApiSensitive(Level.HIGH) // @ApiSensitive(Level.HIGH)
static void DisableHiddenApi(void *handle, HookFunType hook_func) { static void DisableHiddenApi(void *handle) {
const int api_level = lspd::GetAndroidApiLevel(); const int api_level = lspd::GetAndroidApiLevel();
if (api_level < __ANDROID_API_P__) { if (api_level < __ANDROID_API_P__) {
return; return;
} }
if (api_level == __ANDROID_API_P__) { if (api_level == __ANDROID_API_P__) {
lspd::HookSyms(handle, hook_func, GetMethodActionImpl); lspd::HookSyms(handle, GetMethodActionImpl);
lspd::HookSyms(handle, hook_func, GetFieldActionImpl); lspd::HookSyms(handle, GetFieldActionImpl);
} else { } else {
lspd::HookSyms(handle, hook_func, ShouldDenyAccessToMethodImpl); lspd::HookSyms(handle, ShouldDenyAccessToMethodImpl);
lspd::HookSyms(handle, hook_func, ShouldDenyAccessToFieldImpl); lspd::HookSyms(handle, ShouldDenyAccessToFieldImpl);
} }
}; };

View File

@ -22,8 +22,8 @@ namespace art {
} }
}); });
static void DisableUpdateHookedMethodsCode(void *handle, HookFunType hook_func) { static void DisableUpdateHookedMethodsCode(void *handle) {
lspd::HookSym(handle, hook_func, UpdateMethodsCode); lspd::HookSym(handle, UpdateMethodsCode);
} }
} }
} }

View File

@ -21,7 +21,7 @@ namespace art {
JNIEnvExt(void *thiz) : HookedObject(thiz) {} JNIEnvExt(void *thiz) : HookedObject(thiz) {}
// @ApiSensitive(Level.MIDDLE) // @ApiSensitive(Level.MIDDLE)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
RETRIEVE_MEM_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE"); RETRIEVE_MEM_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE");
RETRIEVE_MEM_FUNC_SYMBOL(DeleteLocalRef, "_ZN3art9JNIEnvExt14DeleteLocalRefEP8_jobject"); RETRIEVE_MEM_FUNC_SYMBOL(DeleteLocalRef, "_ZN3art9JNIEnvExt14DeleteLocalRefEP8_jobject");
} }

View File

@ -55,13 +55,13 @@ namespace art {
Class(void *thiz) : HookedObject(thiz) {} Class(void *thiz) : HookedObject(thiz) {}
// @ApiSensitive(Level.MIDDLE) // @ApiSensitive(Level.MIDDLE)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112" RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor, "_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE"); "basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE");
RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv"); RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv");
lspd::HookSyms(handle, hook_func, IsInSamePackage); lspd::HookSyms(handle, IsInSamePackage);
} }
const char *GetDescriptor(std::string *storage) { const char *GetDescriptor(std::string *storage) {

View File

@ -21,8 +21,8 @@ namespace art {
return backup(obj, declaring_class, access_flags, calling_class); return backup(obj, declaring_class, access_flags, calling_class);
}); });
static void PermissiveAccessByReflection(void *handle, HookFunType hook_func) { static void PermissiveAccessByReflection(void *handle) {
lspd::HookSym(handle, hook_func, VerifyAccess); lspd::HookSym(handle, VerifyAccess);
} }
} }
#endif //LSPOSED_REFLECTION_H #endif //LSPOSED_REFLECTION_H

View File

@ -18,7 +18,7 @@ namespace art {
} }
// @ApiSensitive(Level.LOW) // @ApiSensitive(Level.LOW)
static void Setup(void *handle, HookFunType hook_func) { static void Setup(void *handle) {
RETRIEVE_FIELD_SYMBOL(instance, "_ZN3art7Runtime9instance_E"); RETRIEVE_FIELD_SYMBOL(instance, "_ZN3art7Runtime9instance_E");
void * thiz = *reinterpret_cast<void**>(instance); void * thiz = *reinterpret_cast<void**>(instance);
LOGD("_ZN3art7Runtime9instance_E = %p", thiz); LOGD("_ZN3art7Runtime9instance_E = %p", thiz);

View File

@ -26,7 +26,7 @@ namespace art {
return Thread(CurrentFromGdb()); return Thread(CurrentFromGdb());
} }
static void Setup(void *handle, [[maybe_unused]] HookFunType hook_func) { static void Setup(void *handle) {
RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject,
"_ZNK3art6Thread13DecodeJObjectEP8_jobject"); "_ZNK3art6Thread13DecodeJObjectEP8_jobject");
RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, RETRIEVE_FUNC_SYMBOL(CurrentFromGdb,

View File

@ -7,6 +7,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <sys/mman.h> #include <sys/mman.h>
#include "config.h" #include "config.h"
#include "native_hook.h"
#define _uintval(p) reinterpret_cast<uintptr_t>(p) #define _uintval(p) reinterpret_cast<uintptr_t>(p)
#define _ptr(p) reinterpret_cast<void *>(p) #define _ptr(p) reinterpret_cast<void *>(p)
@ -19,8 +20,6 @@
_page_align(_uintval(p) + n) != _page_align(_uintval(p)) ? _page_align(n) + _page_size : _page_align(n), \ _page_align(_uintval(p) + n) != _page_align(_uintval(p)) ? _page_align(n) + _page_size : _page_align(n), \
PROT_READ | PROT_WRITE | PROT_EXEC) PROT_READ | PROT_WRITE | PROT_EXEC)
typedef void (*HookFunType)(void *, void *, void **);
#define CONCATENATE(a, b) a##b #define CONCATENATE(a, b) a##b
#define CREATE_HOOK_STUB_ENTRIES(SYM, RET, FUNC, PARAMS, DEF) \ #define CREATE_HOOK_STUB_ENTRIES(SYM, RET, FUNC, PARAMS, DEF) \
@ -110,10 +109,9 @@ namespace lspd {
return Dlsym(handle, last...); return Dlsym(handle, last...);
} }
ALWAYS_INLINE inline static void HookFunction(HookFunType hook_fun, void *original, ALWAYS_INLINE inline static void HookFunction(void *original, void *replace, void **backup) {
void *replace, void **backup) {
_make_rwx(original, _page_size); _make_rwx(original, _page_size);
hook_fun(original, replace, backup); hook_func(original, replace, backup);
} }
template<class, template<class, class...> class> template<class, template<class, class...> class>
@ -207,15 +205,14 @@ namespace lspd {
}; };
template<typename T> template<typename T>
inline static bool HookSym(void *handle, HookFunType hook_fun, T &arg) { inline static bool HookSymNoHandle(void *original, T &arg) {
auto original = Dlsym(handle, arg.sym);
if (original) { if (original) {
if constexpr(is_instance<decltype(arg.backup), MemberFunction>::value) { if constexpr(is_instance<decltype(arg.backup), MemberFunction>::value) {
void *backup; void *backup;
HookFunction(hook_fun, original, reinterpret_cast<void *>(arg.replace), &backup); HookFunction(original, reinterpret_cast<void *>(arg.replace), &backup);
arg.backup = reinterpret_cast<typename decltype(arg.backup)::FunType>(backup); arg.backup = reinterpret_cast<typename decltype(arg.backup)::FunType>(backup);
} else { } else {
HookFunction(hook_fun, original, reinterpret_cast<void *>(arg.replace), HookFunction(original, reinterpret_cast<void *>(arg.replace),
reinterpret_cast<void **>(&arg.backup)); reinterpret_cast<void **>(&arg.backup));
} }
return true; return true;
@ -224,9 +221,15 @@ namespace lspd {
} }
} }
template<typename T>
inline static bool HookSym(void *handle, T &arg) {
auto original = Dlsym(handle, arg.sym);
return HookSymNoHandle(original, arg);
}
template<typename T, typename...Args> template<typename T, typename...Args>
inline static bool HookSyms(void *handle, HookFunType hook_fun, T &first, Args &...rest) { inline static bool HookSyms(void *handle, T &first, Args &...rest) {
if (!(HookSym(handle, hook_fun, first) || ... || HookSym(handle, hook_fun, rest))) { if (!(HookSym(handle, first) || ... || HookSym(handle, rest))) {
LOGW("Hook Fails: %s", first.sym); LOGW("Hook Fails: %s", first.sym);
return false; return false;
} }

View File

@ -1,7 +1,6 @@
#include <jni.h> #include <jni.h>
#include <android-base/macros.h> #include <android-base/macros.h>
#include <JNIHelper.h> #include <JNIHelper.h>
#include <android-base/logging.h>
#include "jni/config_manager.h" #include "jni/config_manager.h"
#include "jni/art_class_linker.h" #include "jni/art_class_linker.h"
#include "jni/art_heap.h" #include "jni/art_heap.h"
@ -9,19 +8,16 @@
#include "jni/resources_hook.h" #include "jni/resources_hook.h"
#include <dl_util.h> #include <dl_util.h>
#include <art/runtime/jni_env_ext.h> #include <art/runtime/jni_env_ext.h>
#include <art/runtime/mirror/class.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <nativehelper/scoped_local_ref.h>
#include "jni/pending_hooks.h" #include "jni/pending_hooks.h"
#include <sandhook.h> #include <sandhook.h>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include "context.h" #include "context.h"
#include "config_manager.h" #include "config_manager.h"
#include "art/runtime/runtime.h"
#include "art/runtime/gc/heap.h"
#include "native_hook.h" #include "native_hook.h"
#include "jni/logger.h" #include "jni/logger.h"
#include "jni/native_api.h"
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value" #pragma clang diagnostic ignored "-Wunused-value"
@ -121,6 +117,7 @@ namespace lspd {
RegisterArtHeap(env); RegisterArtHeap(env);
RegisterEdxpYahfa(env); RegisterEdxpYahfa(env);
RegisterPendingHooks(env); RegisterPendingHooks(env);
RegisterNativeAPI(env);
variant_ = Variant(ConfigManager::GetInstance()->GetVariant()); variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
LOGI("LSP Variant: %d", variant_); LOGI("LSP Variant: %d", variant_);

View File

@ -0,0 +1,23 @@
//
// Created by 双草酸酯 on 2/7/21.
//
#include "native_api.h"
#include "nativehelper/jni_macros.h"
#include "native_util.h"
#include "JNIHelper.h"
#include "../native_api.h"
namespace lspd {
LSP_DEF_NATIVE_METHOD(void, NativeAPI, recordNativeEntrypoint, jstring jstr) {
JUTFString str(env, jstr);
RegisterNativeLib(str);
}
static JNINativeMethod gMethods[] = {
LSP_NATIVE_METHOD(NativeAPI, recordNativeEntrypoint, "(Ljava/lang/String;)V")
};
void RegisterNativeAPI(JNIEnv *env) {
REGISTER_LSP_NATIVE_METHODS(NativeAPI);
}
}

View File

@ -0,0 +1,15 @@
//
// Created by 双草酸酯 on 2/7/21.
//
#ifndef LSPOSED_JNI_NATIVE_API_H
#define LSPOSED_JNI_NATIVE_API_H
#include <jni.h>
namespace lspd {
void RegisterNativeAPI(JNIEnv*);
}
#endif //LSPOSED_JNI_NATIVE_API_H

View File

@ -1,22 +1,13 @@
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <jni.h> #include <jni.h>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <sys/mman.h>
#include <array> #include <array>
#include <thread>
#include <vector>
#include <utility>
#include <string>
#include <android-base/logging.h>
#include "logging.h" #include "logging.h"
#include "config.h" #include "config.h"
#include "context.h" #include "context.h"
#include <riru.h> #include <riru.h>
#include "config_manager.h" #include "config_manager.h"
#include "native_hook.h" #include "symbol_cache.h"
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value" #pragma clang diagnostic ignored "-Wunused-value"
@ -25,6 +16,7 @@ namespace lspd {
static void onModuleLoaded() { static void onModuleLoaded() {
LOGI("onModuleLoaded: welcome to LSPosed!"); LOGI("onModuleLoaded: welcome to LSPosed!");
// rirud must be used in onModuleLoaded // rirud must be used in onModuleLoaded
InitSymbolCache();
ConfigManager::Init(); ConfigManager::Init();
} }

View File

@ -0,0 +1,89 @@
//
// Created by kotori on 2/4/21.
//
#include "native_api.h"
#include "symbol_cache.h"
#include <dobby.h>
#include <vector>
#include <base/object.h>
/*
* Module: define xposed_native file in /assets, each line is a .so file name
* LSP: Hook do_dlopen, if any .so file matches the name above, try to call
* "native_init(void*)" function in target so with function pointer of "init" below.
* Module: Call init function with the pointer of callback function.
* LSP: Store the callback function pointer (multiple callback allowed) and return
* LsposedNativeAPIEntries struct.
* Module: Since JNI is not yet available at that time, module can store the struct to somewhere else,
* and handle them in JNI_Onload or later.
* Module: Do some MAGIC provided by LSPosed framework.
* LSP: If any so loaded by target app, we will send a callback to the specific module callback function.
* But an exception is, if the target skipped dlopen and handle linker stuffs on their own, the
* callback will not work.
*/
namespace lspd {
std::vector<LsposedNativeOnModuleLoaded> moduleLoadedCallbacks;
std::vector<std::string> moduleNativeLibs;
LsposedNativeAPIEntriesV1 init(LsposedNativeOnModuleLoaded onModuleLoaded) {
if (onModuleLoaded != nullptr) moduleLoadedCallbacks.push_back(onModuleLoaded);
LsposedNativeAPIEntriesV1 ret{
.version = 1,
.inlineHookFunc = HookFunction
};
return ret;
}
void RegisterNativeLib(const std::string& library_name) {
LOGD("native_api: Registered %s", library_name.c_str());
moduleNativeLibs.push_back(library_name);
}
bool hasEnding(std::string_view fullString, std::string_view ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}
CREATE_HOOK_STUB_ENTRIES(
"__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv",
void*, do_dlopen, (const char* name, int flags, const void* extinfo,
const void* caller_addr), {
auto *handle = backup(name, flags, extinfo, caller_addr);
std::string ns(name);
LOGD("native_api: do_dlopen(%s)", name);
if (handle == nullptr) {
return nullptr;
}
for (std::string_view module_lib: moduleNativeLibs) {
// the so is a module so
if (UNLIKELY(hasEnding(ns, module_lib))) {
LOGI("Loading module native library %s", module_lib.data());
void* native_init_sym = dlsym(handle, "native_init");
if (UNLIKELY(native_init_sym == nullptr)) {
LOGE("Failed to get symbol \"native_init\" from library %s", module_lib.data());
break;
}
auto native_init = reinterpret_cast<NativeInit>(native_init_sym);
native_init(reinterpret_cast<void*>(init));
}
}
// Callbacks
for (LsposedNativeOnModuleLoaded callback: moduleLoadedCallbacks) {
callback(name, handle);
}
return handle;
});
void InstallNativeAPI() {
LOGD("InstallNativeAPI: %p", symbol_do_dlopen);
symbol_do_dlopen = DobbySymbolResolver(nullptr, "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv");
HookSymNoHandle(symbol_do_dlopen, do_dlopen);
}
}

View File

@ -0,0 +1,25 @@
//
// Created by kotori on 2/4/21.
//
#ifndef LSPOSED_NATIVE_API_H
#define LSPOSED_NATIVE_API_H
#include <cstdint>
#include <string>
#include <base/object.h>
// typedef int (*HookFunType)(void *, void *, void **); // For portability
typedef void (*LsposedNativeOnModuleLoaded) (const char* name, void* handle);
typedef void (*NativeInit)(void * init_func);
struct LsposedNativeAPIEntriesV1 {
uint32_t version;
lspd::HookFunType inlineHookFunc;
};
namespace lspd {
void InstallNativeAPI();
void RegisterNativeLib(const std::string& library_name);
}
#endif //LSPOSED_NATIVE_API_H

View File

@ -10,6 +10,7 @@
#include "bionic_linker_restriction.h" #include "bionic_linker_restriction.h"
#include "utils.h" #include "utils.h"
#include "logging.h" #include "logging.h"
#include "native_api.h"
#include "native_hook.h" #include "native_hook.h"
#include "riru_hook.h" #include "riru_hook.h"
#include "art/runtime/mirror/class.h" #include "art/runtime/mirror/class.h"
@ -17,7 +18,6 @@
#include "art/runtime/class_linker.h" #include "art/runtime/class_linker.h"
#include "art/runtime/gc/heap.h" #include "art/runtime/gc/heap.h"
#include "art/runtime/hidden_api.h" #include "art/runtime/hidden_api.h"
#include "art/runtime/art_method.h"
#include "art/runtime/instrumentation.h" #include "art/runtime/instrumentation.h"
#include "art/runtime/reflection.h" #include "art/runtime/reflection.h"
@ -27,7 +27,6 @@ namespace lspd {
static volatile bool installed = false; static volatile bool installed = false;
static volatile bool art_hooks_installed = false; static volatile bool art_hooks_installed = false;
static HookFunType hook_func = reinterpret_cast<HookFunType>(DobbyHook);
void InstallArtHooks(void *art_handle); void InstallArtHooks(void *art_handle);
@ -44,7 +43,7 @@ namespace lspd {
return; return;
} }
LOGI("Using api level %d", api_level); LOGI("Using api level %d", api_level);
InstallRiruHooks(hook_func); InstallRiruHooks();
// install ART hooks // install ART hooks
if (api_level >= __ANDROID_API_Q__) { if (api_level >= __ANDROID_API_Q__) {
// From Riru v22 we can't get ART handle by hooking dlopen, so we get libart.so from soinfo. // From Riru v22 we can't get ART handle by hooking dlopen, so we get libart.so from soinfo.
@ -68,22 +67,24 @@ namespace lspd {
ScopedDlHandle art_handle(kLibArtLegacyPath.c_str()); ScopedDlHandle art_handle(kLibArtLegacyPath.c_str());
InstallArtHooks(art_handle.Get()); InstallArtHooks(art_handle.Get());
} }
InstallNativeAPI();
} }
void InstallArtHooks(void *art_handle) { void InstallArtHooks(void *art_handle) {
if (art_hooks_installed) { if (art_hooks_installed) {
return; return;
} }
art::hidden_api::DisableHiddenApi(art_handle, hook_func); art::hidden_api::DisableHiddenApi(art_handle);
art::Runtime::Setup(art_handle, hook_func); art::Runtime::Setup(art_handle);
art::gc::Heap::Setup(art_handle, hook_func); art::gc::Heap::Setup(art_handle);
art::art_method::Setup(art_handle, hook_func); art::art_method::Setup(art_handle);
art::Thread::Setup(art_handle, hook_func); art::Thread::Setup(art_handle);
art::ClassLinker::Setup(art_handle, hook_func); art::ClassLinker::Setup(art_handle);
art::mirror::Class::Setup(art_handle, hook_func); art::mirror::Class::Setup(art_handle);
art::JNIEnvExt::Setup(art_handle, hook_func); art::JNIEnvExt::Setup(art_handle);
art::instrumentation::DisableUpdateHookedMethodsCode(art_handle, hook_func); art::instrumentation::DisableUpdateHookedMethodsCode(art_handle);
art::PermissiveAccessByReflection(art_handle, hook_func); art::PermissiveAccessByReflection(art_handle);
art_hooks_installed = true; art_hooks_installed = true;
LOGI("ART hooks installed"); LOGI("ART hooks installed");

View File

@ -1,8 +1,10 @@
#pragma once #pragma once
#include <dobby.h>
namespace lspd { namespace lspd {
typedef void (*HookFunType)(void *, void *, void **);
static HookFunType hook_func = reinterpret_cast<HookFunType>(DobbyHook);
void InstallInlineHooks(); void InstallInlineHooks();
} }

View File

@ -101,7 +101,7 @@ namespace lspd {
return res; return res;
}); });
void InstallRiruHooks(HookFunType hook_func) { void InstallRiruHooks() {
LOGI("Start to install Riru hook"); LOGI("Start to install Riru hook");
@ -112,8 +112,7 @@ namespace lspd {
LOGE("Failed to get symbol of __system_property_get"); LOGE("Failed to get symbol of __system_property_get");
return; return;
} }
HookFunction(hook_func, sym, reinterpret_cast<void *>(decltype(__system_property_get)::replace), HookSymNoHandle(sym, __system_property_get);
reinterpret_cast<void **>(&decltype(__system_property_get)::backup));
if (GetAndroidApiLevel() >= __ANDROID_API_P__) { if (GetAndroidApiLevel() >= __ANDROID_API_P__) {
sym = DobbySymbolResolver(nullptr, sym = DobbySymbolResolver(nullptr,
@ -122,8 +121,7 @@ namespace lspd {
LOGE("Failed to get symbol of _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_"); LOGE("Failed to get symbol of _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_");
return; return;
} }
HookFunction(hook_func, sym, reinterpret_cast<void *>(decltype(GetProperty)::replace), HookSymNoHandle(sym, GetProperty);
reinterpret_cast<void **>(&decltype(GetProperty)::backup));
} }
LOGI("Riru hooks installed"); LOGI("Riru hooks installed");

View File

@ -16,6 +16,6 @@ namespace lspd {
static constexpr const char *kPropValueCompilerFlagsWS = " --inline-max-code-units=0"; static constexpr const char *kPropValueCompilerFlagsWS = " --inline-max-code-units=0";
void InstallRiruHooks(HookFunType hook_func); void InstallRiruHooks();
} }

View File

@ -0,0 +1,18 @@
//
// Created by kotori on 2/7/21.
//
#include "symbol_cache.h"
#include <dobby.h>
#include <art/base/macros.h>
#include <logging.h>
namespace lspd {
void InitSymbolCache() {
if (LIKELY(initialized)) return;
LOGD("InitSymbolCache");
// TODO: set image name
symbol_do_dlopen = DobbySymbolResolver(nullptr, "__dl__Z9do_dlopenPKciPK17android_dlextinfoPKv");
initialized = true;
}
}

View File

@ -0,0 +1,16 @@
//
// Created by kotori on 2/7/21.
//
#ifndef LSPOSED_SYMBOL_CACHE_H
#define LSPOSED_SYMBOL_CACHE_H
#include <atomic>
namespace lspd {
static std::atomic_bool initialized;
static void* symbol_do_dlopen = nullptr;
void InitSymbolCache();
}
#endif //LSPOSED_SYMBOL_CACHE_H

View File

@ -41,6 +41,7 @@ import de.robv.android.xposed.callbacks.XC_InitPackageResources;
import de.robv.android.xposed.callbacks.XC_InitZygote; import de.robv.android.xposed.callbacks.XC_InitZygote;
import de.robv.android.xposed.callbacks.XC_LoadPackage; import de.robv.android.xposed.callbacks.XC_LoadPackage;
import de.robv.android.xposed.callbacks.XCallback; import de.robv.android.xposed.callbacks.XCallback;
import io.github.lsposed.lspd.nativebridge.NativeAPI;
import static de.robv.android.xposed.XposedBridge.hookAllConstructors; import static de.robv.android.xposed.XposedBridge.hookAllConstructors;
import static de.robv.android.xposed.XposedBridge.hookAllMethods; import static de.robv.android.xposed.XposedBridge.hookAllMethods;
@ -345,6 +346,7 @@ public final class XposedInit {
newLoadedApk.add(apk); newLoadedApk.add(apk);
} else { } else {
loadedModules.add(apk); // temporarily add it for XSharedPreference loadedModules.add(apk); // temporarily add it for XSharedPreference
loadNativeLibs(apk);
boolean loadSuccess = loadModule(apk, topClassLoader, callInitZygote); boolean loadSuccess = loadModule(apk, topClassLoader, callInitZygote);
if (loadSuccess) { if (loadSuccess) {
newLoadedApk.add(apk); newLoadedApk.add(apk);
@ -391,6 +393,45 @@ public final class XposedInit {
} }
} }
/**
* Load all so from an APK by reading <code>assets/native_init</code>.
* It will only store the so names but not doing anything.
*/
private static boolean loadNativeLibs(String apk) {
ZipFile zipFile = null;
InputStream is;
try {
zipFile = new ZipFile(apk);
ZipEntry zipEntry = zipFile.getEntry("assets/native_init");
if (zipEntry == null) {
Log.e(TAG, " assets/native_init not found in the APK");
closeSilently(zipFile);
return false;
}
is = zipFile.getInputStream(zipEntry);
} catch (IOException e) {
Log.w(TAG, " Cannot read assets/native_init in the APK. Maybe it doesn't support native API", e);
closeSilently(zipFile);
return false;
}
BufferedReader moduleLibraryReader = new BufferedReader(new InputStreamReader(is));
String moduleLibraryName;
try {
while ((moduleLibraryName = moduleLibraryReader.readLine()) != null) {
if (!moduleLibraryName.startsWith("#") && moduleLibraryName.endsWith(".so")) {
NativeAPI.recordNativeEntrypoint(moduleLibraryName);
}
}
} catch (IOException e) {
Log.e(TAG, " Failed to load native library list from " + apk, e);
return false;
} finally {
closeSilently(is);
closeSilently(zipFile);
}
return true;
}
/** /**
* Load a module from an APK by calling the init(String) method for all classes defined * Load a module from an APK by calling the init(String) method for all classes defined
* in <code>assets/xposed_init</code>. * in <code>assets/xposed_init</code>.
@ -403,7 +444,14 @@ public final class XposedInit {
return false; return false;
} }
ClassLoader mcl = new PathClassLoader(apk, topClassLoader); // module can load it's own so
StringBuilder nativePath = new StringBuilder();
for (String i: Build.SUPPORTED_ABIS) {
nativePath.append(apk).append("!/lib/").append(i).append(File.pathSeparator);
}
// Log.d(TAG, "Allowed native path" + nativePath.toString());
ClassLoader mcl = new PathClassLoader(apk, nativePath.toString(), topClassLoader);
try { try {
if (mcl.loadClass(INSTANT_RUN_CLASS) != null) { if (mcl.loadClass(INSTANT_RUN_CLASS) != null) {
Log.e(TAG, " Cannot load module, please disable \"Instant Run\" in Android Studio."); Log.e(TAG, " Cannot load module, please disable \"Instant Run\" in Android Studio.");

View File

@ -0,0 +1,5 @@
package io.github.lsposed.lspd.nativebridge;
public class NativeAPI {
public static native void recordNativeEntrypoint(String library_name);
}