Xposed Method Hooking
Xposed method hooking to intercept and modify app behavior in your plugins.
Introduction
Xposed method hooking allows your plugin to intercept calls to methods (or constructors) within the application, modify their parameters, change their behavior, or replace their implementation entirely. This is a powerful technique for altering app functionality at a low level.
Hooking Concepts
To hook a method, you need to provide a "hook handler" — a Python class that defines what code to run when the target method is called. The system supports three main ways to interact with a method call.
The Hook Handler Base Classes
For clarity and correctness, you should create your handler by inheriting from one of the abstract base classes provided in base_plugin.py:
MethodHook: Use this when you want to run code before and/or after the original method executes, but still allow the original method to run.MethodReplacement: Use this when you want to completely replace the original method's logic with your own.
The param Object
All hook callback methods receive a param object (de.robv.android.xposed.XC_MethodHook.MethodHookParam) which is your key to interacting with the method call:
param.thisObject: The instance on which the method was called (Nonefor static methods).param.args: A list-like object of the arguments passed to the method. You can read and modify these. Changes made inbefore_hooked_methodwill affect the original call.param.getResult(): The value returned by the original method. Available inafter_hooked_method. You can read and modify this.param.method: Ajava.lang.reflect.Memberobject representing the hooked method or constructor.
A special and very useful feature is param.setResult(new_result). If you set this in before_hooked_method, the original method and any after_hooked_method logic will be skipped entirely. If you want (and it is possible) for the method to return a null result, do param.setResult(None).
Reference: LSPosed XC_MethodHook.java
Filters
You can set filters to control whether your hook callback methods execute. You use filters by applying the @hook_filters decorator to your before_hooked_method or after_hooked_method.
base_plugin.HookFilter:
RESULT_IS_NULL: check if the result isnull.RESULT_IS_TRUE: check if the result istrue.RESULT_IS_FALSE: check if the result isfalse.RESULT_NOT_NULL: check ifresult != null.ResultIsInstanceOf(clazz): check ifresult instanceof clazz.ResultEqual(value): check ifresult.equals(value).ResultNotEqual(value): check if!result.equals(value).ArgumentIsNull(index): check ifparam.args[index] == null.ArgumentNotNull(index): check ifparam.args[index] != null.ArgumentIsFalse(index): check ifparam.args[index] == false.ArgumentIsTrue(index): check ifparam.args[index] == true.ArgumentIsInstanceOf(index, clazz): check ifparam.args[index] instanceof clazz.ArgumentEqual(index, value): check ifparam.args[index].equals(value).ArgumentNotEqual(index, value): check if!param.args[index].equals(value).Condition(condition, object: Any = None): A MVEL expression. (e.g.,"param.args[0] == 1"or"param.args[0] == object"ifobjectis provided to filter function)Or(*filters): check if at least one of the filters is true.
Examples of usage filters
The Hooking Process (Step-by-Step)
1. Find the Target Method or Constructor
First, you need a reference to the java.lang.reflect.Method or java.lang.reflect.Constructor you want to hook. This is done using Java reflection.
2. Implement the Hook Handler
Create a Python class that inherits from MethodHook or MethodReplacement and implements the required callback(s).
3. Apply the Hook
From your BasePlugin class, instantiate your handler and call self.hook_method().
4. Hooking Multiple Methods/Constructors
If you need to apply the same hook to all methods with a specific name within a class, or to all of a class's constructors, you can use these convenient helper methods.
self.hook_all_methods(hook_class, method_name, xposed_hook, priority): Hooks all methods with the givenmethod_nameinhook_class.self.hook_all_constructors(hook_class, xposed_hook, priority): Hooks all constructors inhook_class.
These methods return a list of Unhook objects, one for each method/constructor that was hooked.
5. Unhooking Methods
Hooks are automatically removed when your plugin is disabled or unloaded. However, if you need to remove a hook manually, you can call self.unhook_method() and pass it the Unhook object that was returned by the original hook_method() call.
If you used hook_all_methods or hook_all_constructors, you would iterate through the returned list and call unhook_method for each item if you need to manually unhook them.
Practical Examples
Example 1: Modifying Arguments (Before Hook)
Let's modify every "Toast" message to add a prefix.
Example 2: Changing the Return Value (After Hook)
This example hooks BuildVars.isMainApp() and makes it always return False.
Example 3: Skipping the Original Method and return custom value (Before Hook)
This example hooks AndroidUtilities.formatFileSize(size) and skips the original method if the size is less than 1024. (This is a simplified example, you can add more conditions and logic.)
Example 4: Replacing a Method (MethodReplacement)
This example completely disables a specific internal logging method to reduce logcat spam.
Return Values in MethodReplacement
When using MethodReplacement, your Python replace_hooked_method is the new implementation. You are responsible for returning a value of the correct type.
- For
voidJava methods,returnorreturn None. - For methods returning primitives (e.g.,
int,boolean), return a standard Pythonintorbool. - For methods returning objects (e.g.,
String), return a compatible Python object orNone(which becomesnullin Java).