在这之前,我们需要分析一下封装Java反射方法是为了干什么。
显然在Android第一代加固壳的原理及实现一文中,我们重复地使用到了Java反射来获取类、字段、方法,无一例外,获取它们的最终目的是获取字段值、修改字段值、调用方法。为了实现这些目的,我们写了很臃肿的代码,因为太多重复的反射获取,所以我们可以将这些代码封装起来,形成一个自定义类,这样一来,加固的代码就可以变得轻巧了,且这个封装类以后还可以被重复利用。
分析完了封装目的之后,我们也得到了三个目的:
- 获取字段值
- 设置字段值
- 调用方法(构造方法和非构造方法,我这里统称为方法)
因此,我们只需要针对以上目的,分别实现它们就行了。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class RefInvoke{
public static Object getFieldObject(String className, String field, Object instanceObj){
try{ Class<?> aClass = Class.forName(className); Field declaredField = aClass.getDeclaredField(field); declaredField.setAccessible(true); Object obj = declaredField.get(instanceObj); return obj; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); }
}
public static void setFieldObject(String className, String field, Object instanceObj ,Object fieldValue){ try{ Class<?> aClass = Class.forName(className); Field declaredField = aClass.getDeclaredField(field); declaredField.setAccessible(true); declaredField.set(instanceObj, fieldValue); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }
public static Object invokeMethod(String className, String methodName, Class[] paramTypes, Object instanceObj , Object[] paramValues){ try{ Class<?> aClass = Class.forName(className); Method declaredMethod = aClass.getDeclaredMethod(methodName, paramTypes); declaredMethod.setAccessible(true); Object obj = declaredMethod.invoke(instanceObj, paramValues); return obj; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } }
public static Object creatObject(String className, Class[] paramTypes,Object[] paramValues){ try { Class<?> aClass = Class.forName(className); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(paramTypes); declaredConstructor.setAccessible(true); Object obj = declaredConstructor.newInstance(paramValues); return obj; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } }
}
|
写完这个之后,我也明白了之前写加固代码时的一个问题:为在获取ActivityThread
实例时(get()
、invoke()
),参数对象设置为null了。
因为静态方法、静态字段,这些可以通过类名点出来,不需要类的实例也能操作它们。(Java学艺不精😭)