Refine codes

This commit is contained in:
LoveSy 2022-03-12 16:10:55 +08:00
parent 6d40254a3f
commit b95af07672
11 changed files with 65 additions and 198 deletions

View File

@ -1,101 +0,0 @@
#pragma once
#include "art/thread.hpp"
#include "common.hpp"
namespace lsplant::art {
namespace dex {
class ClassDef {};
} // namespace dex
namespace mirror {
class Class {
private:
CREATE_MEM_FUNC_SYMBOL_ENTRY(const char *, GetDescriptor, Class *thiz, std::string *storage) {
if (GetDescriptorSym) [[likely]]
return GetDescriptor(thiz, storage);
else
return "";
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(const dex::ClassDef *, GetClassDef, Class *thiz) {
if (GetClassDefSym) [[likely]]
return GetClassDefSym(thiz);
return nullptr;
}
public:
static bool Init(JNIEnv *env, const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetDescriptor,
"_ZN3art6mirror5Class13GetDescriptorEPNSt3__112"
"basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE")) {
return false;
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(GetClassDef, "_ZN3art6mirror5Class11GetClassDefEv")) {
return false;
}
auto clazz = JNI_FindClass(env, "java/lang/Class");
if (!clazz) {
LOGE("Failed to find Class");
return false;
}
if (class_status = JNI_GetFieldID(env, clazz, "status", "I"); !class_status) {
LOGE("Failed to find status");
return false;
}
int sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_P__) {
initialized_status = -14;
} else if (sdk_int == __ANDROID_API_O_MR1__) {
initialized_status = 11;
} else {
initialized_status = 10;
}
return true;
}
const char *GetDescriptor(std::string *storage) {
if (GetDescriptorSym) {
return GetDescriptor(storage);
}
return "";
}
std::string GetDescriptor() {
std::string storage;
return GetDescriptor(&storage);
}
const dex::ClassDef *GetClassDef() {
if (GetClassDefSym) return GetClassDef(this);
return nullptr;
}
static int GetStatus(JNIEnv *env, jclass clazz) {
int status = JNI_GetIntField(env, clazz, class_status);
return initialized_status < 0 ? static_cast<uint32_t>(status) >> (32 - 4) : status;
}
static bool IsInitialized(JNIEnv *env, jclass clazz) {
return initialized_status < 0 ? GetStatus(env, clazz) >= -initialized_status
: GetStatus(env, clazz) == initialized_status;
}
static Class *FromReflectedClass(JNIEnv *, jclass clazz) {
return reinterpret_cast<Class *>(art::Thread::Current()->DecodeJObject(clazz));
}
private:
inline static jfieldID class_status = nullptr;
inline static int initialized_status = 0;
};
} // namespace mirror
} // namespace lsplant::art

View File

@ -1,6 +1,5 @@
#pragma once
#include "art/mirror/class.hpp"
#include "common.hpp"
namespace lsplant::art {

View File

@ -1,6 +1,5 @@
#pragma once
#include "art/mirror/class.hpp"
#include "art/runtime/art_method.hpp"
#include "art/thread.hpp"
#include "common.hpp"
@ -40,7 +39,7 @@ public:
static bool Init(const HookHandler &handler) {
int sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_N__) {
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!HookSyms(handler, ShouldUseInterpreterEntrypoint, ShouldStayInSwitchInterpreter))
[[unlikely]] {
return false;
@ -49,13 +48,14 @@ public:
if (!RETRIEVE_MEM_FUNC_SYMBOL(
SetEntryPointsToInterpreter,
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE")) {
"_ZNK3art11ClassLinker27SetEntryPointsToInterpreterEPNS_9ArtMethodE"))
[[unlikely]] {
if (!RETRIEVE_FUNC_SYMBOL(art_quick_to_interpreter_bridge,
"art_quick_to_interpreter_bridge")) {
"art_quick_to_interpreter_bridge")) [[unlikely]] {
return false;
}
if (!RETRIEVE_FUNC_SYMBOL(art_quick_generic_jni_trampoline,
"art_quick_generic_jni_trampoline")) {
"art_quick_generic_jni_trampoline")) [[unlikely]] {
return false;
}
LOGD("art_quick_to_interpreter_bridge = %p", art_quick_to_interpreter_bridgeSym);

View File

@ -45,11 +45,6 @@ class DexFile {
return nullptr;
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, push_back, std::vector<const DexFile*>* thiz,
const DexFile** dex_file) {
push_backSym(thiz, dex_file);
}
public:
static const DexFile* OpenMemory(const void* dex_file, size_t size, std::string location,
std::string* error_msg) {
@ -58,11 +53,11 @@ public:
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg)
.release();
} else if (OpenMemoryRawSym) {
} else if (OpenMemoryRawSym) [[likely]] {
return OpenMemoryRaw(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_, nullptr,
nullptr, error_msg);
} else if (OpenMemoryWithoutOdexSym) {
} else if (OpenMemoryWithoutOdexSym) [[likely]] {
return OpenMemoryWithoutOdex(reinterpret_cast<const uint8_t*>(dex_file), size, location,
reinterpret_cast<const Header*>(dex_file)->checksum_,
nullptr, error_msg);
@ -119,26 +114,26 @@ public:
return false;
}
dex_file_class = JNI_NewGlobalRef(env, JNI_FindClass(env, "dalvik/system/DexFile"));
if (!dex_file_class) {
if (!dex_file_class) [[unlikely]] {
return false;
}
if (sdk_int >= __ANDROID_API_M__) {
if (sdk_int >= __ANDROID_API_M__) [[unlikely]] {
cookie_field = JNI_GetFieldID(env, dex_file_class, "mCookie", "Ljava/lang/Object;");
} else {
cookie_field = JNI_GetFieldID(env, dex_file_class, "mCookie", "J");
dex_file_start_index = -1;
}
if (!cookie_field) {
if (!cookie_field) [[unlikely]] {
return false;
}
file_name_field = JNI_GetFieldID(env, dex_file_class, "mFileName", "Ljava/lang/String;");
if (!file_name_field) {
if (!file_name_field) [[unlikely]] {
return false;
}
if (sdk_int >= __ANDROID_API_N__) {
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
internal_cookie_field =
JNI_GetFieldID(env, dex_file_class, "mInternalCookie", "Ljava/lang/Object;");
if (!internal_cookie_field) {
if (!internal_cookie_field) [[unlikely]] {
return false;
}
dex_file_start_index = 1u;

View File

@ -16,17 +16,13 @@ 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 (constructorSym) [[likely]]
if (thiz && constructorSym) [[likely]]
return constructorSym(thiz, self, cause, collector_type);
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedGCCriticalSection *thiz) {
if (thiz == nullptr) [[unlikely]]
return;
if (destructorSym) [[likely]]
return destructorSym(thiz);
if (thiz && destructorSym) [[likely]]
destructorSym(thiz);
}
public:
@ -39,7 +35,7 @@ public:
static bool Init(const HookHandler &handler) {
// for Android M, it's safe to not found since we have suspendVM & resumeVM
auto sdk_int = GetAndroidApiLevel();
if (sdk_int >= __ANDROID_API_N__) {
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor,
"_ZN3art2gc23ScopedGCCriticalSectionC2EPNS_6ThreadENS0_"
"7GcCauseENS0_13CollectorTypeE")) [[unlikely]] {

View File

@ -36,7 +36,7 @@ public:
}
}
if (sdk_int >= __ANDROID_API_N__) [[likely]] {
if (!HookSyms(handler, GarbageCollectCache)) {
if (!HookSyms(handler, GarbageCollectCache)) [[unlikely]] {
return false;
}
}

View File

@ -10,7 +10,7 @@ class Thread {
};
CREATE_MEM_FUNC_SYMBOL_ENTRY(ObjPtr, DecodeJObject, Thread *thiz, jobject obj) {
if (DecodeJObjectSym)
if (DecodeJObjectSym) [[likely]]
return DecodeJObjectSym(thiz, obj);
else
return {.data = nullptr};
@ -27,10 +27,12 @@ public:
static Thread *Current() { return CurrentFromGdb(); }
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, "_ZNK3art6Thread13DecodeJObjectEP8_jobject")) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(DecodeJObject, "_ZNK3art6Thread13DecodeJObjectEP8_jobject"))
[[unlikely]] {
return false;
}
if (!RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, "_ZN3art6Thread14CurrentFromGdbEv")) {
if (!RETRIEVE_FUNC_SYMBOL(CurrentFromGdb, "_ZN3art6Thread14CurrentFromGdbEv"))
[[unlikely]] {
return false;
}
return true;

View File

@ -7,9 +7,7 @@ namespace lsplant::art::thread_list {
class ScopedSuspendAll {
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, constructor, ScopedSuspendAll *thiz, const char *cause,
bool long_suspend) {
if (thiz == nullptr) [[unlikely]]
return;
if (constructorSym) [[likely]] {
if (thiz && constructorSym) [[likely]] {
return constructorSym(thiz, cause, long_suspend);
} else {
SuspendVM();
@ -17,10 +15,7 @@ class ScopedSuspendAll {
}
CREATE_MEM_FUNC_SYMBOL_ENTRY(void, destructor, ScopedSuspendAll *thiz) {
if (thiz == nullptr) [[unlikely]] {
return;
}
if (destructorSym) [[likely]] {
if (thiz && destructorSym) [[likely]] {
return destructorSym(thiz);
} else {
ResumeVM();
@ -47,16 +42,14 @@ public:
~ScopedSuspendAll() { destructor(this); }
static bool Init(const HookHandler &handler) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor, "_ZN3art16ScopedSuspendAllC2EPKcb")) {
if (!RETRIEVE_FUNC_SYMBOL(SuspendVM, "_ZN3art3Dbg9SuspendVMEv")) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(constructor, "_ZN3art16ScopedSuspendAllC2EPKcb") &&
!RETRIEVE_FUNC_SYMBOL(SuspendVM, "_ZN3art3Dbg9SuspendVMEv")) [[unlikely]] {
return false;
}
}
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art16ScopedSuspendAllD2Ev")) {
if (!RETRIEVE_FUNC_SYMBOL(ResumeVM, "_ZN3art3Dbg8ResumeVMEv")) {
if (!RETRIEVE_MEM_FUNC_SYMBOL(destructor, "_ZN3art16ScopedSuspendAllD2Ev") &&
!RETRIEVE_FUNC_SYMBOL(ResumeVM, "_ZN3art3Dbg8ResumeVMEv")) [[unlikely]] {
return false;
}
}
return true;
}
};

View File

@ -6,7 +6,6 @@
#include <shared_mutex>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include "logging.hpp"
#include "lsplant.hpp"
@ -55,22 +54,8 @@ inline auto GetAndroidApiLevel() {
inline static constexpr auto kPointerSize = sizeof(void *);
template <typename T>
inline T GetArtSymbol(const std::function<void *(std::string_view)> &resolver,
std::string_view symbol) requires(std::is_pointer_v<T>) {
if (auto *result = resolver(symbol); result) {
return reinterpret_cast<T>(result);
} else {
LOGW("Failed to find symbol %*s", static_cast<int>(symbol.length()), symbol.data());
return nullptr;
}
}
namespace art {
class ArtMethod;
namespace dex {
class ClassDef;
}
} // namespace art
namespace {

View File

@ -59,7 +59,7 @@ 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,
[[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
@ -75,26 +75,29 @@ struct InitInfo {
/// \param[in] callback_method The callback method to the \p hooker_object is used to replace the
/// \p target_method. Whenever the \p target_method is invoked, the \p callback_method will be
/// invoked instead of the original \p target_method. The signature of the \p callback_method must
/// be:<br>
/// be:
/// \code{.java}
/// public Object callback_method(Object []args)
/// \endcode<br>
/// \endcode
/// That is, the return type must be \p Object and the parameter type must be \b Object[]. Behavior
/// is undefined if the signature does not match the requirement.
/// args[0] is the this object for non-static methods and there is NOT null this object placeholder
/// for static methods.
/// Extra info can be provided by defining member variables of \p hooker_object.
/// This method must be a method to \p hooker_object.
/// \return The backup method. You can invoke it by reflection to invoke the original method. null
/// if fails.
/// \note This function will automatically generate a stub class for hook. To help debug, you
/// can set the generated class name, its field name, its source name and its method name
/// by setting generated_* in \p InitInfo.
/// \return The backup method. You can invoke it
/// by reflection to invoke the original method. null if fails.
/// \note This function will
/// automatically generate a stub class for hook. To help debug, you can set the generated class
/// name, its field name, its source name and its method name by setting generated_* in \ref
/// InitInfo.
/// \note This function thread safe (you can call it simultaneously from multiple thread)
/// but it's not atomic to the same \b target_method. That means \p UnHook or \p IsUnhook does
/// not guarantee to work properly on the same \p target_method before it returns. Also,
/// 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,
/// but it's not atomic to the same \b target_method. That means #UnHook() or #IsUnhook() does not
/// guarantee to work properly on the same \p target_method before it returns. Also, 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);
@ -106,7 +109,7 @@ struct InitInfo {
/// \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,
[[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
@ -115,8 +118,7 @@ struct InitInfo {
/// \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.
@ -125,13 +127,14 @@ struct InitInfo {
/// 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
/// callee and that can be hardly achieve (but you can still search all callers by using DexHelper).
/// 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,
[[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
@ -141,15 +144,15 @@ struct InitInfo {
/// \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);
/// \brief Make a class inheritable. It will make the class non-final and make all its private
/// constructors protected.
/// \param[in] env The Java environment.
/// \param[in] target The target class that is to make inheritable.
/// \return Indicate whether the operation has succeed.
[[nodiscard]] [[maybe_unused]] [[gnu::visibility("default")]] bool MakeClassInheritable(
JNIEnv *env, jclass target);
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool MakeClassInheritable(JNIEnv *env,
jclass target);
} // namespace v1
} // namespace lsplant

View File

@ -33,7 +33,6 @@ using art::Instrumentation;
using art::Thread;
using art::gc::ScopedGCCriticalSection;
using art::jit::JitCodeCache;
using art::mirror::Class;
using art::thread_list::ScopedSuspendAll;
namespace {
@ -227,10 +226,6 @@ bool InitNative(JNIEnv *env, const HookHandler &handler) {
LOGE("Failed to init class linker");
return false;
}
if (!Class::Init(env, handler)) {
LOGE("Failed to init mirror class");
return false;
}
if (!Instrumentation::Init(handler)) {
LOGE("Failed to init instrumentation");
return false;