The basic concept of reflection and Class (a) reflection of class loading

More Android Video Advanced Architecture Advanced Learning Click: https://space.bilibili.com/474380680
This article from the following elements to illustrate reflection and class loader:

  • [Three kinds of ways to obtain the Class object]
  • [Get instance constructor object attribute information]
  • [Configure two methods the Android packaged signature information]
  • [Hook dynamic code injection]

First, the basic concept of reflection Get Class object to the three way

Class class is the root of all reflection.
What Class class represents?
A lot of people - you can define a Person class (age, gender, name, etc.)
a lot of cars - you can define a Car class (engine, color, wheels, etc.)
a lot of class --Class class (class name, structure methods, properties, methods)

Class objects of the class obtained in three ways:
first: getClass Object class () method of
a second: class .class
third: Class class by the forName () method

Why learn reflection?
Can in turn reflected by an object class Class acquire class information (public private attributes, methods, etc.) of the target class;
most javaEE frame of the source is reflective manner, we can pave the way to learn this learning javaEE frame
code is as follows:

import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

public class ReflectionDemo {
    /**
     * 三种获取Class对象的的方式:
     * 1、对象.getClass()
     * 2、类名.class
     * 3、Class.forName("完整的类路径")
     */
    @Test
    public void getClassTest() {

        //第一种:对象.getClass()
        Dog dog = new Dog("wangwang", 2, "yellow");
        Class<? extends Dog> aClass = dog.getClass();
        
        //第二种:类名.class
        Class<Dog> dogClass = Dog.class;

        //第三种:Class.forName("完整的类路径")
        try {
            Class<?> aClass1 = Class.forName("com.vince.Dog");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

Second, the object instance constructor acquiring attribute information

code show as below:

import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

public class ReflectionDemo {
    /**
     * 通过反射来实例化目标类对象
     */
    @Test
    public void test1(){
        Class<Dog> dogClass = Dog.class;
        try {
            //通过Class对象(dogClass)来实例化类对象,调用了该类默认的无参的构造方法(所以创建Dog类的时候无参的构造方法要保留呢);此时可以正常使用dog对象了
            Dog dog = ((Dog) dogClass.newInstance());  //所以这里是没有参数的newInstance()
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }


    @Test
    public void test2(){
        Class<Dog> dogClass = ( Dog.class);
        //获取目标类的所有构造方法
        Constructor<?>[] constructors = dogClass.getConstructors(); //通过Class对象(dogClass),得到该类Dog的所有构造方法。返回的是一个数组
        for (int i = 0; i <constructors.length ; i++) {
            System.out.println(constructors[i].getName());           //循环遍历,获取每个构造方法的名称
            System.out.println(constructors[i].getParameterCount()); //获取每个构造方法的参数数量
            System.out.println(constructors[i].getParameterTypes()); //获取构造方法的参数类型
        }


        //获取指定的构造器
        try {
            //获取指定的构造器;因为参数要传入原类的属性,所以用String.class 也是一种“类名.class”的方式
            Constructor<Dog> constructor = dogClass.getConstructor(String.class, int.class, String.class);
            //调用带参数的构造器来实例化对象,因为有参所以这里newInstance()需要传入具体的参数
            Dog dog = constructor.newInstance("haha", 3, "white");

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }


    //获取属性
    @Test
    public void test3(){
        Class<Dog> dogClass = Dog.class;
        //获取目标类的所有属性的的一个抽象对象,返回的是一个数组;且这种只能获取公有的属性
        Field[] fields = dogClass.getFields();
        //System.out.println(fields.length);

        //获取私有的以及公有的属性;即所有的属性
        Field[] declaredFields = dogClass.getDeclaredFields();
        //System.out.println(declaredFields.length);

        int len = declaredFields.length;
        for (int i = 0; i <len ; i++) {
            int modifiers = declaredFields[i].getModifiers();   //获取每个属性的修饰符,但是这么获取的是修饰符的整数值(JVM自动给转换了)
            String modifilesName = Modifier.toString(modifiers); //因此可用修饰符的一个类Modifier.toString()方法再转换成字符串
            System.out.println(modifilesName+" "+declaredFields[i].getType()+" "+declaredFields[i].getName());

        }

    }



}

Three, Android are two ways to configure package signature information

Directory structure is as follows:

 
19956127-a5bb524368167636.png
 

There are two ways:

The first, direct configuration:

signingConfigs { 
  debug { 
    storeFile file("app/keystore.properties") 
    storePassword "111111"
    keyAlias "key"
    keyPassword "111111"
  } 
  release { 
    storeFile file("app/keystore.properties") 
    storePassword "111111"
    keyAlias "key"
    keyPassword "111111"
  } 
} 
buildTypes { 
  debug { 
    signingConfig signingConfigs.debug 
  } 
  release { 
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    signingConfig signingConfigs.release 
  } 
}

The second, by reading the file

New keystore.properties file

storeFile=keyStore.jks 
storePassword=123456
keyAlias=encrypt 
keyPassword=123456
build.gradle配置
signingConfigs { 
  // 从keystore.properties文件中读取信息 
  def keystorePropertiesFile = rootProject.file("app/keystore.properties") 
  def keystoreProperties = new Properties() 
  keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 
  debug { 
    println("======== debug mode: set key ========") 
    storeFile file(keystoreProperties['storeFile']) 
    storePassword keystoreProperties['storePassword'] 
    keyAlias keystoreProperties['keyAlias'] 
    keyPassword keystoreProperties['keyPassword'] 
  } 
  release { 
    println("======== release mode: set key ========") 
    storeFile file(keystoreProperties['storeFile']) 
    storePassword keystoreProperties['storePassword'] 
    keyAlias keystoreProperties['keyAlias'] 
    keyPassword keystoreProperties['keyPassword'] 
  } 
} 
buildTypes { 
  debug { 
    signingConfig signingConfigs.debug 
  } 
  release { 
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    signingConfig signingConfigs.release 
  } 
}

Four, Hook dynamic injection Code

Hook mechanism is a callback mechanism, general callback is static, we must write callback interface in advance; and Hook mechanism in Java you can use reflection, for an entry point (usually a member variable), the use of alternative means the code is changed at runtime, it sounds a bit abstract, following a brief introduction, and then I look at the code.

Looking for Hook point, it should be a member variable, and its method should call the method we need to inject in too, or use its value;
creating a subclass inherits from Hook Point objects, modified according to the needs of its corresponding method;
a reflective target object instance to replace an object to create our own, to achieve the purpose of the substitution.

public class Hero {
  private Weapon weaponMain;

  public Hero(Weapon weaponMain) {
    this.weaponMain = weaponMain;
  }

  public void attack(){
     weaponMain.attack();
  }
}

public class Weapon {
  int damage = 10;

  public void attack(){
    System.out.println(String.format("对目标造成 %d 点伤害",damage));
  }
}

public class Game{
    public static void main(String[] args){
        Hero hero = new Hero(new Weapon());
        hero.attack();
    }
}
//对于上面这段程序,游戏对我们隐藏了Weapon的伤害值,但现在我们想要在每次攻击的时候知道这个伤害值是多少。
//下面看看使用Hook机制如何来实现。

//首先我们通过观察,发现切入点就是weaponMain,我们要对它下手。
//创建一个Weapon的复制品WeaponHook,我们需要用自己的人WeaponHook打入内部。
//WeaponHook一切看起来都和Weapon那么相似,但是我们给它留了一个后门,使得我们可以进行监控。
public class WeaponHook extends Weapon{
  private OnUseWeaponAttackListener onUseWeaponAttackListener;

  @Override
  public void attack(){
    super.attack();
    if (onUseWeaponAttackListener != null){
      onUseWeaponAttackListener.onUseWeaponAttack(damage);
    }
  }

  public void setOnUseWeaponAttackListener(OnUseWeaponAttackListener onUseWeaponAttackListener) {
    this.onUseWeaponAttackListener = onUseWeaponAttackListener;
  }

//这就是我们的后门
  public static interface OnUseWeaponAttackListener {
    int onUseWeaponAttack(int damage);
  }
}

//下面看看如何来进行“偷天换日”
public class Game{
    public static void main(String[] args){
    Hero hero = new Hero(new Weapon());
    try {
      Field weapon = ReflectUtils.getVariable(hero.getClass(), "weaponMain");
      weapon.setAccessible(true);
      Weapon weaponHook = new WeaponHook();
      ((WeaponHook) weaponHook).setOnUseWeaponAttackListener(damage -> {
        //通过后门进行操作,这其实就是我们注入的代码
          System.out.println("damage = " + damage);
          return damage;
      });
      weapon.set(hero, weaponHook); //tou tian偷天换日
      hero.attack();
    } catch (NoSuchFieldException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }Hero hero = new Hero(new Weapon());
        hero.attack();
    }
}
//看输出
对目标造成 10 点伤害
damage = 10   //我们获得了Weapon的伤害值

Summary
due to limited content, I will not summarize the previous review, we examine one of the kind of thinking Hook prevent intrusion.
We add a checking mechanism in Hero class.

public class Hero {
  private Weapon weaponMain;
  private final int weaponMainId; 

  public Hero(Weapon weaponMain) {
    this.weaponMain = weaponMain;
    weaponMainId = this.weaponMain.hashCode();//记录原始Weapon对象的Id,hashCode对于每个对象而言都是唯一的。
  }

  public void attack() {
    if (this.weaponMain.hashCode() != weaponMainId) { //关键位置检查是否遭到替换
      throw new IllegalAccessError(String.format("警告!遭到入侵!入侵者身份:%d", this.weaponMain.hashCode()));
    }
    weaponMain.attack();
  }
}

Now run the program again, the output is as follows:

java.lang.IllegalAccessError: 警告!遭到入侵!入侵者身份:1288141870

More Android Video Advanced Architecture Advanced Learning Click: https://space.bilibili.com/474380680
Reference: https://www.jianshu.com/p/8bf24de75a7a
https://blog.csdn.net/qq_31370269/ Article This article was / the Details / 85,780,165
https://www.cnblogs.com/danew/p/11511952.html
https://www.jianshu.com/p/1a0c368da1b8

Guess you like

Origin www.cnblogs.com/Android-Alvin/p/11949011.html