大家好,又见面了,我是你们的朋友全栈君。
一、什么是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的生命周期应该是对应响应的生命周期,如应用全局的生命周期,如单例等。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/141464.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...