160 lines
9.6 KiB
C++
Raw Normal View History

2022-02-16 07:24:35 +08:00
#pragma once
#include <jni.h>
2022-02-16 07:24:35 +08:00
#include <string_view>
2022-02-16 16:25:05 +08:00
/// \namespace namespace of LSPlant
2022-02-16 07:24:35 +08:00
namespace lsplant {
2022-02-16 16:25:05 +08:00
inline namespace v1 {
/// \struct InitInfo
2022-02-18 11:28:57 +08:00
/// \brief Information and configuration that are needed to call #Init()
2022-02-16 07:24:35 +08:00
struct InitInfo {
2022-02-18 10:53:54 +08:00
/// \brief Type of inline hook function.
2022-02-18 11:28:57 +08:00
/// In \ref std::function form so that user can use lambda expression with capture list.<br>
2022-02-16 16:25:05 +08:00
/// \p target is the target function to be hooked.<br>
/// \p hooker is the hooker function to replace the \p target function.<br>
/// \p return is the backup function that points to the previous target function.
/// it should return null if hook fails and nonnull if successes.
2022-02-16 07:24:35 +08:00
using InlineHookFunType = std::function<void *(void *target, void *hooker)>;
2022-02-18 10:53:54 +08:00
/// \brief Type of inline unhook function.
2022-02-18 11:28:57 +08:00
/// In \ref std::function form so that user can use lambda expression with capture list.<br>
2022-02-16 16:25:05 +08:00
/// \p func is the target function that is previously hooked.<br>
/// \p return should indicate the status of unhooking.<br>
2022-02-16 07:24:35 +08:00
using InlineUnhookFunType = std::function<bool(void *func)>;
2022-02-18 10:53:54 +08:00
/// \brief Type of symbol resolver to \p libart.so.
2022-02-18 11:28:57 +08:00
/// In \ref std::function form so that user can use lambda expression with capture list.<br>
2022-02-16 16:25:05 +08:00
/// \p symbol_name is the symbol name that needs to retrieve.<br>
/// \p return is the absolute address in the memory that points to the target symbol. It should
/// be null if the symbol cannot be found. <br>
/// \note It should be able to resolve symbols from both .dynsym and .symtab.
2022-02-16 07:24:35 +08:00
using ArtSymbolResolver = std::function<void *(std::string_view symbol_name)>;
2022-02-16 16:25:05 +08:00
/// \brief The inline hooker function. Must not be null.
2022-02-16 07:24:35 +08:00
InlineHookFunType inline_hooker;
2022-02-16 16:25:05 +08:00
/// \brief The inline unhooker function. Must not be null.
2022-02-16 07:24:35 +08:00
InlineUnhookFunType inline_unhooker;
2022-02-16 16:25:05 +08:00
/// \brief The symbol resolver to \p libart.so. Must not be null.
2022-02-16 07:24:35 +08:00
ArtSymbolResolver art_symbol_resolver;
2022-02-18 10:01:40 +08:00
/// \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}";
2022-02-16 07:24:35 +08:00
};
2022-02-18 10:01:40 +08:00
/// \brief Initialize LSPlant for the proceeding hook.
2022-02-16 16:25:05 +08:00
/// It mainly prefetch needed symbols and hook some functions.
/// \param[in] env The Java environment. Must not be null.
2022-02-18 11:28:57 +08:00
/// \param[in] info The information for initialized.
2022-02-16 16:25:05 +08:00
/// Basically, the info provides the inline hooker and unhooker together with a symbol resolver of
/// libart.so to hook and extract needed native functions of ART.
/// \return Indicate whether initialization succeed. Behavior is undefined if calling other
/// LSPlant interfaces before initialization or after a fail initialization.
2022-02-18 11:28:57 +08:00
/// \see InitInfo.
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool Init(JNIEnv *env,
const InitInfo &info);
2022-02-16 07:24:35 +08:00
2022-02-16 16:25:05 +08:00
/// \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.
/// \param[in] env The Java environment. Must not be null.
/// \param[in] target_method The method id to the method you want to hook. Must not be null.
/// \param[in] hooker_object The hooker object to store the context of the hook.
/// The most likely usage is to store the \b backup method into it so that when \b callback_method
/// is invoked, it can call the original method. Another scenario is that, for example,
/// in Xposed framework, multiple modules can hook the same Java method and the \b hooker_object
/// can be used to store all the callbacks to allow multiple modules work simultaneously without
/// conflict.
/// \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
2022-02-18 14:34:11 +08:00
/// invoked instead of the original \p target_method. The signature of the \p callback_method must
2022-03-12 16:10:55 +08:00
/// be:
2022-02-16 16:25:05 +08:00
/// \code{.java}
2022-02-18 14:22:27 +08:00
/// public Object callback_method(Object []args)
2022-03-12 16:10:55 +08:00
/// \endcode
2022-02-16 16:25:05 +08:00
/// 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.
2022-03-12 16:10:55 +08:00
/// args[0] is the this object for non-static methods and there is NOT null this object placeholder
/// for static methods.
2022-02-16 16:25:05 +08:00
/// Extra info can be provided by defining member variables of \p hooker_object.
/// This method must be a method to \p hooker_object.
2022-03-12 16:10:55 +08:00
/// \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.
2022-02-16 16:25:05 +08:00
/// \note This function thread safe (you can call it simultaneously from multiple thread)
2022-03-30 15:03:53 +08:00
/// but it's not atomic to the same \b target_method. That means #UnHook() or #IsHooked() does not
2022-03-12 16:10:55 +08:00
/// 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.
/// \note The behavior of getting the \ref jmethodID of the backup method is undfined.
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] jobject Hook(JNIEnv *env,
jobject target_method,
jobject hooker_object,
jobject callback_method);
2022-02-16 07:24:35 +08:00
2022-02-18 11:28:57 +08:00
/// \brief Unhook a Java function that is previously hooked.
2022-02-16 16:25:05 +08:00
/// \param[in] env The Java environment.
/// \param[in] target_method The target method that is previously hooked.
/// \return Indicate whether the unhook succeed.
2022-02-18 15:22:42 +08:00
/// \note Calling \p backup (the return method of #Hook()) after unhooking is undefined behavior.
/// Please read #Hook()'s note for more details.
2022-02-18 11:28:57 +08:00
/// \see Hook()
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool UnHook(JNIEnv *env,
jobject target_method);
2022-02-16 07:24:35 +08:00
2022-02-18 11:28:57 +08:00
/// \brief Check if a Java function is hooked by LSPlant or not
2022-02-16 16:25:05 +08:00
/// \param[in] env The Java environment.
/// \param[in] method The method to check if it was hooked or not.
/// \return If \p method hooked, ture; otherwise, false.
2022-02-18 15:22:42 +08:00
/// Please read #Hook()'s note for more details.
/// \see Hook()
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool IsHooked(JNIEnv *env, jobject method);
2022-02-16 07:24:35 +08:00
2022-02-18 11:28:57 +08:00
/// \brief Deoptimize a method to avoid hooked callee not being called because of inline
2022-02-16 16:25:05 +08:00
/// \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
2022-03-12 16:10:55 +08:00
/// 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
2022-02-16 16:25:05 +08:00
/// 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.
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool Deoptimize(JNIEnv *env,
jobject method);
2022-02-16 07:24:35 +08:00
/// \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
2022-02-16 16:25:05 +08:00
/// 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.
2022-03-12 16:10:55 +08:00
[[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.
2022-03-12 16:10:55 +08:00
[[nodiscard, maybe_unused, gnu::visibility("default")]] bool MakeClassInheritable(JNIEnv *env,
jclass target);
} // namespace v1
} // namespace lsplant