From 3132a66692b70495680b63a262847849eea59780 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 18 Feb 2022 10:01:40 +0800 Subject: [PATCH] Allow customize generated class --- library/jni/include/lsplant.hpp | 16 +++++++++++++- library/jni/lsplant.cc | 39 ++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/library/jni/include/lsplant.hpp b/library/jni/include/lsplant.hpp index 0ef7474..a000063 100644 --- a/library/jni/include/lsplant.hpp +++ b/library/jni/include/lsplant.hpp @@ -35,9 +35,20 @@ struct InitInfo { InlineUnhookFunType inline_unhooker; /// \brief The symbol resolver to \p libart.so. Must not be null. ArtSymbolResolver art_symbol_resolver; + + /// \brief The generated class name. Must not be empty. It contains a field and a method + /// and they could be set by \p generated_field_name and \p generated_method_name respectively. + std::string_view generated_class_name = "LSPHooker_"; + /// \brief The generated source name. Could be empty. + std::string_view generated_source_name = "LSP"; + /// \brief The generated field name. Must not be empty. + std::string_view generated_field_name = "hooker"; + /// \brief The generated class name. Must not be emtpy. If {target} is set, + /// it will follows the name of the target. + std::string_view generated_method_name = "{target}"; }; -/// \brief Initialize LSPlant for procceding hook. +/// \brief Initialize LSPlant for the proceeding hook. /// It mainly prefetch needed symbols and hook some functions. /// \param[in] env The Java environment. Must not be null. /// \param[in] info The information for initialized. \ref InitInfo. @@ -71,6 +82,9 @@ bool Init(JNIEnv *env, const InitInfo &info); /// 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. /// \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, diff --git a/library/jni/lsplant.cc b/library/jni/lsplant.cc index 32e88a6..3934798 100644 --- a/library/jni/lsplant.cc +++ b/library/jni/lsplant.cc @@ -76,6 +76,31 @@ jclass in_memory_class_loader = nullptr; jmethodID in_memory_class_loader_init = nullptr; jmethodID load_class = nullptr; +std::string generated_class_name; +std::string generated_source_name; +std::string generated_field_name; +std::string generated_method_name; + +bool InitConfig(const InitInfo &info) { + if (info.generated_class_name.empty()) { + LOGE("generated class name cannot be empty"); + return false; + } + generated_class_name = info.generated_class_name; + if (info.generated_field_name.empty()) { + LOGE("generated field name cannot be empty"); + return false; + } + generated_field_name = info.generated_field_name; + if (info.generated_method_name.empty()) { + LOGE("generated method name cannot be empty"); + return false; + } + generated_method_name = info.generated_method_name; + generated_source_name = info.generated_source_name; + return true; +} + bool InitJNI(JNIEnv *env) { auto executable = JNI_FindClass(env, "java/lang/reflect/Executable"); if (!executable) { @@ -204,20 +229,18 @@ BuildDex(JNIEnv *env, jobject class_loader, static_cast(param))); } - // TODO(yujincheng08): customize it - ClassBuilder cbuilder{ dex_file.MakeClass("LspHooker_") }; - // TODO(yujincheng08): customize it - cbuilder.set_source_file("LSP"); + ClassBuilder cbuilder{ dex_file.MakeClass(generated_class_name) }; + if (!generated_source_name.empty()) cbuilder.set_source_file(generated_source_name); auto hooker_type = TypeDescriptor::FromClassname(hooker_class.data()); - // TODO(yujincheng08): customize it - auto *hooker_field = cbuilder.CreateField("hooker", hooker_type) + auto *hooker_field = cbuilder.CreateField(generated_field_name, hooker_type) .access_flags(dex::kAccStatic) .Encode(); auto hook_builder{ cbuilder.CreateMethod( - method_name.data(), Prototype{ return_type, parameter_types }) }; + generated_method_name == "{target}" ? method_name.data() : generated_method_name, + Prototype{ return_type, parameter_types }) }; // allocate tmp first because of wide auto tmp{ hook_builder.AllocRegister() }; hook_builder.BuildConst(tmp, static_cast(parameter_types.size())); @@ -420,7 +443,7 @@ using ::lsplant::IsHooked; [[maybe_unused]] bool Init(JNIEnv *env, const InitInfo &info) { - bool static kInit = InitJNI(env) && InitNative(env, info); + bool static kInit = InitConfig(info) && InitJNI(env) && InitNative(env, info); return kInit; }