Say I have an enum class defined like this (adapted from the java documentation)
package com.example.planetExample;
public enum Planet {
MERCURY (3.303e+23, 2.4397e6){
public double surfaceGravity() {
return 42;
}
},
VENUS (4.869e+24, 6.0518e6);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
}
Now, I want to use XPosed hook the surfaceGravity
function that has been overriden by MERCURY (and not the general one defined below). How can I get access to that function?
I tried findAndHookMethod("com.example.planetExample.Planet", lpparam.classLoader, "surfaceGravity", [etc])
, but that one only hooks the general surfaceGravity defined by the Planet class, and not the one defined by MERCURY. If I try com.example.planetExample.Planet$MERCURY
or com.example.planetExample.Planet.MERCURY
, I'm getting errors from XPosed that the function surfaceGravity
could not be found.
Is there a way to hook this function using XPosed?
MERCURY is a field of the Planet Enum. Since it has its own implementation, a class will be generated at compile time for it, unfortunately its name won't match the field's name (e.g., in your case it will likely be com.test.Planet$1).
Consider the following example:
public static void main(String[] args) {
System.out.println("Mercury radius: " + Planet.MERCURY.surfaceGravity()); // 42
System.out.println("Planet class: " + Planet.class.getName()); //prints "com.test.Planet"
try {
Class<?> planet_cls = Class.forName("com.test.Planet");
System.out.println(Planet.class); // com.test.Planet
System.out.println(Planet.MERCURY.getClass()); // com.test.Planet$1
System.out.println(Planet.VENUS.getClass()); // com.test.Planet
for(Class c: Planet.class.getDeclaredClasses())
System.out.println("Name:" + c.getName()); // wont print
for(Field c: Planet.class.getDeclaredFields())
System.out.println("Field Name:" + c.getName()); // MERCURY & VENUS :)
try {
Field mercury_field = planet_cls.getDeclaredField("MERCURY");
Object o = mercury_field.get(null);
System.out.println("Field class name: " + o.getClass()); // com.test.Planet$1
try {
Method surfaceGravity = o.getClass().getDeclaredMethod("surfaceGravity");
System.out.println("Confirm result: " + surfaceGravity.invoke(o)); // 42!
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
So by retrieving the field by name, you can get its class and methods. Note that in Xposed you will have another hooking api that receives the method instead of looking for it by name, just pass it the method in the example.
The output of that code to save you some time:
Mercury radius: 42.0
Planet class: com.test.Planet
class com.test.Planet
class com.test.Planet$1
class com.test.Planet
Field Name:MERCURY
Field Name:VENUS
Field Name:mass
Field Name:radius
Field Name:G
Field Name:$VALUES
Field class name: class com.test.Planet$1
Confirm result: 42.0