一、什么是Hook
Hook翻译成中文就是勾子的意思,在java中它表示在事件到达终点前进行拦截或监控的一种行为。
二、Hook的实现
实现hook我们必须要知道java的反射和动态代理。
1、反射
反射是java的查看、检测、修改自身的一种行为。
在编译阶段,编译器将我们编写的java文件编译成.class文件。而在运行期,jvm又将.class文件通过类加载器ClassLoader加载一个类对应的Class对象到内存当中。通过修改Class对象,达到我们查看、检测、修改自身的行为。
Class clazz = Class.forName("android.com.test.TestClass"),方式加载类的class文件。
生成实例的方法不一定通过new ,可通过下面的方法获取Class对象对应的实例
Constructor constructor = clazz.getConstructor();
TestClass o = (TestClass)constructor.newInstance();
当然,构造函数可能有多个,你应该通过获取他的所有构造函数。了解到他的构造函数之后再使用上面的方法,不然会报错
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors){
constructor.setAccessible(true); //可能有些构造函数你的权限不够,让其可能反射
Class[] params = constructor.getParameterTypes(); //某个构造函数的参
Log.i("tag",String.valueOf(params.length)); //看看构造函数参数的长度
for (Class p : params) {
Log.i("tag","参数类型的名字"+p.getName()); //看看这个构造函数是什么类型
}
}
}
同理,通过class类对象,可以获取对象的方法。动态代理中需要在方法拦截。
Methods method[] = clazz.getDeclaredMethods();
获取对象的域,
Field[] fields = clazz.getDeclaredFields()
获取对象的注解,我们ButterKnife就是运用到了这个。
Annotation[] annotations = hookUserClass.getDeclaredAnnotations();
for (Annotation annotation : annotations){
Log.i("tag","注解类型class文件的名字"+annotation.annotationType().getName());
}
2、动态代理
动态代理相对静态代理而言,动态代理是动态的,通过反射对被代理对象的方法,代理成代理对象对应的方法。动态代理也运用到了反射的知识。
final Leader leader = new Leader(new Worker());
IPerson IpensonImp = (IPerson) Proxy.newProxyInstance(Leader.class.getClassLoader(),new Class[]{IPerson.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(leader,args);
return proxy;
}
});
Field[] fields = clazz.getClass().getDeclaredFields();
for (Field field : fields){
field.setAccessible(true);
if (field.getName().equals("iPerson")){
try {
field.set(clazz,IpensonImp);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
break;
}
}
hookUser.doWhat();
class TestClass{
IPerson iPerson ;
public HookUser(){
iPerson = new Worker();
}
public void doWhat(){
iPerson.say("我是工人");
}
}
public class Leader implements IPerson{
IPerson iPerson;
public Leader(IPerson iPerson) {
this.iPerson = iPerson;
}
@Override
public void say(String what) {
PrintUtils.print("我领导,我叫我去说");
iPerson.say(what);
PrintUtils.print("事情干完了");
}
@Override
public void walk(String where) {
PrintUtils.print("我领导,我叫我手走过去");
iPerson.walk(where);
PrintUtils.print("事情干完了");
}
}
public class Worker implements IPerson {
@Override
public void say(String what) {
PrintUtils.print(what);
}
@Override
public void walk(String where) {
PrintUtils.print(where);
}
}
三、Hook的条件
如上面代码,运用反射和动态代理,可实现将TestClass中 IPerson iPerson 指向的Worker对象,动态修改为Leader对象,实现了hook的过程。那么实现hook需要什么条件呢?
1、代理对象和被代理对象需要实现同样的接口,Leader和Worker都是iPerson的实现类。
2、被代理对象必须在使用的时候,使用接口作为类型。否则不能被动态代理。 field.set(clazz,IpensonImp)会报错,类似设置错误。
3、此案例中hook的对象是TestClass。一次修改后被系统回收后,hook就失效了。如果要长期生效,那么hook的生命周期应该是对应响应的生命周期,如应用全局的生命周期,如单例等。