小白也能看懂的插件化DroidPlugin原理(二)– 反射机制与Hook入门

  前言:以高达同首博文《小白也能看懂的插件化DroidPlugin原理(一)–
动态代理》被详细介绍了
DroidPlugin
原理中涉嫌到之动态代理模式,看罢上篇博文后若不怕会见意识原本动态代理真的非常简单,只不过就是是贯彻一个
InvocationHandler 接口重写一下 invoke 方法而已。不错,其实过多像样 high
level
的艺还连没想象中的那晦涩难理解,只要你愿意下定狠心去询问其,去认识它,去读她而便会见发现,原来都是可以学得亮的。本篇博文将介绍
DroidPlugin 框架中时常因此到之另外两单知识点–反射机制及Hook技术。

  本系列文章的代码都上传至github,下载地址:https://github.com/lgliuwei/DroidPluginStudy 本篇文章对应的代码在
com.liuwei.proxy_hook.reflect
和 com.liuwei.proxy_hook.hook.simplehook 包内。

平、反射机制

  1、反射是什么?

  JAVA反射机制是当运转状态被,对于自由一个类似,都能够掌握此近乎的有属性与方法;对于自由一个目标,都能够调用它的自由方法以及总体性;这种动态获取信息以及动态调用对象方法的效用称为java语言的照机制。

  2、反射机制的作用:

  (1)反射可以于运作时判断任意一个对象所属的切近;

  (2)反射可以在运作时组织任意一个近乎的对象;

  (3)反射可以以运作时判断任意一个类有的即兴成员变量和艺术;

  (4)反射可以以运转时调用任意一个近似的随机方法;

  (5)可以经反射机制变动态代理。

  3、Talk is cheap,show me the code. 来同样组反射机制小示例

  首先创建一个类供反射调用测试用,暂且将类名
BeReflected,并在看似中上加点儿只成员变量、三独平常方法、一个静态变量,一个静态方法,具体代码如下:

 1 /**
 2  * 被反射测试的类
 3  * Created by liuwei on 17/4/2.
 4  */
 5 public class BeReflected {
 6     private String field1 = "I am field1";
 7     private String field2 = "I am field2";
 8     private static String staticField = "I am staticField";
 9     private void method1(){
10         Logger.i(BeReflected.class, "I am method1");
11     }
12     private void method1(String param) {
13         Logger.i(BeReflected.class, "I am method1--param = " + param);
14     }
15     private void method2(){
16         Logger.i(BeReflected.class, "I am method2");
17     }
18     public static void staticMethod(){
19         Logger.i(BeReflected.class, "I am staticMethod");
20     }
21 }

  (1)通过反射获取 BeReflected 的Class类型,并拿该初始化。(其中
Logger 是楼主封装的一个日志打印类,无需当全这些细节)

1 // 1、通过反射获取BeReflected所属的类
2 Class<?> beReflectedClass = Class.forName("com.liuwei.proxy_hook.reflect.BeReflected");
3 Logger.i(ReflectTest.class, beReflectedClass);
4 
5 // 2、通过反射创建实例化一个类
6 Object beReflected = beReflectedClass.newInstance();
7 Logger.i(ReflectTest.class, beReflected);

  输出如下:

  [ReflectTest] : class
com.liuwei.proxy_hook.reflect.BeReflected
  [ReflectTest] :
com.liuwei.proxy_hook.reflect.BeReflected@7d4991ad

  (2)通过反射访问私有方法及村办成员变量,并更改私有变量的价。我们都知,对于一个私家类型的变量,在尚未提供公开的
set 之类方法的状况下,想改变它的值是免可能的,但是以反射就可以成功。

 1 // 3、通过反射调用一个私有方法和成员变量
 2 Method method = beReflectedClass.getDeclaredMethod("method1");
 3 method.setAccessible(true);// 将此值设为true即可访问私有的方法和成员变量
 4 method.invoke(beReflected);// 访问普通成员变量和方法是需要在调用invoke方法是传入该类的对象
 5 
 6 Field field1 = beReflectedClass.getDeclaredField("field1");
 7 field1.setAccessible(true);
 8 Logger.i(ReflectTest.class, "field 改变前的值:" + field1.get(beReflected));
 9 field1.set(beReflected, "我是 field1 被改变后的值");
10 Logger.i(ReflectTest.class, "field 改变后的值:" + field1.get(beReflected));

  输出如下:  

  [BeReflected] : I am
method1
  [ReflectTest] : field
改变前的价值:I am field1
  [ReflectTest] : field
改变后的值:我是 field1 被改变后底价

   (3)通过反射访问静态方法和静态变量。访问静态方法和变量时莫待传入所属类的靶子,传入
null 即可访问。代码如下:

1 // 4、通过反射调用一个静态的方法和变量
2 Method staticMethod = beReflectedClass.getDeclaredMethod("staticMethod");
3 staticMethod.invoke(null);
4 
5 Field staticField = beReflectedClass.getDeclaredField("staticField");
6 staticField.setAccessible(true);
7 Logger.i(ReflectTest.class, staticField.get(null));

  输出如下:

  [BeReflected] : I am
staticMethod
  [ReflectTest] : I am
staticField

  (4)通过反射访问一个拉动参数的计。访问带参数的措施是,需要在
getDeclareMethod 后面传来一组参数的路。

1 // 5、通过反射访问一个带参数的方法
2 Method method1 = beReflectedClass.getDeclaredMethod("method1", String.class);
3 method1.setAccessible(true);
4 method1.invoke(beReflected, "我是被传入的参数");

  输出如下:

  [BeReflected] : I am
method1–param = 我是给传播的参数

   (5)通过反射获取类吃保有的成员变量和方法。

1 // 6、遍历类中所有的方法和成员变量
2 for (Method tempMethod : beReflectedClass.getDeclaredMethods()) {
3     Logger.i(ReflectTest.class, tempMethod.getName());
4 }
5 for (Field tempField : beReflectedClass.getDeclaredFields()) {
6     Logger.i(ReflectTest.class, tempField.getName());
7 }

  输出如下:

  [ReflectTest] :
method2
  [ReflectTest] :
method1
  [ReflectTest] :
method1
  [ReflectTest] :
staticMethod
  [ReflectTest] :
field1
  [ReflectTest] :
field2
  [ReflectTest] :
staticField

  看罢上面几乎独例证之后,你是无是当反射还算神奇,可以好多之所以常规方式做不至的操作。当然者只是示例了映机制中最为基本的一对调用而已,感兴趣之爱人可自动查阅官方文档。废话不多说了,我们尽快上马介绍
Hook 技术。

二、Hook入门

  Hook 中文释意是“钩子”,这点儿上楼主为一直以镂,Hook
到底指的凡呀?如何才能够因此同样种植简易好掌握,生动形象的解释来提现 Hook
技术?以楼主目前针对 Hook
的了解,通俗来以就是经过某种手段对同起事物进行偷梁换柱,从而劫持目标来坐高达控制目标的一言一行的目的。从技术角度来说,就是替换原有的对象,拦截目标函数/方法,从而改变该原始的表现。

  以3月份新刚开念 Hook
技术时写了一个关于替换汽车发动机的小例子,今天虽将此例子贴出吧。先说一下大体流程,首先我们会发生一个简练的汽车类,汽车类里面来个引擎的目标,当然,汽车发动机都是有标准的(这里就是为接口),为简易起见,我们这边的汽车发动机标准且只生一个无限充分快之指标,后续我们见面透过反射机制来替换掉汽车引擎以达成提高极端老快的目的。例子非常简单,通过这例子我们那个容易就能够开的敞亮
Hook 技术。

  汽车类代码如下:

 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class Car {
 5     private CarEngineInterface carEngine;
 6     public Car() {
 7         this.carEngine = new CarEngine();
 8     }
 9     public void showMaxSpeed(){
10         Logger.i(Car.class, "我卯足劲,玩命跑的最大速度可以达到:" + carEngine.maxSpeed());
11     }
12 }

  可以看出,汽车类里面有一个 carEngine
(汽车引擎)的习性,汽车引擎接口代码如下:

1 /**
2  * 车引擎接口
3  * Created by liuwei on 17/3/1.
4  */
5 public interface CarEngineInterface {
6     int maxSpeed();
7 }

  汽车引擎类代码如下:

/**
 * 车引擎
 * Created by liuwei on 17/3/1.
 */
public class CarEngine implements CarEngineInterface {
    @Override
    public int maxSpeed() {
        return 60;
    }
}

  一个简便的轿车搞定了,试走一下:

1 public class Test {
2     public static void main(String[] args) {
3         Car car = new Car();
4         car.showMaxSpeed();
5     }
6 }

  输出结果:[Car] :
我卯足劲,玩命跑的绝老快可高达:60

  额…好吧,卯足劲才会走至60,这发动机速度发出点….,作为一个飙车党,肯定不可知忍心,必须改装!

  以改装之前,我们要事先考察从何下手合适,可以看看,在 Car
类里面来个 CarEngine 的对象,我们需要举行的即使是用这 CarEngine
的目标替换成我们温馨创办的引擎类,这个引擎类需要出就跟 CarEngine
一样的特色,也就是说要实现 CarEngineInterface 接口或者直接接轨
CarEngine ,然后拦截到 maxSpeed
方法并修改返回值。那么这里我们实际上生零星栽方案,一栽方案,可以重新创设一个引擎类,让那继续
CarEngine 或者实现 CarEngineInterface 都履行,然后经反射来替换 Car
对象吃的 carEngine 属性;另一样种植方案,写一个动态代理,让那对 CarEngine
进行代理,然后用反射替换。

  先是种方案:

  首先创建一个 EvilCarEngine 类, 详细代码如下:

 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class EvilCarEngine extends CarEngine {
 5     private CarEngineInterface base;
 6     public EvilCarEngine(CarEngineInterface base) {
 7         this.base = base;
 8     }
 9     public int maxSpeed() {
10         return 3 * base.maxSpeed();
11     }
12 }

  然后就此反射机制替换掉原来的汽车发动机。

 1 public class Test {
 2     public static void main(String[] args) {
 3         Car car = new Car();
 4         Logger.i(Test.class, "------------------替换前----------------");
 5         car.showMaxSpeed();
 6         // 怎样在不手动修改CarEngine类和Car类的情况下将大速度提高?
 7         try {
 8             Field carEngineField = Car.class.getDeclaredField("carEngine");
 9             carEngineField.setAccessible(true);
10             CarEngine carEngine = (CarEngine)carEngineField.get(car);
11             // 方法1
12             carEngineField.set(car, new EvilCarEngine(carEngine));
13         } catch (Exception e) {
14             e.printStackTrace();
15         }
16         Logger.i(Test.class, "------------------替换后----------------");
17         car.showMaxSpeed();
18     }
19 }

   输出结果:

  [Test] :
——————替换前—————-
  [Car] :
我卯足劲,玩命跑的无比充分快可达成:60
  [Test] :
——————替换后—————-
  [Car] :
我卯足劲,玩命跑的最好老快可达标:180

  亚种植方案:

  首先创建一个动态代理类,并阻挠 maxSpeed 方法,修改返回值。

 1 /**
 2  * Created by liuwei on 17/3/1.
 3  */
 4 public class CarEngineProxyHandler implements InvocationHandler {
 5     private Object object;
 6     public CarEngineProxyHandler(Object object) {
 7         this.object = object;
 8     }
 9     @Override
10     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
11         if ("maxSpeed".equals(method.getName())) {
12             Logger.i(CarEngineProxyHandler.class, "我是动态代理,我已拦截到 maxSpeed 方法,并偷偷返回了另一个值!");
13             return 180;
14         }
15         return method.invoke(object, args);
16     }
17 }

   同理,利用反射替换掉原来的汽车引擎

 1 public class Test {
 2     public static void main(String[] args) {
 3         Car car = new Car();
 4         Logger.i(Test.class, "------------------替换前----------------");
 5         car.showMaxSpeed();
 6         // 怎样在不手动修改CarEngine类和Car类的情况下将大速度提高?
 7         try {
 8             Field carEngineField = Car.class.getDeclaredField("carEngine");
 9             carEngineField.setAccessible(true);
10             CarEngine carEngine = (CarEngine)carEngineField.get(car);
11             // 方法2
12             CarEngineInterface carEngineProxy = (CarEngineInterface) Proxy.newProxyInstance(
13                     CarEngine.class.getClassLoader(), 
14                     new Class[]{CarEngineInterface.class}, 
15                     new CarEngineProxyHandler(carEngine));
16             carEngineField.set(car, carEngineProxy);
17 
18         } catch (Exception e) {
19             e.printStackTrace();
20         }
21 
22         Logger.i(Test.class, "------------------替换后----------------");
23         car.showMaxSpeed();
24     }
25 }

  输出结果跟方案一一致。

  写及这边,Hook 的核心用法也已勾勒了了,看了例子之后,或许你曾针对
Hook 有矣一个基本的认识,但值得一提的凡,在 Test
类中之第10行代码中我们先是取出了 Car 中的 carEngine
对象,然后用这目标传入了她的牺牲品中,为什么要如此做的,在替身中莫传播
carEngine 或者再次 new 一个新的 CarEngine
不行啊?这是一个关键点,我们用了解的是,这里我们只是怀念修改一下发动机的无限酷快,而并无盼引擎的旁属性被震慑,我们拿于
Car 中取出原有的 carEngine
对象传入替身中,这样替身就足以就选取我们关注的办法进行修改,对于咱们无思改的点子直接调用传经来的
carEngine
对法即可。因为这里的例证为了简单从表现没增长其它的方以及特性,所以就或多或少亟需重点说明一下。

三、小结

  其实 Hook 技术简单来说可就此替换、拦截来写,并没有行使新技巧。Hook
本身并无麻烦,它的难题在于你以对平段子代码 Hook 之前用找有一个相宜的 Hook
点,也就是说分析产生由哪下手充分重大,这虽要求而对将要 Hook
的对象代码的施行流程很熟悉。本篇博文只是开端认识一下 Hook
技术,下一致篇博文将会晤介绍如何通过 Hook 技术阻碍 Android 中 startActivity
方法,并于解析的长河被介绍如何才是方便的 Hook
点。感兴趣之情侣可以关心一下,敬请期待!

正文地址:http://www.cnblogs.com/codingblock/p/6642476.html