美文网首页反射
Android Hook 技术——反射技术

Android Hook 技术——反射技术

作者: AntCoding | 来源:发表于2019-04-21 21:26 被阅读0次

概要

我们在之前的一篇Android-Framework-Plugin插件话框架-Hook Activity过程 中对 Hook技术 进行过基础讲解,Hook技术 就是以 AOP 切面编程思想去实现的底层代理。
Hook中文名"钩子",它使用的是一种面向切面的编程思想(业内专称 AOP编程思想),主要作用是在事件传递过程中对事件进行拦截、修改、监听,将自身的代码动态性替换进去,当这些方法被调用时,保证执行的是我们代码,已达到我们预期的效果

Hook技术实现

  • 反射技术
  • 动态代理

反射技术

Java反射机制是在运行状态中可获取/修改 类/对象的所有的方法和属性,没有公开私有之分;这种可动态获取/修改类或对象的方法和属性 被称为 JAVA反射机制

反射可实现的功能

  • 可获取并修改类任一的属性值
  • 获取对象的所属类
  • 构造任一类的实例
  • 调用类中任一方法
  • 修改类中任一方法的参数

反射的使用

获取Class

如图所示:

实现方法 使用限制
Object.getClass() 不适用于基础类型,如 int、float 等等
ClassName.class 不适用于无法被Import的类,如private Class 或 Android把一些类加上了 @hide 注解,此类无法被正常调用
Class.forName(包名+类名) 无限制
实例Demo

共有实体类:boy.java

public class Boy {
    int age;
    String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String girlHobby(String mineHobby) {
        String brief = "My name is" + name + ", I'm " + age + "year old.\n";
        String hobby = "我的爱好是" + mineHobby;
        return brief + hobby;
    }
}

私有实体类:girl.java

package com.thtf.leanpackage.plugin_hook.demo;

class Girl {
    int age;
    String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String girlHobby(String mineHobby) {
        String brief = "My name is" + name + ", I'm " + age + "year old.\n";
        String hobby = "我的爱好是" + mineHobby;
        return brief + hobby;
    }
}

主运行程序 HookMian.java

package com.thtf.leanpackage.plugin_hook;


import com.thtf.leanpackage.plugin_hook.demo.Boy;

public class HookMian {
   public static void main(String[] args) {
       //Object.getClass()
       Boy boy = new Boy();
       Class boyClass1 = boy.getClass();
       System.out.println(boyClass1.getCanonicalName());

       //Class Name.class
       Class boyClass2 = Boy.class;
       System.out.println(boyClass2.getCanonicalName());

       //Class.forName(包名+类名)
       try {
           Class boyClass3 = Class.forName("com.thtf.leanpackage.plugin_hook.demo.Boy");
           Class girlClass = Class.forName("com.thtf.leanpackage.plugin_hook.demo.Girl");
           System.out.println(girlClass.getCanonicalName());
           System.out.println(boyClass3.getCanonicalName());
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }
   }
}

通过上面所述的三种方法获取到了Class

通过Class获取类中包含的属性方法

下面我们来列举通过Class的哪些方法获取反射目标类中方法和属性

Class中方法名 使用描述
getDeclaredFields() 获取所有的属性,但不包括从父类继承下来的属性
getDeclaredField(String name) 根据获取name参数指定的属性
getDeclaredMethods() 获取所有的方法,包括 private、public、protected
getDeclaredMethod(String name, Class<?>... parameterTypes) 根据参数name获取特定的方法名,参数parameterTypes代表name方法名中的参数类型,以.class表示,例如String.class,Float.class
getDeclaredConstructors() 返回一个对象数组,反映声明类所有的构造方法
getDeclaredConstructor(Class<?>... parameterTypes) 获取特定的构造方法,参数parameterTypes表示参数类型
getSuperclass() 获取反射目标类的父类
getInterfaces() 获取反射目标类中所有实现的接口
getMethods() 获取自身的所有的 public 方法,包括从父类继承下来的
getMethod(String name, Class<?>... parameterTypes) 根据参数name获取特定的public修饰符下的属性,parameterTypes表示方法传递的参数类型
getFields() 获取自身的所有的 public 属性,包括从父类继承下来的。
getField(String name) 根据参数name获取特定的public修饰符下的属性

Java Demo 案例

package com.thtf.leanpackage.plugin_hook;


import com.thtf.leanpackage.plugin_hook.demo.Boy;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class HookMian {
    public static void main(String[] args) {
        //Object.getClass()
        Boy boy = new Boy();
        Class boyClass1 = boy.getClass();
        System.out.println(boyClass1.getCanonicalName());

        //Class Name.class
        Class boyClass2 = Boy.class;
        System.out.println(boyClass2.getCanonicalName());

        //Class.forName(包名+类名)
        try {
            Class boyClass3 = Class.forName("com.thtf.leanpackage.plugin_hook.demo.Boy");
            Class girlClass = Class.forName("com.thtf.leanpackage.plugin_hook.demo.Girl");
            System.out.println(girlClass.getCanonicalName());
            System.out.println(boyClass3.getCanonicalName());
            //构造器
            Constructor[] boyConstructors = boyClass3.getConstructors();
            for (Constructor c : boyConstructors) {
                System.out.println("getConstructor:" + c.toString());
            }

            boyConstructors = boyClass3.getDeclaredConstructors();
            for (Constructor c : boyConstructors) {
                System.out.println("getDeclaredConstructors:" + c.toString());
            }

            //获取指定方法属性
            Method boyHobby = boyClass3.getDeclaredMethod("boyHobby", String.class);
            Field ageField = boyClass3.getDeclaredField("age");

            System.out.println("获取的方法名:" + boyHobby.getName());
            System.out.println("获取的属性:" + ageField.getName());

            //获取反射类中的公共方法
            Method girlLittleName = girlClass.getMethod("girlLittleName", String.class);
            System.out.println("获取的公共方法名:" + girlLittleName.getName());
            //获取所有方法
            Method[] methods = girlClass.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Declared Method :" + method.getName());
            }
            //获取公共方法
            methods=girlClass.getMethods();
            for (Method method : methods) {
                System.out.println("Public Method :" + method.getName());
            }

            //获取所有的属性
            Field[] filed1 = boyClass3.getDeclaredFields();

            for (Field f : filed1) {
                System.out.println("Declared Field :" + f.getName());
            }
            //获取公共的字段属性
            Field[] filed2 = boyClass3.getFields();

            for (Field f : filed2) {
                System.out.println("Field :" + f.getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

我们通过反射获取的属性对象类型都是Field类型,而获取的方法对象的类型都是Method类型,接下来我们来讲解一下 Field 和 Method都可以触发那些操作.

Field 和 Method 操作解析

  • Field操作通过反射获取的属性,主要作用读取属性值以及为属性赋值
  • Method操作通过反射获取的方法,反射的核心就在于此;通过调取Methodinvoke()实现对原始方法的代理,从而达到反射的目的.
public class HookMian {
    public static void main(String[] args) {
        try {
            //反射获取Class对象
            Class boyClass3 = Class.forName("com.thtf.leanpackage.plugin_hook.demo.Boy");
            //实现反射类中对属性的操作
            realizeField(boyClass3);
            //实现反射类中对方法的操作
            realizeMthod(boyClass3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void realizeField(Class boyClass3) {
        try {
            //实例化反射类对象
            Object boy = boyClass3.newInstance();
            //获取其中一个属性
            Field nameField = boyClass3.getDeclaredField("age");
            //将其状态设置成可访问
            nameField.setAccessible(true);
            //为属性赋值
            nameField.setInt(boy, 15);
            System.out.println(nameField.getInt(boy));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    private static void realizeMthod(Class boyClass) {
        try {
            //实例化
            Object girlInstance = boyClass.newInstance();
            //获取其中的指定的方法
            Method girlHobbyMethod = boyClass.getDeclaredMethod("boyHobby", String.class);
            //设置成可访问状态
            girlHobbyMethod.setAccessible(true);
            //为得到方法传递参数,并获取执行结果
            Object result = girlHobbyMethod.invoke(girlInstance, "11");
            System.out.println("我执行的结果: " + result);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Andorid反射实例

我们以ViewOnClick事件Hook的实例做讲解,我们先来对View.setOnClickListener()做分析讲解:

我们在Activity中为控件设置onClick事件,进入View类中查看setOnClickListener(@Nullable OnClickListener l)源码分析,我们发现参数OnClickListener 最终存储在了静态内部类ListenerInfo类中的mOnClickListener属性

  public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

   ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }
接下来我们对onClick事件进行反射操作:
  1. 首先我们首先在 主程序 中设置控件的事件监听,然后设置我们的Hook的监听事件,当我们触发了控件的onClick事件由于我们将其进行了Hook代理,所以会触发我们的Hook代理操作
public class HookViewClickActivity extends Activity implements View.OnClickListener {
    private Button hook_btn;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.hook_click_layout);
        hook_btn = (Button) findViewById(R.id.hook_btn);
        hook_btn.setOnClickListener(this);
        try {
            HookHelper.hookOnClickListener(hook_btn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.hook_btn:
                Toast.makeText(HookViewClickActivity.this, "OnClick 被正常调用了", Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

  1. 我们来看具体的Hook操作步骤,主要的就是将我们的Proxy替换掉mOnClickListener属性
package com.thtf.leanpackage.view.hook_click;

import android.view.View;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class HookHelper {
    public static void hookOnClickListener(View view) throws Exception {
        // [1] 通过反射获取 ListenerInfo 对象
        Method listenerInfoMethod = View.class.getDeclaredMethod("getListenerInfo");
        listenerInfoMethod.setAccessible(true);
        //获取getListenerInfo函数执行的返回结果
        Object listenerInfoObj = listenerInfoMethod.invoke(view);

        // [2]反射获取到存储OnClickListener事件的属性 mOnClickListener
        Class<?> listenerInfoClazz = Class.forName("android.view.View$ListenerInfo");
        //获取ListenerInfo的属性mOnClickListener
        Field mOnClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener");
        mOnClickListenerField.setAccessible(true);
        //获取mOnClickListener的属性值
        View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListenerField.get(listenerInfoObj);

        // [3]用 Hook代理类 替换mOnClickListener属性
        View.OnClickListener hookedOnClickListener = new HookedClickListenerProxy(originOnClickListener);
        //为mOnClickListener属性 赋值
        mOnClickListenerField.set(listenerInfoObj, hookedOnClickListener);
    }
}

  1. 那么我们在自定义的Proxy类,如何做处理的:
package com.thtf.leanpackage.view.hook_click;

import android.view.View;
import android.widget.Toast;

public class HookedClickListenerProxy implements View.OnClickListener {
    private View.OnClickListener listener;
    public HookedClickListenerProxy(View.OnClickListener listener){
        this.listener=listener;
    }
    @Override
    public void onClick(View v) {
        //自定义操作
        Toast.makeText(v.getContext(),"Hook Click Listener",Toast.LENGTH_SHORT).show();
        if (listener!=null){
            listener.onClick(v);
        }
    }
}

总结

  • 1.获取Class对象是反射机制的入口
  • 2.通过Class对象获取Field,对反射类中属性进行读取或赋值等操作
  • 3.通过Class对象获取Method,并实现Method.invoke()从而实现对反射类中所包含的方法进行 代理操作
此篇文章只对 反射技术 进行讲解,下一篇将着重讲述 动态代理 的实现原理。

This ALL! Thanks EveryBody!

相关文章

网友评论

    本文标题:Android Hook 技术——反射技术

    本文链接:https://www.haomeiwen.com/subject/ctxcgqtx.html