Tips for writing Xposed Module to Hook Android App’s Methods

Hi, Today I’m giving some tips to use while hooking methods of an android app with Xposed. I assume that you have installed xposed framework and the installer on your android device and knows the basics of writing an Xposed module using Android Studio.

Avoiding ClassCastException

This exception could occur when you hook a method or a consturctor and try to cast the params of the method or the return object. By default the both param args and results (which are accessible inside beforeHookedMethod(MethodHookParam param)) are  instances of java.lang.Object. If you try to cast this object using an external java package which includes this method you may get an exception like follows:

java.lang.ClassCastException: okhttp3.Response cannot be cast to okhttp3.Response 

Reason: The java Object instances return for param.args and param.getResults are loaded by the class loader of the android app. However, if you try to cast that object to its type using a class from an external jar of android module (class loaded by the android module), it cannot be casted due to different class loaders.

Solutions:

Therefore, instead of trying to cast, we can use some methods provided by the Xposed API (de.robv.android.xposed.XposedHelpers class).

Tip 1:

You can create a new instance of a particular class if you know the constructor arguments using XposedHelpers.newInstance method. So identify a class constructor which takes the param.args and/or param.getResults as method arguments. The output of newInstance is an Object. This Object can be cast to the class from the external jar.

XposedHelpers.newInstance

argument1 The class type which you require to instantiate. Here, you can specify the class name from the external jar.
argument2 The arguments for the constructor call. They are either param.args or param.getResults object instances.
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        Object x = XposedHelpers.newInstance(okhttp3.Response.class, param.getResults());
    }

Tip 2:

Instead of casting the object, you can access fields of the real class type of that object.  Here, the method I hook returns an instance of class type “okhttp3.internal. http.RealResponseBody”. In this class there are three class fields including “private final BufferedSource source;”. Now, we can get the value of the field with name “source” as a java.lang.Object. If the field you are interested is a primitive type or a String you are lucky. Otherwise, you may require further processing.

XposedHelpers.getObjectField

This method takes two arguments:

argument1: The Object with the required field.                                                                           argument2: The field name as in the real class.

 @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        Object bufferedSource = XposedHelpers.getObjectField(param.getResults(), "source");
        Log.v("xposed1", "BufferedSource:" + bufferedSource );                              
  }
 }

Tip 3:

Without casting the Object, you can also call methods of the real class of the Object instance. For example, the “okio.BufferedSource” interface has many methods including “String readUtf8() throws IOException;”. Now, the return object  “bufferedSource ”  from the above, can be further process using XposedHelpers.call Method(). This method returns the return value of the calle method wrapped in a java.lang.Object instance.

XposedHelpers.callMethod

argument1: The Object with the required method to call.                                                       argument2: The method name.                                                                                               argument3: The arguments for the method call.

However, in this example the method does not have any input arguments. Therefore, only two arguments were added.

@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
     Object bufferedSource = XposedHelpers.getObjectField(param.getResults(), "source");
     Log.v("xposed1", "BufferedSource:" + bufferedSource );
     Object string = XposedHelpers.callMethod(bufferedSource , "readUtf8"); 
     Log.v("xposed1", "BufferedSource readUtf8() " + string.toString());
 }
}

Hope these tips were helpful !

 

Cheers! 🙂

 

 

 

Leave a comment