Add clang-format and reformat all files

This commit is contained in:
LoveSy 2022-02-19 03:47:26 +08:00
parent a28b3e1227
commit 5bab7a0e38
13 changed files with 504 additions and 561 deletions

18
library/jni/.clang-format Normal file
View File

@ -0,0 +1,18 @@
---
BasedOnStyle: Google
IndentWidth: 4
UseCRLF: false
UseTab: false
---
Language: Cpp
DerivePointerAlignment: true
PointerAlignment: Right
ColumnLimit: 100
AlignEscapedNewlines: Right
Cpp11BracedListStyle: true
Standard: Latest
# IndentAccessModifiers: false
IndentCaseLabels: false
IndentExternBlock: false
AccessModifierOffset: -4
# EmptyLineBeforeAccessModifier: true

View File

@ -3,7 +3,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := lsplant
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include
LOCAL_SRC_FILES := lsplant.cc
LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include
LOCAL_SHARED_LIBRARIES := dex_builder
@ -16,7 +16,7 @@ include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := lsplant_static
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/external/dex_builder/include
LOCAL_SRC_FILES := lsplant.cc
LOCAL_EXPORT_C_INCLUDES:= $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := dex_builder_static

View File

@ -1,14 +1,13 @@
#pragma once
#include "art/thread.hpp"
#include "common.hpp"
namespace lsplant::art {
namespace dex {
class ClassDef {
};
}
class ClassDef {};
} // namespace dex
namespace mirror {
@ -71,8 +70,7 @@ public:
}
const dex::ClassDef *GetClassDef() {
if (GetClassDefSym)
return GetClassDef(this);
if (GetClassDefSym) return GetClassDef(this);
return nullptr;
}
@ -94,5 +92,5 @@ private:
inline static bool is_unsigned = false;
};
}
}
} // namespace mirror
} // namespace lsplant::art

View File

@ -1,15 +1,18 @@
#pragma once
#include "common.hpp"
#include "art/mirror/class.hpp"
#include "common.hpp"
namespace lsplant::art {
class ArtMethod {
CREATE_MEM_FUNC_SYMBOL_ENTRY(std::string, PrettyMethod, ArtMethod *thiz, bool with_signature) {
if (thiz == nullptr) [[unlikely]] return "null";
else if (PrettyMethodSym) [[likely]] return PrettyMethodSym(thiz, with_signature);
else return "null sym";
if (thiz == nullptr) [[unlikely]]
return "null";
else if (PrettyMethodSym) [[likely]]
return PrettyMethodSym(thiz, with_signature);
else
return "null sym";
}
public:
@ -36,43 +39,35 @@ public:
}
}
bool IsStatic() {
return GetAccessFlags() & kAccStatic;
}
bool IsStatic() { return GetAccessFlags() & kAccStatic; }
bool IsNative() {
return GetAccessFlags() & kAccNative;
}
bool IsNative() { return GetAccessFlags() & kAccNative; }
void CopyFrom(const ArtMethod *other) {
memcpy(this, other, art_method_size);
}
void CopyFrom(const ArtMethod *other) { memcpy(this, other, art_method_size); }
void SetEntryPoint(void *entry_point) {
*reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
entry_point_offset) = entry_point;
*reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) + entry_point_offset) =
entry_point;
}
void *GetEntryPoint() {
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
entry_point_offset);
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) + entry_point_offset);
}
void *GetData() {
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) +
data_offset);
return *reinterpret_cast<void **>(reinterpret_cast<uintptr_t>(this) + data_offset);
}
uint32_t GetAccessFlags() {
return (reinterpret_cast<const std::atomic<uint32_t> *>(
reinterpret_cast<uintptr_t>(this) + access_flags_offset))->load(
std::memory_order_relaxed);
return (reinterpret_cast<const std::atomic<uint32_t> *>(reinterpret_cast<uintptr_t>(this) +
access_flags_offset))
->load(std::memory_order_relaxed);
}
void SetAccessFlags(uint32_t flags) {
return (reinterpret_cast<std::atomic<uint32_t> *>(
reinterpret_cast<uintptr_t>(this) + access_flags_offset))->store(
flags, std::memory_order_relaxed);
return (reinterpret_cast<std::atomic<uint32_t> *>(reinterpret_cast<uintptr_t>(this) +
access_flags_offset))
->store(flags, std::memory_order_relaxed);
}
std::string PrettyMethod(bool with_signature = true) {
@ -90,8 +85,8 @@ public:
return false;
}
if (art_method_field = JNI_GetFieldID(env, executable, "artMethod",
"J"); !art_method_field) {
if (art_method_field = JNI_GetFieldID(env, executable, "artMethod", "J");
!art_method_field) {
LOGE("Failed to find artMethod field");
return false;
}
@ -105,8 +100,8 @@ public:
static_assert(std::is_same_v<decltype(clazz)::BaseType, jclass>);
jmethodID get_declared_constructors = JNI_GetMethodID(env, clazz, "getDeclaredConstructors",
"()[Ljava/lang/reflect/Constructor;");
auto constructors = JNI_Cast<jobjectArray>(
JNI_CallObjectMethod(env, throwable, get_declared_constructors));
auto constructors =
JNI_Cast<jobjectArray>(JNI_CallObjectMethod(env, throwable, get_declared_constructors));
auto length = JNI_GetArrayLength(env, constructors);
if (length < 2) {
LOGE("Throwable has less than 2 constructors");
@ -151,20 +146,18 @@ public:
if (sdk_int < __ANDROID_API_Q__) kAccFastInterpreterToInterpreterInvoke = 0;
get_method_shorty_symbol = GetArtSymbol<decltype(get_method_shorty_symbol)>(
info.art_symbol_resolver,
"_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID");
info.art_symbol_resolver, "_ZN3artL15GetMethodShortyEP7_JNIEnvP10_jmethodID");
if (!get_method_shorty_symbol) return false;
return true;
}
static const char *GetMethodShorty(_JNIEnv *env, _jmethodID *method) {
if (get_method_shorty_symbol) [[likely]] return get_method_shorty_symbol(env, method);
if (get_method_shorty_symbol) [[likely]]
return get_method_shorty_symbol(env, method);
return nullptr;
}
static size_t GetEntryPointOffset() {
return entry_point_offset;
}
static size_t GetEntryPointOffset() { return entry_point_offset; }
constexpr static uint32_t kAccPublic = 0x0001; // class, field, method, ic
constexpr static uint32_t kAccPrivate = 0x0002; // field, method, ic
@ -182,8 +175,8 @@ private:
inline static uint32_t kAccPreCompiled = 0x00200000;
inline static uint32_t kAccCompileDontBother = 0x02000000;
inline static const char *
(*get_method_shorty_symbol)(_JNIEnv *env, _jmethodID *method) = nullptr;
inline static const char *(*get_method_shorty_symbol)(_JNIEnv *env,
_jmethodID *method) = nullptr;
};
}
} // namespace lsplant::art

View File

@ -1,24 +1,21 @@
#pragma once
#include "art/runtime/art_method.hpp"
#include "art/mirror/class.hpp"
#include "art/runtime/art_method.hpp"
#include "art/thread.hpp"
#include "common.hpp"
namespace lsplant::art {
class ClassLinker {
private:
CREATE_MEM_FUNC_SYMBOL_ENTRY(
void, SetEntryPointsToInterpreter, ClassLinker *thiz, ArtMethod *art_method) {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, SetEntryPointsToInterpreter, ClassLinker *thiz,
ArtMethod *art_method) {
if (SetEntryPointsToInterpreterSym) [[likely]] {
SetEntryPointsToInterpreterSym(thiz, art_method);
}
}
[[gnu::always_inline]]
static void MaybeDelayHook(mirror::Class *clazz) {
[[gnu::always_inline]] static void MaybeDelayHook(mirror::Class *clazz) {
const auto *class_def = clazz->GetClassDef();
bool should_intercept = class_def && IsPending(class_def);
if (should_intercept) [[unlikely]] {
@ -28,8 +25,8 @@ private:
}
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE",
void, FixupStaticTrampolines, (ClassLinker * thiz, mirror::Class * clazz), {
"_ZN3art11ClassLinker22FixupStaticTrampolinesENS_6ObjPtrINS_6mirror5ClassEEE", void,
FixupStaticTrampolines, (ClassLinker * thiz, mirror::Class *clazz), {
backup(thiz, clazz);
MaybeDelayHook(clazz);
});
@ -44,8 +41,7 @@ private:
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art11ClassLinker20MarkClassInitializedEPNS_6ThreadENS_6HandleINS_6mirror5ClassEEE",
void*, MarkClassInitialized, (ClassLinker * thiz, Thread * self, uint32_t * clazz_ptr),
{
void *, MarkClassInitialized, (ClassLinker * thiz, Thread *self, uint32_t *clazz_ptr), {
void *result = backup(thiz, self, clazz_ptr);
auto clazz = reinterpret_cast<mirror::Class *>(*clazz_ptr);
MaybeDelayHook(clazz);
@ -53,11 +49,10 @@ private:
});
CREATE_HOOK_STUB_ENTRY(
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv",
bool, ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code),
{
if (quick_code != nullptr &&
(IsHooked(art_method) || IsPending(art_method))) [[unlikely]] {
"_ZN3art11ClassLinker30ShouldUseInterpreterEntrypointEPNS_9ArtMethodEPKv", bool,
ShouldUseInterpreterEntrypoint, (ArtMethod * art_method, const void *quick_code), {
if (quick_code != nullptr && (IsHooked(art_method) || IsPending(art_method)))
[[unlikely]] {
return false;
}
return backup(art_method, quick_code);
@ -67,8 +62,7 @@ private:
CREATE_FUNC_SYMBOL_ENTRY(void, art_quick_generic_jni_trampoline, void *) {}
CREATE_HOOK_STUB_ENTRY(
"_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE",
CREATE_HOOK_STUB_ENTRY("_ZN3art11interpreter29ShouldStayInSwitchInterpreterEPNS_9ArtMethodE",
bool, ShouldStayInSwitchInterpreter, (ArtMethod * art_method), {
if (IsHooked(art_method) || IsPending(art_method)) [[unlikely]] {
return false;
@ -80,7 +74,8 @@ public:
static bool Init(const HookHandler &handler) {
int api_level = GetAndroidApiLevel();
if (!RETRIEVE_MEM_FUNC_SYMBOL(SetEntryPointsToInterpreter,
if (!RETRIEVE_MEM_FUNC_SYMBOL(
SetEntryPointsToInterpreter,
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE")) {
return false;
}
@ -123,8 +118,7 @@ public:
return true;
}
[[gnu::always_inline]]
static bool SetEntryPointsToInterpreter(ArtMethod *art_method) {
[[gnu::always_inline]] static bool SetEntryPointsToInterpreter(ArtMethod *art_method) {
if (art_quick_to_interpreter_bridgeSym && art_quick_generic_jni_trampolineSym) [[likely]] {
if (art_method->GetAccessFlags() & ArtMethod::kAccNative) [[unlikely]] {
art_method->SetEntryPoint(
@ -141,6 +135,5 @@ public:
}
return false;
}
};
}
} // namespace lsplant::art

View File

@ -39,4 +39,4 @@ enum CollectorType {
// Fake collector type for ScopedGCCriticalSection
kCollectorTypeCriticalSection,
};
} // namespace gc
} // namespace lsplant::art::gc

View File

@ -16,7 +16,8 @@ enum GcCause {
kGcCauseForNativeAlloc,
// GC triggered for a collector transition.
kGcCauseCollectorTransition,
// Not a real GC cause, used when we disable moving GC (currently for GetPrimitiveArrayCritical).
// Not a real GC cause, used when we disable moving GC (currently for
// GetPrimitiveArrayCritical).
kGcCauseDisableMovingGc,
// Not a real GC cause, used when we trim the heap.
kGcCauseTrim,
@ -41,4 +42,4 @@ enum GcCause {
// GC cause for the profile saver.
kGcCauseProfileSaver,
};
} // namespace art
} // namespace lsplant::art::gc

View File

@ -1,10 +1,9 @@
#pragma once
#include "gc_cause.hpp"
#include "collector_type.hpp"
#include "art/thread.hpp"
#include "collector_type.hpp"
#include "common.hpp"
#include "gc_cause.hpp"
namespace lsplant::art::gc {
@ -17,13 +16,15 @@ private:
class ScopedGCCriticalSection {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedGCCriticalSection *thiz, Thread *self,
GcCause cause, CollectorType collector_type) {
if (thiz == nullptr) [[unlikely]] return;
if (thiz == nullptr) [[unlikely]]
return;
if (constructorSym) [[likely]]
return constructorSym(thiz, self, cause, collector_type);
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) {
if (thiz == nullptr) [[unlikely]] return;
if (thiz == nullptr) [[unlikely]]
return;
if (destructorSym) [[likely]]
return destructorSym(thiz);
}
@ -33,13 +34,12 @@ public:
constructor(this, self, cause, collector_type);
}
~ScopedGCCriticalSection() {
destructor(this);
}
~ScopedGCCriticalSection() { destructor(this); }
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor,
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_7GcCauseENS0_13CollectorTypeE")) {
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_"
"7GcCauseENS0_13CollectorTypeE")) {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art2gc23ScopedGCCriticalSectionD2Ev")) {
@ -52,4 +52,4 @@ private:
[[maybe_unused]] GCCriticalSection critical_section_;
[[maybe_unused]] const char *old_no_suspend_reason_;
};
}
} // namespace lsplant::art::gc

View File

@ -6,13 +6,12 @@ namespace lsplant::art::jit {
class JitCodeCache {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, MoveObsoleteMethod, JitCodeCache *thiz,
ArtMethod *old_method, ArtMethod *new_method) {
if (MoveObsoleteMethodSym)
[[likely]] MoveObsoleteMethodSym(thiz, old_method, new_method);
if (MoveObsoleteMethodSym) [[likely]]
MoveObsoleteMethodSym(thiz, old_method, new_method);
}
CREATE_MEM_HOOK_STUB_ENTRY(
"_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE",
void, GarbageCollectCache, (JitCodeCache * thiz, Thread * self), {
CREATE_MEM_HOOK_STUB_ENTRY("_ZN3art3jit12JitCodeCache19GarbageCollectCacheEPNS_6ThreadE", void,
GarbageCollectCache, (JitCodeCache * thiz, Thread *self), {
LOGD("Before jit cache gc, moving hooked methods");
for (auto [target, backup] : GetJitMovements()) {
MoveObsoleteMethod(thiz, target, backup);
@ -22,7 +21,8 @@ class JitCodeCache {
public:
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(MoveObsoleteMethod,
if (!RETRIEVE_MEM_FUNC_SYMBOL(
MoveObsoleteMethod,
"_ZN3art3jit12JitCodeCache18MoveObsoleteMethodEPNS_9ArtMethodES3_")) {
return false;
}
@ -32,4 +32,4 @@ public:
return true;
}
};
}
} // namespace lsplant::art::jit

View File

@ -1,6 +1,7 @@
#pragma once
#include <jni.h>
#include <string_view>
/// \namespace namespace of LSPlant
@ -58,8 +59,8 @@ struct InitInfo {
/// \return Indicate whether initialization succeed. Behavior is undefined if calling other
/// LSPlant interfaces before initialization or after a fail initialization.
/// \see InitInfo.
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
bool Init(JNIEnv *env, const InitInfo &info);
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool Init(JNIEnv *env,
const InitInfo &info);
/// \brief Hook a Java method by providing the \p target_method together with the context object
/// \p hooker_object and its callback \p callback_method.
@ -93,9 +94,10 @@ bool Init(JNIEnv *env, const InitInfo &info);
/// simultaneously call on this function with the same \p target_method does not guarantee only one
/// will success. If you call this with different \p hooker_object on the same target_method
/// simultaneously, the behavior is undefined.
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
jobject
Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback_method);
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] jobject Hook(JNIEnv *env,
jobject target_method,
jobject hooker_object,
jobject callback_method);
/// \brief Unhook a Java function that is previously hooked.
/// \param[in] env The Java environment.
@ -104,8 +106,8 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback
/// \note Calling \p backup (the return method of #Hook()) after unhooking is undefined behavior.
/// Please read #Hook()'s note for more details.
/// \see Hook()
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
bool UnHook(JNIEnv *env, jobject target_method);
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool UnHook(JNIEnv *env,
jobject target_method);
/// \brief Check if a Java function is hooked by LSPlant or not
/// \param[in] env The Java environment.
@ -113,33 +115,33 @@ bool UnHook(JNIEnv *env, jobject target_method);
/// \return If \p method hooked, ture; otherwise, false.
/// Please read #Hook()'s note for more details.
/// \see Hook()
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
bool IsHooked(JNIEnv *env, jobject method);
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool IsHooked(JNIEnv *env,
jobject method);
/// \brief Deoptimize a method to avoid hooked callee not being called because of inline
/// \param[in] env The Java environment.
/// \param[in] method The method to deoptimize. By deoptimizing the method, the method will back all
/// callee without inlining. For example, if you hooked a short method B that is invoked by method
/// A, and you find that your callback to B is not invoked after hooking, then it may mean A has
/// inlined B inside its method body. To force A to call your hooked B, you can deoptimize A and then
/// your hook can take effect. Generally, you need to find all the callers of your hooked callee
/// and that can be hardly achieve. Use this function if you are sure the deoptimized callers
/// inlined B inside its method body. To force A to call your hooked B, you can deoptimize A and
/// then your hook can take effect. Generally, you need to find all the callers of your hooked
/// callee and that can be hardly achieve. Use this function if you are sure the deoptimized callers
/// are all you need. Otherwise, it would be better to change the hook point or to deoptimize the
/// whole app manually (by simple reinstall the app without uninstalled).
/// \return Indicate whether the deoptimizing succeed or not.
/// \note It is safe to call deoptimizing on a hooked method because the deoptimization will
/// perform on the backup method instead.
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
bool Deoptimize(JNIEnv *env, jobject method);
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool Deoptimize(JNIEnv *env,
jobject method);
/// \brief Get the registered native function pointer of a native function. It helps user to hook native
/// methods directly by backing up the native function pointer this function returns and
/// \brief Get the registered native function pointer of a native function. It helps user to hook
/// native methods directly by backing up the native function pointer this function returns and
/// env->registerNatives another native function pointer.
/// \param[in] env The Java environment.
/// \param[in] method The native method to get the native function pointer.
/// \return The native function pointer the \p method previously registered. If it has not been
/// registered or it is not a native method, null is returned instead.
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]]
void *GetNativeFunction(JNIEnv *env, jobject method);
}
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] void *GetNativeFunction(
JNIEnv *env, jobject method);
} // namespace v1
} // namespace lsplant

View File

@ -1,29 +1,27 @@
#pragma once
#include "logging.hpp"
#include "jni_helper.hpp"
#include "hook_helper.hpp"
#include <concepts>
#include "hook_helper.hpp"
#include "jni_helper.hpp"
#include "logging.hpp"
#define CONCATENATE(a, b) a##b
#define CREATE_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \
inline static struct : public lsplant::Hooker<RET PARAMS, decltype(CONCATENATE(SYM, _tstr))>{ \
inline static RET replace PARAMS DEF \
} FUNC
inline static RET replace PARAMS DEF} FUNC
#define CREATE_MEM_HOOK_STUB_ENTRY(SYM, RET, FUNC, PARAMS, DEF) \
inline static struct : public lsplant::MemHooker<RET PARAMS, decltype(CONCATENATE(SYM,_tstr))>{ \
inline static RET replace PARAMS DEF \
} FUNC
inline static struct : public lsplant::MemHooker<RET PARAMS, \
decltype(CONCATENATE(SYM, _tstr))>{ \
inline static RET replace PARAMS DEF} FUNC
#define RETRIEVE_FUNC_SYMBOL(name, ...) \
(name##Sym = reinterpret_cast<name##Type>( \
lsplant::Dlsym(handler, __VA_ARGS__)))
(name##Sym = reinterpret_cast<name##Type>(lsplant::Dlsym(handler, __VA_ARGS__)))
#define RETRIEVE_MEM_FUNC_SYMBOL(name, ...) \
(name##Sym = reinterpret_cast<name##Type::FunType>( \
lsplant::Dlsym(handler, __VA_ARGS__)))
(name##Sym = reinterpret_cast<name##Type::FunType>(lsplant::Dlsym(handler, __VA_ARGS__)))
#define RETRIEVE_FIELD_SYMBOL(name, ...) \
(name = reinterpret_cast<decltype(name)>(lsplant::Dlsym(handler, __VA_ARGS__)))
@ -44,13 +42,9 @@ using HookHandler = InitInfo;
template <char... chars>
struct tstring : public std::integer_sequence<char, chars...> {
inline constexpr static const char *c_str() {
return str_;
}
inline constexpr static const char *c_str() { return str_; }
inline constexpr operator std::string_view() const {
return { c_str(), sizeof...(chars) };
}
inline constexpr operator std::string_view() const { return {c_str(), sizeof...(chars)}; }
private:
inline static constexpr char str_[]{chars..., '\0'};
@ -61,10 +55,8 @@ inline constexpr tstring<chars...> operator ""_tstr() {
return {};
}
template <char... as, char... bs>
inline constexpr tstring<as..., bs...>
operator+(const tstring<as...> &, const tstring<bs...> &) {
inline constexpr tstring<as..., bs...> operator+(const tstring<as...> &, const tstring<bs...> &) {
return {};
}
@ -85,8 +77,8 @@ inline void *Dlsym(const HookHandler &handle, const char *name) {
}
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...)) {
requires(std::is_same_v<T, void> ||
std::is_same_v<Class, T>) inline static auto memfun_cast(Return (*func)(T *, Args...)) {
union {
Return (Class::*f)(Args...);
@ -112,10 +104,13 @@ class MemberFunction<Return(Args...), This> {
using SelfType = MemberFunction<Return(This *, Args...), This>;
using ThisType = std::conditional_t<std::is_same_v<This, void>, SelfType, This>;
using MemFunType = Return (ThisType::*)(Args...);
public:
using FunType = Return (*)(This *, Args...);
private:
MemFunType f_ = nullptr;
public:
MemberFunction() = default;
@ -127,9 +122,7 @@ public:
return (reinterpret_cast<ThisType *>(thiz)->*f_)(std::forward<Args>(args)...);
}
inline operator bool() {
return f_ != nullptr;
}
inline operator bool() { return f_ != nullptr; }
};
// deduction guide
@ -170,8 +163,8 @@ inline static bool HookSymNoHandle(const HookHandler &handler, void *original, T
void *backup = handler.inline_hooker(original, reinterpret_cast<void *>(arg.replace));
arg.backup = reinterpret_cast<typename decltype(arg.backup)::FunType>(backup);
} else {
arg.backup = reinterpret_cast<decltype(arg.backup)>(handler.inline_hooker(original,
reinterpret_cast<void *>(arg.replace)));
arg.backup = reinterpret_cast<decltype(arg.backup)>(
handler.inline_hooker(original, reinterpret_cast<void *>(arg.replace)));
}
return true;
} else {
@ -194,4 +187,4 @@ inline static bool HookSyms(const HookHandler &handle, T &first, Args &...rest)
return true;
}
}
} // namespace lsplant

View File

@ -4,7 +4,9 @@
#pragma once
#include <jni.h>
#include <string>
#include "logging.hpp"
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
@ -13,12 +15,10 @@
namespace lsplant {
template <class, template <class, class...> class>
struct is_instance : public std::false_type {
};
struct is_instance : public std::false_type {};
template <class... Ts, template <class, class...> class U>
struct is_instance<U<Ts...>, U> : public std::true_type {
};
struct is_instance<U<Ts...>, U> : public std::true_type {};
template <class T, template <class, class...> class U>
inline constexpr bool is_instance_v = is_instance<T, U>::value;
@ -31,22 +31,16 @@ class ScopedLocalRef {
public:
using BaseType [[maybe_unused]] = T;
ScopedLocalRef(JNIEnv *env, T localRef) : env_(env), local_ref_(localRef) {
}
ScopedLocalRef(JNIEnv *env, T localRef) : env_(env), local_ref_(localRef) {}
ScopedLocalRef(ScopedLocalRef &&s) noexcept: env_(s.env_), local_ref_(s.release()) {
}
ScopedLocalRef(ScopedLocalRef &&s) noexcept : env_(s.env_), local_ref_(s.release()) {}
template <JObject U>
ScopedLocalRef(ScopedLocalRef<U> &&s) noexcept: env_(s.env_), local_ref_((T) s.release()) {
}
ScopedLocalRef(ScopedLocalRef<U> &&s) noexcept : env_(s.env_), local_ref_((T)s.release()) {}
explicit ScopedLocalRef(JNIEnv *env) noexcept: env_(env), local_ref_(nullptr) {
}
explicit ScopedLocalRef(JNIEnv *env) noexcept : env_(env), local_ref_(nullptr) {}
~ScopedLocalRef() {
reset();
}
~ScopedLocalRef() { reset(); }
void reset(T ptr = nullptr) {
if (ptr != local_ref_) {
@ -63,13 +57,9 @@ public:
return localRef;
}
T get() const {
return local_ref_;
}
T get() const { return local_ref_; }
operator T() const {
return local_ref_;
}
operator T() const { return local_ref_; }
// We do not expose an empty constructor as it can easily lead to errors
// using common idioms, e.g.:
@ -82,16 +72,12 @@ public:
return *this;
}
operator bool() const {
return local_ref_;
}
operator bool() const { return local_ref_; }
template <JObject U>
friend
class ScopedLocalRef;
friend class ScopedLocalRef;
friend
class JUTFString;
friend class JUTFString;
private:
JNIEnv *env_;
@ -99,23 +85,19 @@ private:
DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
};
class JNIScopeFrame {
JNIEnv *env_;
public:
JNIScopeFrame(JNIEnv *env, jint size) : env_(env) {
env_->PushLocalFrame(size);
}
~JNIScopeFrame() {
env_->PopLocalFrame(nullptr);
}
public:
JNIScopeFrame(JNIEnv *env, jint size) : env_(env) { env_->PushLocalFrame(size); }
~JNIScopeFrame() { env_->PopLocalFrame(nullptr); }
};
template <typename T, typename U>
concept ScopeOrRaw = std::is_convertible_v<T, U> ||
(is_instance_v<std::decay_t<T>, ScopedLocalRef> &&
std::is_convertible_v<typename std::decay_t<T>::BaseType, U>);
(is_instance_v<std::decay_t<T>, ScopedLocalRef>
&&std::is_convertible_v<typename std::decay_t<T>::BaseType, U>);
template <typename T>
concept ScopeOrClass = ScopeOrRaw<T, jclass>;
@ -127,8 +109,8 @@ inline ScopedLocalRef<jstring> ClearException(JNIEnv *env) {
if (auto exception = env->ExceptionOccurred()) {
env->ExceptionClear();
static jclass log = (jclass)env->NewGlobalRef(env->FindClass("android/util/Log"));
static jmethodID toString = env->GetStaticMethodID(log, "getStackTraceString",
"(Ljava/lang/Throwable;)Ljava/lang/String;");
static jmethodID toString = env->GetStaticMethodID(
log, "getStackTraceString", "(Ljava/lang/Throwable;)Ljava/lang/String;");
auto str = (jstring)env->CallStaticObjectMethod(log, toString, exception);
env->DeleteLocalRef(exception);
return {env, str};
@ -137,32 +119,31 @@ inline ScopedLocalRef<jstring> ClearException(JNIEnv *env) {
}
template <typename T>
[[maybe_unused]]
inline auto UnwrapScope(T &&x) {
[[maybe_unused]] inline auto UnwrapScope(T &&x) {
if constexpr (std::is_same_v<std::decay_t<T>, std::string_view>)
return x.data();
else if constexpr (is_instance_v<std::decay_t<T>, ScopedLocalRef>)
return x.get();
else return std::forward<T>(x);
else
return std::forward<T>(x);
}
template <typename T>
[[maybe_unused]]
inline auto WrapScope(JNIEnv *env, T &&x) {
[[maybe_unused]] inline auto WrapScope(JNIEnv *env, T &&x) {
if constexpr (std::is_convertible_v<T, _jobject *>) {
return ScopedLocalRef(env, std::forward<T>(x));
} else return x;
} else
return x;
}
template <typename... T, size_t... I>
[[maybe_unused]]
inline auto WrapScope(JNIEnv *env, std::tuple<T...> &&x, std::index_sequence<I...>) {
[[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple<T...> &&x,
std::index_sequence<I...>) {
return std::make_tuple(WrapScope(env, std::forward<T>(std::get<I>(x)))...);
}
template <typename... T>
[[maybe_unused]]
inline auto WrapScope(JNIEnv *env, std::tuple<T...> &&x) {
[[maybe_unused]] inline auto WrapScope(JNIEnv *env, std::tuple<T...> &&x) {
return WrapScope(env, std::forward<std::tuple<T...>>(x),
std::make_index_sequence<sizeof...(T)>());
}
@ -173,17 +154,17 @@ inline auto JNI_NewStringUTF(JNIEnv *env, std::string_view sv) {
class JUTFString {
public:
inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {
}
inline JUTFString(JNIEnv *env, jstring jstr) : JUTFString(env, jstr, nullptr) {}
inline JUTFString(const ScopedLocalRef<jstring> &jstr) : JUTFString(jstr.env_, jstr.local_ref_,
nullptr) {
}
inline JUTFString(const ScopedLocalRef<jstring> &jstr)
: JUTFString(jstr.env_, jstr.local_ref_, nullptr) {}
inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr) : env_(env),
jstr_(jstr) {
if (env_ && jstr_) cstr_ = env_->GetStringUTFChars(jstr, nullptr);
else cstr_ = default_cstr;
inline JUTFString(JNIEnv *env, jstring jstr, const char *default_cstr)
: env_(env), jstr_(jstr) {
if (env_ && jstr_)
cstr_ = env_->GetStringUTFChars(jstr, nullptr);
else
cstr_ = default_cstr;
}
inline operator const char *() const { return cstr_; }
@ -199,13 +180,13 @@ public:
}
JUTFString(JUTFString &&other)
: env_(std::move(other.env_)), jstr_(std::move(other.jstr_)),
: env_(std::move(other.env_)),
jstr_(std::move(other.jstr_)),
cstr_(std::move(other.cstr_)) {
other.cstr_ = nullptr;
}
JUTFString &
operator=(JUTFString &&other) {
JUTFString &operator=(JUTFString &&other) {
if (&other != this) {
env_ = std::move(other.env_);
jstr_ = std::move(other.jstr_);
@ -225,11 +206,9 @@ private:
JUTFString &operator=(const JUTFString &) = delete;
};
template <typename Func, typename... Args>
requires(std::is_function_v<Func>)
[[maybe_unused]]
inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) {
[[maybe_unused]] inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&...args) {
struct finally {
finally(JNIEnv *env) : env_(env) {}
@ -242,185 +221,166 @@ inline auto JNI_SafeInvoke(JNIEnv *env, Func JNIEnv::*f, Args &&... args) {
JNIEnv *env_;
} _(env);
if constexpr(!std::is_same_v<void, std::invoke_result_t<Func, decltype(UnwrapScope(
if constexpr (!std::is_same_v<void,
std::invoke_result_t<Func, decltype(UnwrapScope(
std::forward<Args>(args)))...>>)
return WrapScope(env, (env->*f)(UnwrapScope(std::forward<Args>(args))...));
else (env->*f)(UnwrapScope(std::forward<Args>(args))...);
else
(env->*f)(UnwrapScope(std::forward<Args>(args))...);
}
[[maybe_unused]]
inline auto JNI_FindClass(JNIEnv *env, std::string_view name) {
[[maybe_unused]] inline auto JNI_FindClass(JNIEnv *env, std::string_view name) {
return JNI_SafeInvoke(env, &JNIEnv::FindClass, name);
}
template <ScopeOrObject Object>
[[maybe_unused]]
inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) {
[[maybe_unused]] inline auto JNI_GetObjectClass(JNIEnv *env, const Object &obj) {
return JNI_SafeInvoke(env, &JNIEnv::GetObjectClass, obj);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_GetFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
[[maybe_unused]] 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);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_ToReflectedMethod(JNIEnv *env, const Class &clazz, jmethodID method, jboolean isStatic) {
[[maybe_unused]] inline auto JNI_ToReflectedMethod(JNIEnv *env, const Class &clazz,
jmethodID method, jboolean isStatic) {
return JNI_SafeInvoke(env, &JNIEnv::ToReflectedMethod, clazz, method, isStatic);
}
template <ScopeOrObject Object>
[[maybe_unused]]
inline auto JNI_GetObjectField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
[[maybe_unused]] inline auto JNI_GetObjectField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
return JNI_SafeInvoke(env, &JNIEnv::GetObjectField, obj, fieldId);
}
template <ScopeOrObject Object>
[[maybe_unused]]
inline auto JNI_GetLongField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
[[maybe_unused]] inline auto JNI_GetLongField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
return JNI_SafeInvoke(env, &JNIEnv::GetLongField, obj, fieldId);
}
template <ScopeOrObject Object>
[[maybe_unused]]
inline auto JNI_GetIntField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
[[maybe_unused]] inline auto JNI_GetIntField(JNIEnv *env, const Object &obj, jfieldID fieldId) {
return JNI_SafeInvoke(env, &JNIEnv::GetIntField, obj, fieldId);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
[[maybe_unused]] inline auto JNI_GetMethodID(JNIEnv *env, const Class &clazz, std::string_view name,
std::string_view sig) {
return JNI_SafeInvoke(env, &JNIEnv::GetMethodID, clazz, name, sig);
}
template <ScopeOrObject Object, typename... Args>
[[maybe_unused]]
inline auto
JNI_CallObjectMethod(JNIEnv *env, const Object &obj, jmethodID method, Args &&... args) {
[[maybe_unused]] inline auto JNI_CallObjectMethod(JNIEnv *env, const Object &obj, jmethodID method,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallObjectMethod, obj, method, std::forward<Args>(args)...);
}
template <ScopeOrObject Object, typename... Args>
[[maybe_unused]]
inline auto JNI_CallIntMethod(JNIEnv *env, const Object &obj, jmethodID method, Args &&... args) {
[[maybe_unused]] inline auto JNI_CallIntMethod(JNIEnv *env, const Object &obj, jmethodID method,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallIntMethod, obj, method, std::forward<Args>(args)...);
}
template <ScopeOrObject Object, typename... Args>
[[maybe_unused]]
inline auto JNI_CallLongMethod(JNIEnv *env, const Object &obj, Args &&... args) {
[[maybe_unused]] inline auto JNI_CallLongMethod(JNIEnv *env, const Object &obj, Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallLongMethod, obj, std::forward<Args>(args)...);
}
template <ScopeOrObject Object, typename... Args>
[[maybe_unused]]
inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallVoidMethod(JNIEnv *env, const Object &obj, Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallVoidMethod, obj, std::forward<Args>(args)...);
}
template <ScopeOrObject Object, typename... Args>
[[maybe_unused]]
inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallBooleanMethod(JNIEnv *env, const Object &obj, Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallBooleanMethod, obj, std::forward<Args>(args)...);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz, std::string_view name, std::string_view sig) {
[[maybe_unused]] inline auto JNI_GetStaticFieldID(JNIEnv *env, const Class &clazz,
std::string_view name, std::string_view sig) {
return JNI_SafeInvoke(env, &JNIEnv::GetStaticFieldID, clazz, name, sig);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz, jfieldID fieldId) {
[[maybe_unused]] inline auto JNI_GetStaticObjectField(JNIEnv *env, const Class &clazz,
jfieldID fieldId) {
return JNI_SafeInvoke(env, &JNIEnv::GetStaticObjectField, clazz, fieldId);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto JNI_GetStaticIntField(JNIEnv *env, const Class &clazz, jfieldID fieldId) {
[[maybe_unused]] inline auto JNI_GetStaticIntField(JNIEnv *env, const Class &clazz,
jfieldID fieldId) {
return JNI_SafeInvoke(env, &JNIEnv::GetStaticIntField, clazz, fieldId);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz, std::string_view name,
std::string_view sig) {
[[maybe_unused]] inline auto JNI_GetStaticMethodID(JNIEnv *env, const Class &clazz,
std::string_view name, std::string_view sig) {
return JNI_SafeInvoke(env, &JNIEnv::GetStaticMethodID, clazz, name, sig);
}
template <ScopeOrClass Class, typename... Args>
[[maybe_unused]]
inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallStaticVoidMethod(JNIEnv *env, const Class &clazz,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallStaticVoidMethod, clazz, std::forward<Args>(args)...);
}
template <ScopeOrClass Class, typename... Args>
[[maybe_unused]]
inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallStaticObjectMethod(JNIEnv *env, const Class &clazz,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallStaticObjectMethod, clazz, std::forward<Args>(args)...);
}
template <ScopeOrClass Class, typename... Args>
[[maybe_unused]]
inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallStaticIntMethod(JNIEnv *env, const Class &clazz,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallStaticIntMethod, clazz, std::forward<Args>(args)...);
}
template <ScopeOrClass Class, typename... Args>
[[maybe_unused]]
inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, const Class &clazz, Args &&...args) {
[[maybe_unused]] inline auto JNI_CallStaticBooleanMethod(JNIEnv *env, const Class &clazz,
Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::CallStaticBooleanMethod, clazz,
std::forward<Args>(args)...);
}
template <ScopeOrRaw<jarray> Array>
[[maybe_unused]]
inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) {
[[maybe_unused]] inline auto JNI_GetArrayLength(JNIEnv *env, const Array &array) {
return JNI_SafeInvoke(env, &JNIEnv::GetArrayLength, array);
}
template <ScopeOrRaw<jobjectArray> Array>
[[maybe_unused]]
inline auto JNI_GetObjectArrayElement(JNIEnv *env, const Array &array, jsize idx) {
[[maybe_unused]] inline auto JNI_GetObjectArrayElement(JNIEnv *env, const Array &array, jsize idx) {
return JNI_SafeInvoke(env, &JNIEnv::GetObjectArrayElement, array, idx);
}
template <ScopeOrClass Class, typename... Args>
[[maybe_unused]]
inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) {
[[maybe_unused]] inline auto JNI_NewObject(JNIEnv *env, const Class &clazz, Args &&...args) {
return JNI_SafeInvoke(env, &JNIEnv::NewObject, clazz, std::forward<Args>(args)...);
}
template <ScopeOrClass Class>
[[maybe_unused]]
inline auto
JNI_RegisterNatives(JNIEnv *env, const Class &clazz, const JNINativeMethod *methods, jint size) {
[[maybe_unused]] inline auto JNI_RegisterNatives(JNIEnv *env, const Class &clazz,
const JNINativeMethod *methods, jint size) {
return JNI_SafeInvoke(env, &JNIEnv::RegisterNatives, clazz, methods, size);
}
template <ScopeOrObject Object>
[[maybe_unused]]
inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) {
[[maybe_unused]] inline auto JNI_NewGlobalRef(JNIEnv *env, Object &&x) {
return (decltype(UnwrapScope(std::forward<Object>(x))))env->NewGlobalRef(
UnwrapScope(std::forward<Object>(x)));
}
template <typename U, typename T>
[[maybe_unused]]
inline auto
JNI_Cast(ScopedLocalRef<T> &&x)requires(std::is_convertible_v<T, _jobject *>) {
[[maybe_unused]] inline auto JNI_Cast(ScopedLocalRef<T> &&x) requires(
std::is_convertible_v<T, _jobject *>) {
return ScopedLocalRef<U>(std::move(x));
}
}
} // namespace lsplant
#undef DISALLOW_COPY_AND_ASSIGN

View File

@ -1,20 +1,22 @@
#include "lsplant.hpp"
#include <sys/mman.h>
#include <android/api-level.h>
#include <atomic>
#include <array>
#include <sys/mman.h>
#include <sys/system_properties.h>
#include "utils/jni_helper.hpp"
#include "art/runtime/gc/scoped_gc_critical_section.hpp"
#include "art/thread.hpp"
#include <array>
#include <atomic>
#include "art/instrumentation.hpp"
#include "art/runtime/jit/jit_code_cache.hpp"
#include "art/runtime/art_method.hpp"
#include "art/thread_list.hpp"
#include "art/runtime/class_linker.hpp"
#include "dex_builder.h"
#include "art/runtime/gc/scoped_gc_critical_section.hpp"
#include "art/runtime/jit/jit_code_cache.hpp"
#include "art/thread.hpp"
#include "art/thread_list.hpp"
#include "common.hpp"
#include "dex_builder.h"
#include "utils/jni_helper.hpp"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
@ -24,13 +26,13 @@
namespace lsplant {
using art::ArtMethod;
using art::thread_list::ScopedSuspendAll;
using art::ClassLinker;
using art::mirror::Class;
using art::Thread;
using art::Instrumentation;
using art::Thread;
using art::gc::ScopedGCCriticalSection;
using art::jit::JitCodeCache;
using art::mirror::Class;
using art::thread_list::ScopedSuspendAll;
namespace {
template <typename T, T... chars>
@ -40,8 +42,7 @@ inline consteval auto operator ""_uarr() {
consteval inline auto GetTrampoline() {
if constexpr (kArch == Arch::kArm) {
return std::make_tuple(
"\x00\x00\x9f\xe5\x20\xf0\x90\xe5\x78\x56\x34\x12"_uarr,
return std::make_tuple("\x00\x00\x9f\xe5\x20\xf0\x90\xe5\x78\x56\x34\x12"_uarr,
// NOLINTNEXTLINE
uint8_t{32u}, uintptr_t{8u});
}
@ -52,14 +53,12 @@ consteval inline auto GetTrampoline() {
uint8_t{44u}, uintptr_t{12u});
}
if constexpr (kArch == Arch::kX86) {
return std::make_tuple(
"\xb8\x78\x56\x34\x12\xff\x70\x20\xc3"_uarr,
return std::make_tuple("\xb8\x78\x56\x34\x12\xff\x70\x20\xc3"_uarr,
// NOLINTNEXTLINE
uint8_t{56u}, uintptr_t{1u});
}
if constexpr (kArch == Arch::kX8664) {
return std::make_tuple(
"\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x20\xc3"_uarr,
return std::make_tuple("\x48\xbf\x78\x56\x34\x12\x78\x56\x34\x12\xff\x77\x20\xc3"_uarr,
// NOLINTNEXTLINE
uint8_t{96u}, uintptr_t{2u});
}
@ -110,13 +109,14 @@ bool InitJNI(JNIEnv *env) {
return false;
}
if (method_get_name = JNI_GetMethodID(env, executable, "getName",
"()Ljava/lang/String;"); !method_get_name) {
if (method_get_name = JNI_GetMethodID(env, executable, "getName", "()Ljava/lang/String;");
!method_get_name) {
LOGE("Failed to find getName method");
return false;
}
if (method_get_declaring_class = JNI_GetMethodID(env, executable, "getDeclaringClass",
"()Ljava/lang/Class;"); !method_get_declaring_class) {
if (method_get_declaring_class =
JNI_GetMethodID(env, executable, "getDeclaringClass", "()Ljava/lang/Class;");
!method_get_declaring_class) {
LOGE("Failed to find getName method");
return false;
}
@ -126,8 +126,8 @@ bool InitJNI(JNIEnv *env) {
return false;
}
if (class_get_class_loader = JNI_GetMethodID(env, clazz, "getClassLoader",
"()Ljava/lang/ClassLoader;");
if (class_get_class_loader =
JNI_GetMethodID(env, clazz, "getClassLoader", "()Ljava/lang/ClassLoader;");
!class_get_class_loader) {
LOGE("Failed to find getClassLoader");
return false;
@ -144,10 +144,10 @@ bool InitJNI(JNIEnv *env) {
return false;
}
in_memory_class_loader = JNI_NewGlobalRef(env, JNI_FindClass(env,
"dalvik/system/InMemoryDexClassLoader"));
in_memory_class_loader_init = JNI_GetMethodID(env, in_memory_class_loader, "<init>",
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
in_memory_class_loader =
JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/InMemoryDexClassLoader"));
in_memory_class_loader_init = JNI_GetMethodID(
env, in_memory_class_loader, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
load_class = JNI_GetMethodID(env, in_memory_class_loader, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
@ -214,10 +214,11 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
return true;
}
std::tuple<jclass, jfieldID, jmethodID, jmethodID>
BuildDex(JNIEnv *env, jobject class_loader,
std::string_view shorty, bool is_static, std::string_view method_name,
std::string_view hooker_class, std::string_view callback_name) {
std::tuple<jclass, jfieldID, jmethodID, jmethodID> BuildDex(JNIEnv *env, jobject class_loader,
std::string_view shorty, bool is_static,
std::string_view method_name,
std::string_view hooker_class,
std::string_view callback_name) {
// NOLINTNEXTLINE
using namespace startop::dex;
@ -232,13 +233,12 @@ BuildDex(JNIEnv *env, jobject class_loader,
parameter_types.reserve(shorty.size() - 1);
std::string storage;
auto return_type =
shorty[0] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
shorty[0]);
shorty[0] == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(shorty[0]);
if (!is_static) parameter_types.push_back(TypeDescriptor::Object); // this object
for (const char &param : shorty.substr(1)) {
parameter_types.push_back(
param == 'L' ? TypeDescriptor::Object : TypeDescriptor::FromDescriptor(
static_cast<char>(param)));
parameter_types.push_back(param == 'L'
? TypeDescriptor::Object
: TypeDescriptor::FromDescriptor(static_cast<char>(param)));
}
ClassBuilder cbuilder{dex_file.MakeClass(generated_class_name)};
@ -262,8 +262,8 @@ BuildDex(JNIEnv *env, jobject class_loader,
hook_builder.BuildBoxIfPrimitive(Value::Parameter(j), parameter_types[i],
Value::Parameter(j));
hook_builder.BuildConst(tmp, static_cast<int>(i));
hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array,
Value::Parameter(j), tmp);
hook_builder.BuildAput(Instruction::Op::kAputObject, hook_params_array, Value::Parameter(j),
tmp);
if (parameter_types[i].is_wide()) ++j;
}
auto handle_hook_method{dex_file.GetOrDeclareMethod(
@ -271,27 +271,24 @@ BuildDex(JNIEnv *env, jobject class_loader,
Prototype{TypeDescriptor::Object, TypeDescriptor::Object.ToArray()})};
hook_builder.AddInstruction(
Instruction::GetStaticObjectField(hooker_field->decl->orig_index, tmp));
hook_builder.AddInstruction(Instruction::InvokeVirtualObject(
handle_hook_method.id, tmp, tmp, hook_params_array));
hook_builder.AddInstruction(
Instruction::InvokeVirtualObject(handle_hook_method.id, tmp, tmp, hook_params_array));
if (return_type == TypeDescriptor::Void) {
hook_builder.BuildReturn();
} else if (return_type.is_primitive()) {
auto box_type{return_type.ToBoxType()};
const ir::Type *type_def = dex_file.GetOrAddType(box_type);
hook_builder.AddInstruction(
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
hook_builder.AddInstruction(Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
hook_builder.BuildUnBoxIfPrimitive(tmp, box_type, tmp);
hook_builder.BuildReturn(tmp, false, return_type.is_wide());
} else {
const ir::Type *type_def = dex_file.GetOrAddType(return_type);
hook_builder.AddInstruction(
Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
hook_builder.AddInstruction(Instruction::Cast(tmp, Value::Type(type_def->orig_index)));
hook_builder.BuildReturn(tmp, true);
}
auto *hook_method = hook_builder.Encode();
auto backup_builder{
cbuilder.CreateMethod("backup", Prototype{ return_type, parameter_types }) };
auto backup_builder{cbuilder.CreateMethod("backup", Prototype{return_type, parameter_types})};
if (return_type == TypeDescriptor::Void) {
backup_builder.BuildReturn();
} else if (return_type.is_wide()) {
@ -310,14 +307,16 @@ BuildDex(JNIEnv *env, jobject class_loader,
slicer::MemView image{dex_file.CreateImage()};
auto *dex_buffer = env->NewDirectByteBuffer(const_cast<void *>(image.ptr()), image.size());
auto my_cl = JNI_NewObject(env, in_memory_class_loader, in_memory_class_loader_init,
dex_buffer, class_loader);
auto my_cl = JNI_NewObject(env, in_memory_class_loader, in_memory_class_loader_init, dex_buffer,
class_loader);
env->DeleteLocalRef(dex_buffer);
if (my_cl) {
auto *target_class = JNI_Cast<jclass>(
auto *target_class =
JNI_Cast<jclass>(
JNI_CallObjectMethod(env, my_cl, load_class,
JNI_NewStringUTF(env, generated_class_name.data()))).release();
JNI_NewStringUTF(env, generated_class_name.data())))
.release();
if (target_class) {
return {
target_class,
@ -378,7 +377,6 @@ void *GenerateTrampolineFor(art::ArtMethod *hook) {
trampoline_pool.store(tl.address, std::memory_order_release);
trampoline_lock.clear(std::memory_order_release);
trampoline_lock.notify_all();
}
LOGV("trampoline: count = %u, address = %zx, target = %zx", count, address,
address + count * kTrampolineSize);
@ -396,8 +394,7 @@ void *GenerateTrampolineFor(art::ArtMethod *hook) {
}
bool DoHook(ArtMethod *target, ArtMethod *hook, ArtMethod *backup) {
ScopedGCCriticalSection section(art::Thread::Current(),
art::gc::kGcCauseDebugger,
ScopedGCCriticalSection section(art::Thread::Current(), art::gc::kGcCauseDebugger,
art::gc::kCollectorTypeDebugger);
ScopedSuspendAll suspend("LSPlant Hook", false);
LOGV("Hooking: target = %p, hook = %p, backup = %p", target, hook, backup);
@ -421,27 +418,25 @@ bool DoHook(ArtMethod *target, ArtMethod *hook, ArtMethod *backup) {
backup->SetPrivate();
LOGV("Done hook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p; hook(%p:0x%x) -> %p",
target, target->GetAccessFlags(), target->GetEntryPoint(),
backup, backup->GetAccessFlags(), backup->GetEntryPoint(),
hook, hook->GetAccessFlags(), hook->GetEntryPoint());
LOGV("Done hook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p; hook(%p:0x%x) -> %p", target,
target->GetAccessFlags(), target->GetEntryPoint(), backup, backup->GetAccessFlags(),
backup->GetEntryPoint(), hook, hook->GetAccessFlags(), hook->GetEntryPoint());
return true;
}
}
bool DoUnHook(ArtMethod *target, ArtMethod *backup) {
ScopedGCCriticalSection section(art::Thread::Current(),
art::gc::kGcCauseDebugger,
ScopedGCCriticalSection section(art::Thread::Current(), art::gc::kGcCauseDebugger,
art::gc::kCollectorTypeDebugger);
ScopedSuspendAll suspend("LSPlant Hook", false);
LOGV("Unhooking: target = %p, backup = %p", target, backup);
auto access_flags = target->GetAccessFlags();
target->CopyFrom(backup);
target->SetAccessFlags(access_flags);
LOGV("Done unhook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p;",
target, target->GetAccessFlags(), target->GetEntryPoint(),
backup, backup->GetAccessFlags(), backup->GetEntryPoint());
LOGV("Done unhook: target(%p:0x%x) -> %p; backup(%p:0x%x) -> %p;", target,
target->GetAccessFlags(), target->GetEntryPoint(), backup, backup->GetAccessFlags(),
backup->GetEntryPoint());
return true;
}
@ -458,16 +453,13 @@ inline namespace v1 {
using ::lsplant::IsHooked;
[[maybe_unused]]
bool Init(JNIEnv *env, const InitInfo &info) {
[[maybe_unused]] bool Init(JNIEnv *env, const InitInfo &info) {
bool static kInit = InitConfig(info) && InitJNI(env) && InitNative(env, info);
return kInit;
}
[[maybe_unused]]
jobject
Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback_method) {
[[maybe_unused]] jobject Hook(JNIEnv *env, jobject target_method, jobject hooker_object,
jobject callback_method) {
if (!env->IsInstanceOf(target_method, executable)) {
LOGE("target method is not an executable");
return nullptr;
@ -481,8 +473,8 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback
jmethodID backup_method = nullptr;
jfieldID hooker_field = nullptr;
auto target_class = JNI_Cast<jclass>(
JNI_CallObjectMethod(env, target_method, method_get_declaring_class));
auto target_class =
JNI_Cast<jclass>(JNI_CallObjectMethod(env, target_method, method_get_declaring_class));
bool is_proxy = JNI_CallBooleanMethod(env, target_class, class_is_proxy);
auto *target = ArtMethod::FromReflectedMethod(env, target_method);
bool is_static = target->IsStatic();
@ -494,28 +486,24 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback
ScopedLocalRef<jclass> built_class{env};
{
auto callback_name = JNI_Cast<jstring>(
JNI_CallObjectMethod(env, callback_method, method_get_name));
auto callback_name =
JNI_Cast<jstring>(JNI_CallObjectMethod(env, callback_method, method_get_name));
JUTFString method_name(callback_name);
auto callback_class = JNI_Cast<jclass>(JNI_CallObjectMethod(env, callback_method,
method_get_declaring_class));
auto callback_class_loader = JNI_CallObjectMethod(env, callback_class,
class_get_class_loader);
auto callback_class_name = JNI_Cast<jstring>(JNI_CallObjectMethod(env, callback_class,
class_get_name));
auto callback_class = JNI_Cast<jclass>(
JNI_CallObjectMethod(env, callback_method, method_get_declaring_class));
auto callback_class_loader =
JNI_CallObjectMethod(env, callback_class, class_get_class_loader);
auto callback_class_name =
JNI_Cast<jstring>(JNI_CallObjectMethod(env, callback_class, class_get_name));
JUTFString class_name(callback_class_name);
if (!env->IsInstanceOf(hooker_object, callback_class)) {
LOGE("callback_method is not a method of hooker_object");
return nullptr;
}
std::tie(built_class, hooker_field, hook_method, backup_method) =
WrapScope(env, BuildDex(env, callback_class_loader,
ArtMethod::GetMethodShorty(env, env->FromReflectedMethod(
target_method)),
is_static,
method_name.get(),
class_name.get(),
method_name.get()));
std::tie(built_class, hooker_field, hook_method, backup_method) = WrapScope(
env, BuildDex(env, callback_class_loader,
ArtMethod::GetMethodShorty(env, env->FromReflectedMethod(target_method)),
is_static, method_name.get(), class_name.get(), method_name.get()));
if (!built_class || !hooker_field || !hook_method || !backup_method) {
LOGE("Failed to generate hooker");
return nullptr;
@ -550,15 +538,15 @@ Hook(JNIEnv *env, jobject target_method, jobject hooker_object, jobject callback
if (DoHook(target, hook, backup)) {
jobject global_backup = JNI_NewGlobalRef(env, reflected_backup);
RecordHooked(target, global_backup);
if (!is_proxy) [[likely]] RecordJitMovement(target, backup);
if (!is_proxy) [[likely]]
RecordJitMovement(target, backup);
return global_backup;
}
return nullptr;
}
[[maybe_unused]]
bool UnHook(JNIEnv *env, jobject target_method) {
[[maybe_unused]] bool UnHook(JNIEnv *env, jobject target_method) {
if (!env->IsInstanceOf(target_method, executable)) {
LOGE("target method is not an executable");
return false;
@ -588,8 +576,7 @@ bool UnHook(JNIEnv *env, jobject target_method) {
return DoUnHook(target, backup);
}
[[maybe_unused]]
bool IsHooked(JNIEnv *env, jobject method) {
[[maybe_unused]] bool IsHooked(JNIEnv *env, jobject method) {
if (!env->IsInstanceOf(method, executable)) {
LOGE("method is not an executable");
return false;
@ -605,8 +592,7 @@ bool IsHooked(JNIEnv *env, jobject method) {
return false;
}
[[maybe_unused]]
bool Deoptimize(JNIEnv *env, jobject method) {
[[maybe_unused]] bool Deoptimize(JNIEnv *env, jobject method) {
if (!env->IsInstanceOf(method, executable)) {
LOGE("method is not an executable");
return false;
@ -626,8 +612,7 @@ bool Deoptimize(JNIEnv *env, jobject method) {
return ClassLinker::SetEntryPointsToInterpreter(art_method);
}
[[maybe_unused]]
void *GetNativeFunction(JNIEnv *env, jobject method) {
[[maybe_unused]] void *GetNativeFunction(JNIEnv *env, jobject method) {
if (!env->IsInstanceOf(method, executable)) {
LOGE("method is not an executable");
return nullptr;