[core] RAII JNI interface (#444)
Co-authored-by: Wang Han <wanghan1995315@gmail.com>
This commit is contained in:
parent
6d056a40fd
commit
80bc9c0b35
|
|
@ -122,6 +122,7 @@ namespace yahfa {
|
||||||
SDKVersion = sdkVersion;
|
SDKVersion = sdkVersion;
|
||||||
jclass classExecutable = env->FindClass("java/lang/reflect/Executable");
|
jclass classExecutable = env->FindClass("java/lang/reflect/Executable");
|
||||||
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
|
fieldArtMethod = env->GetFieldID(classExecutable, "artMethod", "J");
|
||||||
|
env->DeleteLocalRef(classExecutable);
|
||||||
LOGI("init to SDK %d", sdkVersion);
|
LOGI("init to SDK %d", sdkVersion);
|
||||||
switch (sdkVersion) {
|
switch (sdkVersion) {
|
||||||
case __ANDROID_API_S__:
|
case __ANDROID_API_S__:
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "native_hook.h"
|
#include "native_hook.h"
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
#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)
|
||||||
|
|
@ -140,9 +141,8 @@ namespace lspd {
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Class, typename Return, typename T, typename... Args>
|
template<typename Class, typename Return, typename T, typename... Args>
|
||||||
|
requires (std::is_same_v<T, void> || std::is_same_v<Class, T>)
|
||||||
inline static auto memfun_cast(Return (*func)(T *, Args...)) {
|
inline static auto memfun_cast(Return (*func)(T *, Args...)) {
|
||||||
static_assert(std::is_same_v<T, void> || std::is_same_v<Class, T>,
|
|
||||||
"Not viable cast");
|
|
||||||
union {
|
union {
|
||||||
Return (Class::*f)(Args...);
|
Return (Class::*f)(Args...);
|
||||||
|
|
||||||
|
|
@ -155,8 +155,7 @@ namespace lspd {
|
||||||
return u.f;
|
return u.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename Return, typename... Args,
|
template<std::same_as<void> T, typename Return, typename... Args>
|
||||||
typename = std::enable_if_t<!std::is_same_v<T, void>>>
|
|
||||||
inline auto memfun_cast(Return (*func)(T *, Args...)) {
|
inline auto memfun_cast(Return (*func)(T *, Args...)) {
|
||||||
return memfun_cast<T>(func);
|
return memfun_cast<T>(func);
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +202,7 @@ namespace lspd {
|
||||||
struct Hooker<Ret(Args...), tstring<cs...>> {
|
struct Hooker<Ret(Args...), tstring<cs...>> {
|
||||||
inline static Ret (*backup)(Args...) = nullptr;
|
inline static Ret (*backup)(Args...) = nullptr;
|
||||||
|
|
||||||
inline static constexpr const char sym[sizeof...(cs) + 1] = {cs..., '\0'};
|
inline static constexpr const char *sym = tstring<cs...>::c_str();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename, typename>
|
template<typename, typename>
|
||||||
|
|
@ -211,10 +210,16 @@ namespace lspd {
|
||||||
template<typename Ret, typename This, typename... Args, char... cs>
|
template<typename Ret, typename This, typename... Args, char... cs>
|
||||||
struct MemHooker<Ret(This, Args...), tstring<cs...>> {
|
struct MemHooker<Ret(This, Args...), tstring<cs...>> {
|
||||||
inline static MemberFunction<Ret(Args...)> backup;
|
inline static MemberFunction<Ret(Args...)> backup;
|
||||||
inline static constexpr const char sym[sizeof...(cs) + 1] = {cs..., '\0'};
|
inline static constexpr const char *sym = tstring<cs...>::c_str();
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
concept HookerType = requires(T a) {
|
||||||
|
a.backup;
|
||||||
|
a.replace;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<HookerType T>
|
||||||
inline static bool HookSymNoHandle(void *original, T &arg) {
|
inline static bool HookSymNoHandle(void *original, T &arg) {
|
||||||
if (original) {
|
if (original) {
|
||||||
if constexpr(is_instance<decltype(arg.backup), MemberFunction>::value) {
|
if constexpr(is_instance<decltype(arg.backup), MemberFunction>::value) {
|
||||||
|
|
@ -231,13 +236,13 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<HookerType T>
|
||||||
inline static bool HookSym(void *handle, T &arg) {
|
inline static bool HookSym(void *handle, T &arg) {
|
||||||
auto original = Dlsym(handle, arg.sym);
|
auto original = Dlsym(handle, arg.sym);
|
||||||
return HookSymNoHandle(original, arg);
|
return HookSymNoHandle(original, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename...Args>
|
template<HookerType T, HookerType...Args>
|
||||||
inline static bool HookSyms(void *handle, T &first, Args &...rest) {
|
inline static bool HookSyms(void *handle, T &first, Args &...rest) {
|
||||||
if (!(HookSym(handle, first) || ... || HookSym(handle, rest))) {
|
if (!(HookSym(handle, first) || ... || HookSym(handle, rest))) {
|
||||||
LOGW("Hook Fails: %s", first.sym);
|
LOGW("Hook Fails: %s", first.sym);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "base/object.h"
|
||||||
|
|
||||||
#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz
|
#define JNI_START JNIEnv *env, [[maybe_unused]] jclass clazz
|
||||||
|
|
||||||
|
|
@ -78,6 +79,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
concept JObject = std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>;
|
||||||
|
|
||||||
|
template<JObject T>
|
||||||
class ScopedLocalRef {
|
class ScopedLocalRef {
|
||||||
public:
|
public:
|
||||||
ScopedLocalRef(JNIEnv *env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
ScopedLocalRef(JNIEnv *env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
||||||
|
|
@ -86,6 +90,10 @@ public:
|
||||||
ScopedLocalRef(ScopedLocalRef &&s) noexcept: mEnv(s.mEnv), mLocalRef(s.release()) {
|
ScopedLocalRef(ScopedLocalRef &&s) noexcept: mEnv(s.mEnv), mLocalRef(s.release()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<JObject U>
|
||||||
|
ScopedLocalRef(ScopedLocalRef<U> &&s) noexcept: mEnv(s.mEnv), mLocalRef((T) s.release()) {
|
||||||
|
}
|
||||||
|
|
||||||
explicit ScopedLocalRef(JNIEnv *env) : mEnv(env), mLocalRef(nullptr) {
|
explicit ScopedLocalRef(JNIEnv *env) : mEnv(env), mLocalRef(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,18 +101,18 @@ public:
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(T ptr = NULL) {
|
void reset(T ptr = nullptr) {
|
||||||
if (ptr != mLocalRef) {
|
if (ptr != mLocalRef) {
|
||||||
if (mLocalRef != NULL) {
|
if (mLocalRef != nullptr) {
|
||||||
mEnv->DeleteLocalRef(mLocalRef);
|
mEnv->DeleteLocalRef(mLocalRef);
|
||||||
}
|
}
|
||||||
mLocalRef = ptr;
|
mLocalRef = ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T release() __attribute__((warn_unused_result)) {
|
[[nodiscard]] T release() {
|
||||||
T localRef = mLocalRef;
|
T localRef = mLocalRef;
|
||||||
mLocalRef = NULL;
|
mLocalRef = nullptr;
|
||||||
return localRef;
|
return localRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,26 +131,28 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows "if (scoped_ref == nullptr)"
|
|
||||||
bool operator==(std::nullptr_t) const {
|
|
||||||
return mLocalRef == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allows "if (scoped_ref != nullptr)"
|
|
||||||
bool operator!=(std::nullptr_t) const {
|
|
||||||
return mLocalRef != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
return mLocalRef;
|
return mLocalRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<JObject U>
|
||||||
|
friend
|
||||||
|
class ScopedLocalRef;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JNIEnv *mEnv;
|
JNIEnv *mEnv;
|
||||||
T mLocalRef;
|
T mLocalRef;
|
||||||
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
|
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename U>
|
||||||
|
concept ScopeOrRaw = std::is_same_v<T, U> || std::is_same_v<ScopedLocalRef<T>, U>;
|
||||||
|
template<typename T>
|
||||||
|
concept ScopeOrClass = ScopeOrRaw<jclass, T>;
|
||||||
|
template<typename T>
|
||||||
|
concept ScopeOrObject = ScopeOrRaw<jobject, T>;
|
||||||
|
|
||||||
inline ScopedLocalRef<jstring> ClearException(JNIEnv *env) {
|
inline ScopedLocalRef<jstring> ClearException(JNIEnv *env) {
|
||||||
if (auto exception = env->ExceptionOccurred()) {
|
if (auto exception = env->ExceptionOccurred()) {
|
||||||
env->ExceptionClear();
|
env->ExceptionClear();
|
||||||
|
|
@ -157,15 +167,24 @@ inline ScopedLocalRef<jstring> ClearException(JNIEnv *env) {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto unwrap_sv(T &&x) {
|
inline auto unwrap_scope(T &&x) {
|
||||||
if constexpr (std::is_same_v<std::decay_t<T>, std::string_view>) return x.data();
|
if constexpr (std::is_same_v<std::decay_t<T>, std::string_view>) return x.data();
|
||||||
|
else if constexpr (lspd::is_instance<std::decay_t<T>, ScopedLocalRef>::value) return x.get();
|
||||||
else return std::forward<T>(x);
|
else return std::forward<T>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func, typename ...Args>
|
template<typename T>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_SafeInvoke(JNIEnv *env, Func f, Args &&... args) {
|
inline auto wrap_scope(JNIEnv *env, T &&x) {
|
||||||
static_assert(std::is_member_function_pointer_v<Func>);
|
if constexpr (std::is_convertible_v<T, jobject>) {
|
||||||
|
return ScopedLocalRef(env, std::forward<T>(x));
|
||||||
|
} else return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func, typename ...Args>
|
||||||
|
requires(std::is_function_v<Func>)
|
||||||
|
[[maybe_unused]]
|
||||||
|
inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) {
|
||||||
struct finally {
|
struct finally {
|
||||||
finally(JNIEnv *env) : env_(env) {}
|
finally(JNIEnv *env) : env_(env) {}
|
||||||
|
|
||||||
|
|
@ -178,7 +197,10 @@ inline auto JNI_SafeInvoke(JNIEnv *env, Func f, Args &&... args) {
|
||||||
JNIEnv *env_;
|
JNIEnv *env_;
|
||||||
} _(env);
|
} _(env);
|
||||||
|
|
||||||
return (env->*f)(unwrap_sv(std::forward<Args>(args))...);
|
if constexpr (!std::is_same_v<void, std::invoke_result_t<Func, decltype(unwrap_scope(
|
||||||
|
std::forward<Args>(args)))...>>)
|
||||||
|
return wrap_scope(env, (env->*f)(unwrap_scope(std::forward<Args>(args))...));
|
||||||
|
else (env->*f)(unwrap_scope(std::forward<Args>(args))...);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
|
|
@ -186,93 +208,118 @@ inline auto JNI_FindClass(JNIEnv *env, std::string_view name) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::FindClass, name);
|
return JNI_SafeInvoke(env, &JNIEnv::FindClass, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrObject Object>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_GetObjectClass(JNIEnv *env, jobject obj) {
|
inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj);
|
return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_GetFieldID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) {
|
inline auto
|
||||||
|
JNI_GetFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, clazz, name, sig);
|
return JNI_SafeInvoke(env, &JNIEnv::GetFieldID, clazz, name, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_GetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldId) {
|
inline auto JNI_GetObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, clazz, fieldId);
|
return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, clazz, fieldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto
|
inline auto
|
||||||
JNI_GetMethodID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) {
|
JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig);
|
return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrObject Object, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallObjectMethod(JNIEnv *env, jobject obj, Args &&... args) {
|
inline auto JNI_CallObjectMethod(JNIEnv *env, const Object &obj, Args &&... args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrObject Object, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallVoidMethod(JNIEnv *env, jobject obj, Args &&...args) {
|
inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrObject Object, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallBooleanMethod(JNIEnv *env, jobject obj, Args &&...args) {
|
inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto
|
inline auto
|
||||||
JNI_GetStaticFieldID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) {
|
JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig);
|
return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldId) {
|
inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId);
|
return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto
|
inline auto
|
||||||
JNI_GetStaticMethodID(JNIEnv *env, jclass clazz, std::string_view name, std::string_view sig) {
|
JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz, std::string_view name,
|
||||||
|
std::string_view sig) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig);
|
return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrClass Class, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallStaticVoidMethod(JNIEnv *env, jclass clazz, Args &&...args) {
|
inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrClass Class, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallStaticObjectMethod(JNIEnv *env, jclass clazz, Args &&...args) {
|
inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrClass Class, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_CallStaticIntMethod(JNIEnv *env, jclass clazz, Args &&...args) {
|
inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrRaw<jarray> Array>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_GetArrayLength(JNIEnv *env, jarray array) {
|
inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array);
|
return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ...Args>
|
template<ScopeOrClass Class, typename ...Args>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto JNI_NewObject(JNIEnv *env, jclass clazz, Args &&...args) {
|
inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward<Args>(args)...);
|
return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<ScopeOrClass Class>
|
||||||
[[maybe_unused]]
|
[[maybe_unused]]
|
||||||
inline auto
|
inline auto
|
||||||
JNI_RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint size) {
|
JNI_RegisterNatives(JNIEnv *env, const Class &clazz, const JNINativeMethod *methods, jint size) {
|
||||||
return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size);
|
return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[maybe_unused]]
|
||||||
|
inline auto JNI_NewGlobalRef(JNIEnv *env, T &&x) requires(std::is_convertible_v<T, jobject>){
|
||||||
|
return (T) env->NewGlobalRef(std::forward<T>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[maybe_unused]]
|
||||||
|
inline auto
|
||||||
|
JNI_NewGlobalRef(JNIEnv *env, const ScopedLocalRef<T> &x) requires(
|
||||||
|
std::is_convertible_v<T, jobject>){
|
||||||
|
return (T) env->NewGlobalRef(x.get());
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,12 @@ namespace lspd {
|
||||||
const JNINativeMethod *methods,
|
const JNINativeMethod *methods,
|
||||||
jint method_count) {
|
jint method_count) {
|
||||||
|
|
||||||
ScopedLocalRef clazz(env,
|
auto clazz = Context::GetInstance()->FindClassFromCurrentLoader(env, class_name);
|
||||||
Context::GetInstance()->FindClassFromCurrentLoader(env, class_name));
|
|
||||||
if (clazz.get() == nullptr) {
|
if (clazz.get() == nullptr) {
|
||||||
LOGF("Couldn't find class: %s", class_name);
|
LOGF("Couldn't find class: %s", class_name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return JNI_RegisterNatives(env, clazz.get(), methods, method_count);
|
return JNI_RegisterNatives(env, clazz, methods, method_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ namespace lspd {
|
||||||
|
|
||||||
template<char... chars>
|
template<char... chars>
|
||||||
struct tstring : public std::integer_sequence<char, chars...> {
|
struct tstring : public std::integer_sequence<char, chars...> {
|
||||||
constexpr const char *c_str() const {
|
inline constexpr static const char *c_str() {
|
||||||
return str_;
|
return str_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr operator std::string_view() const {
|
inline constexpr operator std::string_view() const {
|
||||||
return c_str();
|
return c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ namespace lspd {
|
||||||
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
|
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
|
||||||
art::JNIEnvExt env_ext(env);
|
art::JNIEnvExt env_ext(env);
|
||||||
ScopedLocalRef clazz(env, env_ext.NewLocalRefer(class_ptr));
|
ScopedLocalRef clazz(env, env_ext.NewLocalRefer(class_ptr));
|
||||||
if (clazz != nullptr) {
|
if (clazz) {
|
||||||
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz.get());
|
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,48 +74,44 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::LoadDex(JNIEnv *env) {
|
void Context::LoadDex(JNIEnv *env) {
|
||||||
jclass classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
auto classloader = JNI_FindClass(env, "java/lang/ClassLoader");
|
||||||
jmethodID getsyscl_mid = JNI_GetStaticMethodID(
|
auto getsyscl_mid = JNI_GetStaticMethodID(
|
||||||
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
env, classloader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
||||||
jobject sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid);
|
auto sys_classloader = JNI_CallStaticObjectMethod(env, classloader, getsyscl_mid);
|
||||||
if (UNLIKELY(!sys_classloader)) {
|
if (UNLIKELY(!sys_classloader)) {
|
||||||
LOGE("getSystemClassLoader failed!!!");
|
LOGE("getSystemClassLoader failed!!!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jclass in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
auto in_memory_classloader = JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
||||||
jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
auto initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||||
jclass byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
auto byte_buffer_class = JNI_FindClass(env, "java/nio/ByteBuffer");
|
||||||
auto dex_buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
auto dex_buffer = env->NewDirectByteBuffer(reinterpret_cast<void *>(dex.data()),
|
||||||
dex.size());
|
dex.size());
|
||||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
if (auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||||
dex_buffer, sys_classloader);
|
dex_buffer, sys_classloader)) {
|
||||||
env->DeleteLocalRef(classloader);
|
inject_class_loader_ = JNI_NewGlobalRef(env, my_cl);
|
||||||
env->DeleteLocalRef(sys_classloader);
|
} else {
|
||||||
env->DeleteLocalRef(in_memory_classloader);
|
|
||||||
env->DeleteLocalRef(byte_buffer_class);
|
|
||||||
|
|
||||||
if (UNLIKELY(my_cl == nullptr)) {
|
|
||||||
LOGE("InMemoryDexClassLoader creation failed!!!");
|
LOGE("InMemoryDexClassLoader creation failed!!!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
inject_class_loader_ = env->NewGlobalRef(my_cl);
|
env->DeleteLocalRef(dex_buffer);
|
||||||
|
|
||||||
env->DeleteLocalRef(my_cl);
|
|
||||||
|
|
||||||
env->GetJavaVM(&vm_);
|
env->GetJavaVM(&vm_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::Init(JNIEnv *env) {
|
void Context::Init(JNIEnv *env) {
|
||||||
class_linker_class_ = (jclass) env->NewGlobalRef(
|
if (auto class_linker_class = FindClassFromCurrentLoader(env, kClassLinkerClassName)) {
|
||||||
FindClassFromCurrentLoader(env, kClassLinkerClassName));
|
class_linker_class_ = JNI_NewGlobalRef(env, class_linker_class);
|
||||||
|
}
|
||||||
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
post_fixup_static_mid_ = JNI_GetStaticMethodID(env, class_linker_class_,
|
||||||
"onPostFixupStaticTrampolines",
|
"onPostFixupStaticTrampolines",
|
||||||
"(Ljava/lang/Class;)V");
|
"(Ljava/lang/Class;)V");
|
||||||
|
|
||||||
entry_class_ = (jclass) (env->NewGlobalRef(
|
if (auto entry_class = FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)) {
|
||||||
FindClassFromLoader(env, GetCurrentClassLoader(), kEntryClassName)));
|
entry_class_ = JNI_NewGlobalRef(env, entry_class);
|
||||||
|
}
|
||||||
|
|
||||||
RegisterLogger(env);
|
RegisterLogger(env);
|
||||||
RegisterResourcesHook(env);
|
RegisterResourcesHook(env);
|
||||||
|
|
@ -125,44 +121,41 @@ namespace lspd {
|
||||||
RegisterNativeAPI(env);
|
RegisterNativeAPI(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass
|
ScopedLocalRef<jclass>
|
||||||
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) {
|
Context::FindClassFromLoader(JNIEnv *env, jobject class_loader, std::string_view class_name) {
|
||||||
if (class_loader == nullptr) return nullptr;
|
if (class_loader == nullptr) return {env, nullptr};
|
||||||
static auto clz = (jclass) env->NewGlobalRef(
|
static auto clz = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/DexClassLoader"));
|
||||||
env->FindClass("dalvik/system/DexClassLoader"));
|
|
||||||
static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
static jmethodID mid = JNI_GetMethodID(env, clz, "loadClass",
|
||||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
jclass ret = nullptr;
|
|
||||||
if (!mid) {
|
if (!mid) {
|
||||||
mid = JNI_GetMethodID(env, clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
mid = JNI_GetMethodID(env, clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
}
|
}
|
||||||
if (LIKELY(mid)) {
|
if (LIKELY(mid)) {
|
||||||
jobject target = JNI_CallObjectMethod(env, class_loader, mid,
|
auto target = JNI_CallObjectMethod(env, class_loader, mid,
|
||||||
env->NewStringUTF(class_name.data()));
|
env->NewStringUTF(class_name.data()));
|
||||||
if (target) {
|
if (target) {
|
||||||
return (jclass) target;
|
return target;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOGE("No loadClass/findClass method found");
|
LOGE("No loadClass/findClass method found");
|
||||||
}
|
}
|
||||||
LOGE("Class %s not found", class_name.data());
|
LOGE("Class %s not found", class_name.data());
|
||||||
return ret;
|
return {env, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Context::FindAndCall(JNIEnv *env, const char *method_name,
|
template<typename ...Args>
|
||||||
const char *method_sig, ...) const {
|
void
|
||||||
|
Context::FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig,
|
||||||
|
Args &&... args) const {
|
||||||
if (UNLIKELY(!entry_class_)) {
|
if (UNLIKELY(!entry_class_)) {
|
||||||
LOGE("cannot call method %s, entry class is null", method_name);
|
LOGE("cannot call method %s, entry class is null", method_name.data());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jmethodID mid = JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig);
|
jmethodID mid = JNI_GetStaticMethodID(env, entry_class_, method_name, method_sig);
|
||||||
if (LIKELY(mid)) {
|
if (LIKELY(mid)) {
|
||||||
va_list args;
|
JNI_CallStaticVoidMethod(env, entry_class_, mid, std::forward<Args>(args)...);
|
||||||
va_start(args, method_sig);
|
|
||||||
env->CallStaticVoidMethodV(entry_class_, mid, args);
|
|
||||||
va_end(args);
|
|
||||||
} else {
|
} else {
|
||||||
LOGE("method %s id is null", method_name);
|
LOGE("method %s id is null", method_name.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +177,7 @@ namespace lspd {
|
||||||
InstallInlineHooks();
|
InstallInlineHooks();
|
||||||
Init(env);
|
Init(env);
|
||||||
FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
|
FindAndCall(env, "forkSystemServerPost", "(Landroid/os/IBinder;)V", binder);
|
||||||
}
|
} else skip_ = true;
|
||||||
}
|
}
|
||||||
setAllowUnload(skip_);
|
setAllowUnload(skip_);
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +214,8 @@ namespace lspd {
|
||||||
void
|
void
|
||||||
Context::OnNativeForkAndSpecializePost(JNIEnv *env) {
|
Context::OnNativeForkAndSpecializePost(JNIEnv *env) {
|
||||||
const JUTFString process_name(env, nice_name_);
|
const JUTFString process_name(env, nice_name_);
|
||||||
auto binder = skip_ ? nullptr : Service::instance()->RequestBinder(env, nice_name_);
|
auto binder = skip_ ? ScopedLocalRef<jobject>{env, nullptr}
|
||||||
|
: Service::instance()->RequestBinder(env, nice_name_);
|
||||||
if (binder) {
|
if (binder) {
|
||||||
LoadDex(env);
|
LoadDex(env);
|
||||||
InstallInlineHooks();
|
InstallInlineHooks();
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "jni_helper.h"
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
class Context {
|
class Context {
|
||||||
|
|
@ -44,9 +45,7 @@ namespace lspd {
|
||||||
|
|
||||||
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
void CallOnPostFixupStaticTrampolines(void *class_ptr);
|
||||||
|
|
||||||
void FindAndCall(JNIEnv *env, const char *method_name, const char *method_sig, ...) const;
|
inline ScopedLocalRef<jclass> FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
||||||
|
|
||||||
inline jclass FindClassFromCurrentLoader(JNIEnv *env, std::string_view className) const {
|
|
||||||
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -78,10 +77,13 @@ namespace lspd {
|
||||||
|
|
||||||
void Init(JNIEnv *env);
|
void Init(JNIEnv *env);
|
||||||
|
|
||||||
static jclass FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
static ScopedLocalRef<jclass> FindClassFromLoader(JNIEnv *env, jobject class_loader,
|
||||||
std::string_view class_name);
|
std::string_view class_name);
|
||||||
static void setAllowUnload(bool unload);
|
static void setAllowUnload(bool unload);
|
||||||
|
|
||||||
|
template<typename ...Args>
|
||||||
|
void FindAndCall(JNIEnv *env, std::string_view method_name, std::string_view method_sig, Args&&... args) const;
|
||||||
|
|
||||||
friend std::unique_ptr<Context> std::make_unique<Context>();
|
friend std::unique_ptr<Context> std::make_unique<Context>();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,10 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
|
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, initXResourcesNative) {
|
||||||
classXResources = Context::GetInstance()->FindClassFromCurrentLoader(env,
|
if (auto classXResources_ = Context::GetInstance()->FindClassFromCurrentLoader(env,
|
||||||
kXResourcesClassName);
|
kXResourcesClassName)) {
|
||||||
if (!classXResources) {
|
classXResources = JNI_NewGlobalRef(env, classXResources_);
|
||||||
|
} else {
|
||||||
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
LOGE("Error while loading XResources class '%s':", kXResourcesClassName);
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
@ -90,14 +91,13 @@ namespace lspd {
|
||||||
if (!PrepareSymbols()) {
|
if (!PrepareSymbols()) {
|
||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
classXResources = reinterpret_cast<jclass>(env->NewGlobalRef(classXResources));
|
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ApiSensitive(Level.MIDDLE)
|
// @ApiSensitive(Level.MIDDLE)
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, removeFinalFlagNative, jclass target_class) {
|
LSP_DEF_NATIVE_METHOD(jboolean, ResourcesHook, removeFinalFlagNative, jclass target_class) {
|
||||||
if (target_class) {
|
if (target_class) {
|
||||||
jclass class_clazz = JNI_FindClass(env, "java/lang/Class");
|
auto class_clazz = JNI_FindClass(env, "java/lang/Class");
|
||||||
jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
jfieldID java_lang_Class_accessFlags = JNI_GetFieldID(
|
||||||
env, class_clazz, "accessFlags", "I");
|
env, class_clazz, "accessFlags", "I");
|
||||||
jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags);
|
jint access_flags = env->GetIntField(target_class, java_lang_Class_accessFlags);
|
||||||
|
|
@ -110,8 +110,8 @@ namespace lspd {
|
||||||
LSP_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent,
|
LSP_DEF_NATIVE_METHOD(jobject, ResourcesHook, buildDummyClassLoader, jobject parent,
|
||||||
jobject resource_super_class, jobject typed_array_super_class) {
|
jobject resource_super_class, jobject typed_array_super_class) {
|
||||||
using namespace startop::dex;
|
using namespace startop::dex;
|
||||||
static auto in_memory_classloader = (jclass) env->NewGlobalRef(
|
static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env,
|
||||||
env->FindClass("dalvik/system/InMemoryDexClassLoader"));
|
"dalvik/system/InMemoryDexClassLoader"));
|
||||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||||
DexBuilder dex_file;
|
DexBuilder dex_file;
|
||||||
|
|
@ -132,7 +132,7 @@ namespace lspd {
|
||||||
|
|
||||||
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
|
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
|
||||||
return JNI_NewObject(env, in_memory_classloader, initMid,
|
return JNI_NewObject(env, in_memory_classloader, initMid,
|
||||||
dex_buffer, parent);
|
dex_buffer, parent).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(void, ResourcesHook, rewriteXmlReferencesNative,
|
LSP_DEF_NATIVE_METHOD(void, ResourcesHook, rewriteXmlReferencesNative,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace lspd {
|
||||||
LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass,
|
LSP_DEF_NATIVE_METHOD(jobject, Yahfa, findMethodNative, jclass targetClass,
|
||||||
jstring methodName, jstring methodSig) {
|
jstring methodName, jstring methodSig) {
|
||||||
return yahfa::findMethodNative(env, clazz, targetClass, methodName,
|
return yahfa::findMethodNative(env, clazz, targetClass, methodName,
|
||||||
methodSig);
|
methodSig);
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target,
|
LSP_DEF_NATIVE_METHOD(jboolean, Yahfa, backupAndHookNative, jobject target,
|
||||||
|
|
@ -60,8 +60,8 @@ namespace lspd {
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
|
LSP_DEF_NATIVE_METHOD(jclass, Yahfa, buildHooker, jobject app_class_loader, jclass return_class,
|
||||||
jobjectArray classes, jstring method_name) {
|
jobjectArray classes, jstring method_name) {
|
||||||
static auto in_memory_classloader = (jclass) env->NewGlobalRef(
|
static auto in_memory_classloader = JNI_NewGlobalRef(env, JNI_FindClass(env,
|
||||||
env->FindClass("dalvik/system/InMemoryDexClassLoader"));
|
"dalvik/system/InMemoryDexClassLoader"));
|
||||||
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
static jmethodID initMid = JNI_GetMethodID(env, in_memory_classloader, "<init>",
|
||||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||||
DexBuilder dex_file;
|
DexBuilder dex_file;
|
||||||
|
|
@ -158,8 +158,9 @@ namespace lspd {
|
||||||
slicer::MemView image{dex_file.CreateImage()};
|
slicer::MemView image{dex_file.CreateImage()};
|
||||||
|
|
||||||
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
|
auto dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
|
||||||
jobject my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
auto my_cl = JNI_NewObject(env, in_memory_classloader, initMid,
|
||||||
dex_buffer, app_class_loader);
|
dex_buffer, app_class_loader);
|
||||||
|
env->DeleteLocalRef(dex_buffer);
|
||||||
|
|
||||||
static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass",
|
static jmethodID mid = JNI_GetMethodID(env, in_memory_classloader, "loadClass",
|
||||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
|
|
@ -167,10 +168,10 @@ namespace lspd {
|
||||||
mid = JNI_GetMethodID(env, in_memory_classloader, "findClass",
|
mid = JNI_GetMethodID(env, in_memory_classloader, "findClass",
|
||||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
}
|
}
|
||||||
jobject target = env->CallObjectMethod(my_cl, mid, env->NewStringUTF("LspHooker_"));
|
auto target = JNI_CallObjectMethod(env, my_cl, mid, env->NewStringUTF("LspHooker_"));
|
||||||
// LOGD("Created %zd", image.size());
|
// LOGD("Created %zd", image.size());
|
||||||
if (target) {
|
if (target) {
|
||||||
return (jclass) target;
|
return (jclass) target.release();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,66 +62,64 @@ namespace lspd {
|
||||||
|
|
||||||
void Service::InitService(JNIEnv *env) {
|
void Service::InitService(JNIEnv *env) {
|
||||||
if (LIKELY(initialized_)) return;
|
if (LIKELY(initialized_)) return;
|
||||||
initialized_ = true;
|
|
||||||
|
|
||||||
// ServiceManager
|
// ServiceManager
|
||||||
service_manager_class_ = env->FindClass("android/os/ServiceManager");
|
if (auto service_manager_class = JNI_FindClass(env, "android/os/ServiceManager"))
|
||||||
if (service_manager_class_) {
|
service_manager_class_ = JNI_NewGlobalRef(env, service_manager_class);
|
||||||
service_manager_class_ = (jclass) env->NewGlobalRef(service_manager_class_);
|
else return;
|
||||||
} else {
|
get_service_method_ = JNI_GetStaticMethodID(env, service_manager_class_, "getService",
|
||||||
env->ExceptionClear();
|
"(Ljava/lang/String;)Landroid/os/IBinder;");
|
||||||
return;
|
if (!get_service_method_) return;
|
||||||
}
|
|
||||||
get_service_method_ = env->GetStaticMethodID(service_manager_class_, "getService",
|
|
||||||
"(Ljava/lang/String;)Landroid/os/IBinder;");
|
|
||||||
if (!get_service_method_) {
|
|
||||||
env->ExceptionClear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IBinder
|
// IBinder
|
||||||
jclass ibinder_class = env->FindClass("android/os/IBinder");
|
if (auto ibinder_class = JNI_FindClass(env, "android/os/IBinder"))
|
||||||
transact_method_ = env->GetMethodID(ibinder_class, "transact",
|
transact_method_ = JNI_GetMethodID(env, ibinder_class, "transact",
|
||||||
"(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z");
|
"(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z");
|
||||||
|
else return;
|
||||||
|
|
||||||
binder_class_ = env->FindClass("android/os/Binder");
|
if (auto binder_class = JNI_FindClass(env, "android/os/Binder"))
|
||||||
if (binder_class_) binder_class_ = (jclass) env->NewGlobalRef(binder_class_);
|
binder_class_ = JNI_NewGlobalRef(env, binder_class);
|
||||||
binder_ctor_ = env->GetMethodID(binder_class_, "<init>", "()V");
|
else return;
|
||||||
|
binder_ctor_ = JNI_GetMethodID(env, binder_class_, "<init>", "()V");
|
||||||
|
|
||||||
// Parcel
|
// Parcel
|
||||||
parcel_class_ = env->FindClass("android/os/Parcel");
|
if (auto parcel_class = JNI_FindClass(env, "android/os/Parcel"))
|
||||||
if (parcel_class_) parcel_class_ = (jclass) env->NewGlobalRef(parcel_class_);
|
parcel_class_ = JNI_NewGlobalRef(env, parcel_class);
|
||||||
obtain_method_ = env->GetStaticMethodID(parcel_class_, "obtain", "()Landroid/os/Parcel;");
|
else return;
|
||||||
recycleMethod_ = env->GetMethodID(parcel_class_, "recycle", "()V");
|
obtain_method_ = JNI_GetStaticMethodID(env, parcel_class_, "obtain",
|
||||||
write_interface_token_method_ = env->GetMethodID(parcel_class_, "writeInterfaceToken",
|
"()Landroid/os/Parcel;");
|
||||||
"(Ljava/lang/String;)V");
|
recycleMethod_ = JNI_GetMethodID(env, parcel_class_, "recycle", "()V");
|
||||||
write_int_method_ = env->GetMethodID(parcel_class_, "writeInt", "(I)V");
|
write_interface_token_method_ = JNI_GetMethodID(env, parcel_class_, "writeInterfaceToken",
|
||||||
write_string_method_ = env->GetMethodID(parcel_class_, "writeString",
|
"(Ljava/lang/String;)V");
|
||||||
"(Ljava/lang/String;)V");
|
write_int_method_ = JNI_GetMethodID(env, parcel_class_, "writeInt", "(I)V");
|
||||||
write_strong_binder_method_ = env->GetMethodID(parcel_class_, "writeStrongBinder",
|
write_string_method_ = JNI_GetMethodID(env, parcel_class_, "writeString",
|
||||||
"(Landroid/os/IBinder;)V");
|
"(Ljava/lang/String;)V");
|
||||||
read_exception_method_ = env->GetMethodID(parcel_class_, "readException", "()V");
|
write_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "writeStrongBinder",
|
||||||
read_strong_binder_method_ = env->GetMethodID(parcel_class_, "readStrongBinder",
|
"(Landroid/os/IBinder;)V");
|
||||||
"()Landroid/os/IBinder;");
|
read_exception_method_ = JNI_GetMethodID(env, parcel_class_, "readException", "()V");
|
||||||
|
read_strong_binder_method_ = JNI_GetMethodID(env, parcel_class_, "readStrongBinder",
|
||||||
|
"()Landroid/os/IBinder;");
|
||||||
// createStringArray_ = env->GetMethodID(parcel_class_, "createStringArray",
|
// createStringArray_ = env->GetMethodID(parcel_class_, "createStringArray",
|
||||||
// "()[Ljava/lang/String;");
|
// "()[Ljava/lang/String;");
|
||||||
|
|
||||||
deadObjectExceptionClass_ = env->FindClass("android/os/DeadObjectException");
|
if (auto deadObjectExceptionClass = JNI_FindClass(env, "android/os/DeadObjectException"))
|
||||||
if (deadObjectExceptionClass_)
|
deadObjectExceptionClass_ = JNI_NewGlobalRef(env, deadObjectExceptionClass);
|
||||||
deadObjectExceptionClass_ = (jclass) env->NewGlobalRef(deadObjectExceptionClass_);
|
initialized_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Service::HookBridge(const Context &context, JNIEnv *env) {
|
void Service::HookBridge(const Context &context, JNIEnv *env) {
|
||||||
static bool hooked = false;
|
static bool hooked = false;
|
||||||
// This should only be ran once, so unlikely
|
// This should only be ran once, so unlikely
|
||||||
if (UNLIKELY(hooked)) return;
|
if (UNLIKELY(hooked)) return;
|
||||||
|
if (UNLIKELY(!initialized_)) return;
|
||||||
hooked = true;
|
hooked = true;
|
||||||
bridge_service_class_ = context.FindClassFromCurrentLoader(env, kBridgeServiceClassName);
|
if (auto bridge_service_class = context.FindClassFromCurrentLoader(env,
|
||||||
if (!bridge_service_class_) {
|
kBridgeServiceClassName))
|
||||||
|
bridge_service_class_ = JNI_NewGlobalRef(env, bridge_service_class);
|
||||||
|
else {
|
||||||
LOGE("server class not found");
|
LOGE("server class not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bridge_service_class_ = (jclass) env->NewGlobalRef(bridge_service_class_);
|
|
||||||
exec_transact_replace_methodID_ = JNI_GetStaticMethodID(env, bridge_service_class_,
|
exec_transact_replace_methodID_ = JNI_GetStaticMethodID(env, bridge_service_class_,
|
||||||
"execTransact",
|
"execTransact",
|
||||||
"(IJJI)Z");
|
"(IJJI)Z");
|
||||||
|
|
@ -130,8 +128,8 @@ namespace lspd {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedLocalRef binderClass(env, env->FindClass("android/os/Binder"));
|
auto binderClass = JNI_FindClass(env, "android/os/Binder");
|
||||||
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binderClass.get(), "execTransact",
|
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binderClass, "execTransact",
|
||||||
"(IJJI)Z");
|
"(IJJI)Z");
|
||||||
auto set_table_override = reinterpret_cast<void (*)(
|
auto set_table_override = reinterpret_cast<void (*)(
|
||||||
JNINativeInterface *)>(Dlsym(handle_libart,
|
JNINativeInterface *)>(Dlsym(handle_libart,
|
||||||
|
|
@ -151,10 +149,10 @@ namespace lspd {
|
||||||
LOGD("Done InitService");
|
LOGD("Done InitService");
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject Service::RequestBinder(JNIEnv *env, jstring nice_name) {
|
ScopedLocalRef<jobject> Service::RequestBinder(JNIEnv *env, jstring nice_name) {
|
||||||
if (UNLIKELY(!initialized_)) {
|
if (UNLIKELY(!initialized_)) {
|
||||||
LOGE("Service not initialized");
|
LOGE("Service not initialized");
|
||||||
return nullptr;
|
return {env, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bridgeServiceName = env->NewStringUTF(BRIDGE_SERVICE_NAME.data());
|
auto bridgeServiceName = env->NewStringUTF(BRIDGE_SERVICE_NAME.data());
|
||||||
|
|
@ -162,7 +160,7 @@ namespace lspd {
|
||||||
get_service_method_, bridgeServiceName);
|
get_service_method_, bridgeServiceName);
|
||||||
if (!bridgeService) {
|
if (!bridgeService) {
|
||||||
LOGD("can't get %s", BRIDGE_SERVICE_NAME.data());
|
LOGD("can't get %s", BRIDGE_SERVICE_NAME.data());
|
||||||
return nullptr;
|
return {env, nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto heart_beat_binder = JNI_NewObject(env, binder_class_, binder_ctor_);
|
auto heart_beat_binder = JNI_NewObject(env, binder_class_, binder_ctor_);
|
||||||
|
|
@ -181,7 +179,7 @@ namespace lspd {
|
||||||
data,
|
data,
|
||||||
reply, 0);
|
reply, 0);
|
||||||
|
|
||||||
jobject service = nullptr;
|
ScopedLocalRef<jobject> service = {env, nullptr};
|
||||||
if (res) {
|
if (res) {
|
||||||
JNI_CallVoidMethod(env, reply, read_exception_method_);
|
JNI_CallVoidMethod(env, reply, read_exception_method_);
|
||||||
service = JNI_CallObjectMethod(env, reply, read_strong_binder_method_);
|
service = JNI_CallObjectMethod(env, reply, read_strong_binder_method_);
|
||||||
|
|
@ -189,25 +187,23 @@ namespace lspd {
|
||||||
JNI_CallVoidMethod(env, data, recycleMethod_);
|
JNI_CallVoidMethod(env, data, recycleMethod_);
|
||||||
JNI_CallVoidMethod(env, reply, recycleMethod_);
|
JNI_CallVoidMethod(env, reply, recycleMethod_);
|
||||||
if (service) {
|
if (service) {
|
||||||
env->NewGlobalRef(heart_beat_binder);
|
JNI_NewGlobalRef(env, heart_beat_binder);
|
||||||
} else {
|
|
||||||
env->DeleteLocalRef(heart_beat_binder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject Service::RequestBinderForSystemServer(JNIEnv *env) {
|
ScopedLocalRef<jobject> Service::RequestBinderForSystemServer(JNIEnv *env) {
|
||||||
if (UNLIKELY(!initialized_ || !bridge_service_class_)) {
|
if (UNLIKELY(!initialized_ || !bridge_service_class_)) {
|
||||||
LOGE("Service not initialized");
|
LOGE("Service not initialized");
|
||||||
return nullptr;
|
return {env, nullptr};
|
||||||
}
|
}
|
||||||
auto bridgeServiceName = env->NewStringUTF(SYSTEM_SERVER_BRIDGE_SERVICE_NAME.data());
|
auto bridgeServiceName = env->NewStringUTF(SYSTEM_SERVER_BRIDGE_SERVICE_NAME.data());
|
||||||
auto binder = JNI_CallStaticObjectMethod(env, service_manager_class_,
|
auto binder = JNI_CallStaticObjectMethod(env, service_manager_class_,
|
||||||
get_service_method_, bridgeServiceName);
|
get_service_method_, bridgeServiceName);
|
||||||
if (!binder) {
|
if (!binder) {
|
||||||
LOGD("Fail to get binder for system server");
|
LOGD("Fail to get binder for system server");
|
||||||
return nullptr;
|
return {env, nullptr};
|
||||||
}
|
}
|
||||||
auto method = JNI_GetStaticMethodID(env, bridge_service_class_,
|
auto method = JNI_GetStaticMethodID(env, bridge_service_class_,
|
||||||
"getApplicationServiceForSystemServer",
|
"getApplicationServiceForSystemServer",
|
||||||
|
|
@ -216,9 +212,7 @@ namespace lspd {
|
||||||
auto app_binder = JNI_CallStaticObjectMethod(env, bridge_service_class_, method, binder,
|
auto app_binder = JNI_CallStaticObjectMethod(env, bridge_service_class_, method, binder,
|
||||||
heart_beat_binder);
|
heart_beat_binder);
|
||||||
if (app_binder) {
|
if (app_binder) {
|
||||||
env->NewGlobalRef(heart_beat_binder);
|
JNI_NewGlobalRef(env, heart_beat_binder);
|
||||||
} else {
|
|
||||||
env->DeleteLocalRef(heart_beat_binder);
|
|
||||||
}
|
}
|
||||||
return app_binder;
|
return app_binder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,9 @@ namespace lspd {
|
||||||
void InitService(JNIEnv *env);
|
void InitService(JNIEnv *env);
|
||||||
|
|
||||||
void HookBridge(const Context& context, JNIEnv *env);
|
void HookBridge(const Context& context, JNIEnv *env);
|
||||||
|
ScopedLocalRef<jobject> RequestBinder(JNIEnv *env, jstring nice_name);
|
||||||
|
|
||||||
jobject RequestBinder(JNIEnv *env, jstring nice_name);
|
ScopedLocalRef<jobject> RequestBinderForSystemServer(JNIEnv *env);
|
||||||
|
|
||||||
jobject RequestBinderForSystemServer(JNIEnv *env);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>();
|
inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue