EzXHelper Documentation

repository·3.x·Indexed 20 days ago

https://github.com/kyuubiran/ezxhelper

A utility library designed to simplify Xposed module development. It provides an ergonomic DSL for method hooking and reflection, supporting Xposed API 82 and 101. The library includes modular components such as core, xposed-api-82, xposed-api-101, and android-utils. Key features include ConstructorFinder, FieldFinder, and MethodFinder for advanced reflection and filtering, as well as ClassHelper and ObjectHelper for manipulating classes and static objects.

Tokens
8.4K
Snippets
29
Records
31
Agent score
69%

What's inside EzXHelper

  1. Install EzXHelper dependencies

    3.x

    EzXHelper is modular. You must include the core library, and then choose the appropriate Xposed API compatibility layer and optional utilities based on your target environment.

    Modules:

    • core: The main library.
    • xposed-api-82: For Xposed API 82 compatibility.
    • xposed-api-101: For Xposed API 101 compatibility.
    • android-utils: (Optional) Provides Android-specific utility extensions.
    // build.gradle
    dependencies {
        def ezxhelperVersion = '<version>'
        implementation "io.github.kyuubiran.ezxhelper:core:$ezxhelperVersion"
        implementation "io.github.kyuubiran.ezxhelper:xposed-api-82:$ezxhelperVersion"
        implementation "io.github.kyuubiran.ezxhelper:android-utils:$ezxhelperVersion"
    }
    // build.gradle.kts
    dependencies {
        val ezxhelperVersion = "<version>"
        implementation("io.github.kyuubiran.ezxhelper:core:$ezxhelperVersion")
        implementation("io.github.kyuubiran.ezxhelper:xposed-api-82:$ezxhelperVersion")
        implementation("io.github.kyuubiran.ezxhelper:android-utils:$ezxhelperVersion")
    }
  2. Initialize EzXHelper for Xposed API 101

    3.x

    If you are using the xposed-api-101 module, initialize the library in the corresponding lifecycle methods provided by the API 101 interface.

    override fun onModuleLoaded(param: ModuleLoadedParam) {
        EzXposed.initOnModuleLoaded(this, param)
    }
    
    @RequiresApi(Build.VERSION_CODES.Q)
    override fun onPackageLoaded(param: PackageLoadedParam) {
        EzXposed.initOnPackageLoaded(param)
    }
    
    override fun onPackageReady(param: PackageReadyParam) {
        EzXposed.initOnPackageReady(param)
    }
  3. Initialize EzXHelper for Xposed API 82

    3.x

    When using the xposed-api-82 implementation, initialize the library within your handleLoadPackage method. You can also optionally initialize it in initZygote.

    override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
        // ...
        EzXposed.initHandleLoadPackage(lpparam)
    }
    
    // Optional
    override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
        EzXposed.initZygote(startupParam)
    }
  4. How HookFactory DSL works for Xposed hooking

    3.x

    The HookFactory provides a Kotlin DSL to simplify hooking methods and constructors using the Xposed API. Instead of manually implementing XC_MethodHook, you use a builder-style pattern to define behavior before or after the target execution.

    Core DSL Operations:

    • before(callback): Executes logic before the method/constructor is called.
    • after(callback): Executes logic after the method/constructor has finished.
    • replace(callback): Replaces the method's return value by providing a new result via the callback. This is a wrapper around before.
    • returnConstant(constant): Forces the method to return a specific constant value. This is a wrapper around before.
    • interrupt(): Forces the method to return null.
      • WARNING: This may cause exceptions if the target method is defined to return a non-nullable type.

    All these operations are applied to a Method or Constructor and return an XC_MethodHook.Unhook object, which can be used to remove the hook later.

    // Example of using the DSL to hook a method
    myMethod.createHook {
        before = IMethodHookCallback { param -> 
            // logic before
        }
        replace { param -> 
            // return a new value
            "new value"
        }
    }
  5. How HookFactory stages work together

    3.x

    The HookFactory uses a HookChain to compose multiple execution stages into a single interceptor. When a method is hooked, the stages are executed in the order they were added to the factory:

    1. Before Stages: Executed first. They allow inspecting or modifying arguments before the original call.
    2. After Stages: Executed after the original method call. They allow inspecting the original return value.
    3. Replace/Interrupt Stages: These determine what the final return value of the intercepted method will be. If replace or returnConstant is used, the original method's result is superseded by the provided value.

    This allows for complex logic where you might want to log an entry (before), modify the result based on some condition (replace), and then log the exit (after).

  6. Search fields in superclasses

    3.x

    By default, FieldFinder.fromClass(clazz) only looks at declaredFields (fields defined in that specific class). To search through the class hierarchy, use findSuper.

    findSuper traverses up the superclass chain until it reaches Any::class.java or until a provided predicate returns true. It collects all declaredFields from each level of the hierarchy into the finder.

    // Search for a field named 'id' in the class OR any of its superclasses
    val field = FieldFinder.fromClass(ChildClass::class.java)
        .findSuper()
        .filterByName("id")
        .first()
  7. Use HookFactory DSL to hook methods and constructors

    3.x

    The HookFactory provides a Kotlin DSL for defining hook behaviors for Java Method or Constructor objects using the Xposed API 101. You can define a chain of execution stages including before, after, and replace logic.

    Available DSL Stages

    • before { param -> ... }: Executes logic before the target method/constructor is invoked. Receives a HookParam object.
    • after { param -> ... }: Executes logic after the target method/constructor has been invoked. Receives a HookParam object.
    • replace { param -> return value }: Replaces the original method result with the value returned by the callback.
    • returnConstant(value): A shorthand to replace the method result with a specific constant.
    • interrupt(): A shorthand to interrupt the method and return null.
    • exceptionMode(mode): Sets the XposedInterface.ExceptionMode for the hook.

    Hooking Methods

    You can use extension functions on Method objects to create various types of hooks:

    • createHook: Full control over the execution chain via a HookFactory block.
    • createBeforeHook: Specifically for before logic.
    • createAfterHook: Specifically for after logic.
    • hook: Low-level intercept using an XposedInterface.Hooker.
    // Example: Hooking a method with a before and after stage
    val targetMethod: Method = ...
    targetMethod.createHook(priority = 10) {
        before { param -> 
            // Do something before invocation
        }
        after { param -> 
            // Do something after invocation
        }
        replace { param -> 
            // Return a custom result
            "new result"
        }
    }
    
    // Example: Simple before hook
    targetMethod.createBeforeHook { param ->
        println("Method called with args: ${param.args}")
    }
  8. Configure the default ClassLoader for reflection

    3.x

    By default, EzXReflection uses ClassLoader.getSystemClassLoader(). If you need to perform reflection using a specific ClassLoader (e.g., the one from the target application), call EzXReflection.init(yourClassLoader) before performing reflection operations.

    // Optional: Set the default ClassLoader for reflection
    EzXReflection.init(yourClassLoader)
  9. Initialize EzXReflection with a custom ClassLoader

    3.x

    If you are using the reflection utilities, it is recommended to call EzXReflection.init(yourClassLoader) before using them. If you do not call this, the library will default to using ClassLoader.getSystemClassLoader().

    // Optional
    // Invoke this before use reflection utils
    // or it will use ClassLoader.getSystemClassLoader() by default.
    EzXReflection.init(yourClassLoader)
  10. Hook methods using EzXHelper

    3.x

    EzXHelper provides a DSL for hooking methods. You can use createHook for a simple before/after pattern, or hook when you need to control the execution chain (e.g., modifying arguments and calling proceed).

    // Using createHook for simple before/after logic
    method.createHook {
        before { param ->
            param.args[0] = "before"
        }
    
        after { param ->
            android.util.Log.i("sample", "result=${param.result}")
        }
    }
    
    // Using hook for manual chain control
    method.hook { chain ->
        val args = chain.args.toTypedArray()
        args[0] = "chain"
        chain.proceed(args)
    }
  11. Hook methods using createHook and hook

    3.x

    EzXHelper provides two primary ways to hook methods:

    1. createHook: A high-level API that uses before and after blocks. This is useful for simple parameter modification or logging.
    2. hook: A lower-level API that provides a chain object, allowing you to manually control the execution flow via chain.proceed().
    // Using createHook for before/after logic
    method.createHook {
        before { param ->
            param.args[0] = "before"
        }
    
        after { param ->
            android.util.Log.i("sample", "result=${param.result}")
        }
    }
    
    // Using hook for manual chain control
    method.hook { chain ->
        val args = chain.args.toTypedArray()
        args[0] = "chain"
        chain.proceed(args)
    }
  12. Initialize a ConstructorFinder

    3.x

    The ConstructorFinder class is a utility for finding and locating constructors within a class or a collection of constructors using sequences. You can initialize it using several static factory methods in the companion object or via extension functions on standard Kotlin/Java types.

    Initialization Methods

    • From a Class: Use fromClass(clazz: Class<*>) or fromClass(kclazz: KClass<*>) to find constructors declared in a specific class.
    • From a Class Name: Use fromClass(clazzName: String, classLoader: ClassLoader) to look up a class by its name string.
    • From Collections: Use fromSequence(seq: Sequence<Constructor<*>>), fromArray(array: Array<Constructor<*>>), fromVararg(vararg array: Constructor<*>), or fromIterable(iterable: Iterable<Constructor<*>>) to wrap existing constructor collections.
    • Extension Functions: If you have the appropriate imports, you can call .constructorFinder() directly on Class<*>, Array<Constructor<*>>, Iterable<Constructor<*>>, or Sequence<Constructor<*>>.
    import io.github.kyuubiran.ezxhelper.core.finder.ConstructorFinder
    
    // Using Class
    val finderFromClass = ConstructorFinder.fromClass(MyClass::class.java)
    
    // Using Class Name
    val finderByName = ConstructorFinder.fromClass("com.example.MyClass")
    
    // Using Extension Functions
    val finderFromExt = MyClass::class.java.constructorFinder()
    
    // Using an Iterable
    val myConstructors: List<Constructor<*>> = // ...
    val finderFromList = myConstructors.constructorFinder()