Java安全之Java反射(一)

打印 上一主题 下一主题

主题 934|帖子 934|积分 2802


  • Java安全大部分都是从反射开始,可以动态调用一些Payload而不出现相关特征。
类初始化代码执行顺序


  • https://www.cnblogs.com/wugongzi/p/11849266.html
  • 首先new一个类,加载的顺序是(父)静态变量->(父)静态代码块->(父)代码块->(父)构造方法。
  • 如果初始化同一个类,静态变量、静态代码块只执行一次。
  1. package Advance;
  2. public class ExecutionOrder {
  3.     public static void main(String[] args) {
  4.         Student student = new Student();
  5.     }
  6. }
  7. class Student extends Person {
  8.     public Student() {
  9.         System.out.println("学生构造函数");
  10.     }
  11.     {
  12.         System.out.println("学生代码块");
  13.     }
  14.     static {
  15.         System.out.println("学生静态代码块");
  16.     }
  17. }
  18. class Person {
  19.     public static String staticName = "Pan3a";
  20.     public String name = "Forever";
  21.     {
  22.         System.out.println("人类代码块");
  23.     }
  24.     static {
  25.         System.out.println("人类静态代码块");
  26.     }
  27.     public Person() {
  28.         System.out.println("人类构造函数");
  29.     }
  30. }
复制代码

  • 输出结果
  1. 人类静态代码块
  2. 学生静态代码块
  3. 人类代码块
  4. 人类构造函数
  5. 学生代码块
  6. 学生构造函数
复制代码
类加载器

环境
  1. package Reflection;
  2. public class ReflectData {
  3.     public ReflectData() {
  4.     }
  5. }
  6. class Person {
  7.     private int age;
  8.     private String name;
  9.     public long id = 9527;
  10.     public long grade;
  11.     protected float score;
  12.     protected int rank;
  13.     public Person() {
  14.     }
  15.     protected Person(long id) {
  16.         this(18, "Pan3a", id, 9, 9999, 31);
  17.     }
  18.     private Person(int age) {
  19.         this(age, "Pan3a", 9527, 9, 9999, 30);
  20.     }
  21.     public Person(int age, String name, long id, long grade, float score, int rank) {
  22.         this.age = age;
  23.         this.name = name;
  24.         this.id = id;
  25.         this.grade = grade;
  26.         this.score = score;
  27.         this.rank = rank;
  28.     }
  29.     public int getAge() {
  30.         return age;
  31.     }
  32.     public void setAge(int age) {
  33.         this.age = age;
  34.     }
  35.     public String getName() {
  36.         return name;
  37.     }
  38.     public void setName(String name) {
  39.         this.name = name;
  40.     }
  41.     public long getId() {
  42.         return id;
  43.     }
  44.     public void setId(long id) {
  45.         this.id = id;
  46.     }
  47.     public long getGrade() {
  48.         return grade;
  49.     }
  50.     public void setGrade(long grade) {
  51.         this.grade = grade;
  52.     }
  53.     public float getScore() {
  54.         return score;
  55.     }
  56.     public void setScore(float score) {
  57.         this.score = score;
  58.     }
  59.     public int getRank() {
  60.         return rank;
  61.     }
  62.     public void setRank(int rank) {
  63.         this.rank = rank;
  64.     }
  65.     private static void sayHello() {
  66.         System.out.println("Hello World");
  67.     }
  68.     private void sayHello(String name) {
  69.         System.out.println("Hello " + name);
  70.     }
  71.     @Override
  72.     public String toString() {
  73.         final StringBuffer stringBuffer = new StringBuffer("Person{");
  74.         stringBuffer.append("age=").append(age);
  75.         stringBuffer.append(", name='").append(name).append('\'');
  76.         stringBuffer.append(", id=").append(id);
  77.         stringBuffer.append(", grade=").append(grade);
  78.         stringBuffer.append(", score=").append(score);
  79.         stringBuffer.append(", rank=").append(rank);
  80.         stringBuffer.append('}');
  81.         return stringBuffer.toString();
  82.     }
  83. }
  84. class Teacher extends Person {
  85.     private String role = "Teacher";
  86.     public void sayHello() {
  87.         System.out.println("Hello Teacher");
  88.     }
  89. }
  90. class Student extends Teacher {
  91.     private String role = "Student";
  92.     static {
  93.         System.out.println("run Student static codes!");
  94.     }
  95.     {
  96.         System.out.println("run Student codes!");
  97.     }
  98.     @Override
  99.     public void sayHello() {
  100.         System.out.println("Hello Student");
  101.     }
  102.     public Student(){
  103.     }
  104.     public Student(String name){
  105.         System.out.printf("hello %s", name);
  106.     }
  107. }
复制代码
获取对象


  • 获取对象有三种方式,Class.forName(全类名)、对象.getclass()、类名.class。
  • 这里的三种方式虽然作用都一样,但是都有各自的缺点。class.forName()需要知道类名的全路径。对象名.getclass()需要存在已经实例化的对象,类名.class需要提前在编译前知道类名。
  • 这里都返回true了,说明这里返回的对象都是同一个,因此Person.class只加载了一次。用instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。
  1. package Reflection;
  2. public class ReflectGetObject {
  3.     public static void main(String[] args) throws ClassNotFoundException{
  4.         compareReflectGetObject();
  5.         
  6.     }
  7.     public static void compareReflectGetObject() throws ClassNotFoundException {
  8.         Student student = new Student();
  9.         Teacher teacher = new Teacher();
  10.         Person person = new Person();
  11.         if(student instanceof Person){
  12.             System.out.println("Student 是Person子类");
  13.         }
  14.         Class<?> clazz1 = Class.forName("Reflection.Person");
  15.         System.out.println(clazz1.getName());
  16.         Class<?> clazz2 = person.getClass();
  17.         System.out.println(clazz2.getName());
  18.         Class<?> clazz3 = Person.class;
  19.         System.out.println(clazz3.getName());
  20.         System.out.println(clazz1 == clazz2);
  21.         System.out.println(clazz1 == clazz3);
  22.     }
  23. }
复制代码

  • 输出结果如下
  1. run Student static codes!
  2. run Student codes!
  3. Student 是Person子类
  4. Reflection.Person
  5. Reflection.Person
  6. Reflection.Person
  7. true
  8. true
复制代码

  • 这里的Class.forName()有两个函数重载,我们可以跟进看看。第一个函数是我们平常用的,第二个有参数boolean initialize、ClassLoader loader,可以指定类是否在加载时初始化,和指定类加载器。
  • 重点,这里的boolean initialize参数是控制是否初始化,而不是实例化,初始化会加载静态变量、静态代码块。
  1. @CallerSensitive
  2.     public static Class<?> forName(String className)
  3.                 throws ClassNotFoundException {
  4.         Class<?> caller = Reflection.getCallerClass();
  5.         return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
  6.     }
复制代码
  1.     @CallerSensitive
  2.     public static Class<?> forName(String name, boolean initialize,
  3.                                    ClassLoader loader)
  4.         throws ClassNotFoundException
  5.     {
  6.         Class<?> caller = null;
  7.         SecurityManager sm = System.getSecurityManager();
  8.         if (sm != null) {
  9.             // Reflective call to get caller class is only needed if a security manager
  10.             // is present.  Avoid the overhead of making this call otherwise.
  11.             caller = Reflection.getCallerClass();
  12.             if (sun.misc.VM.isSystemDomainLoader(loader)) {
  13.                 ClassLoader ccl = ClassLoader.getClassLoader(caller);
  14.                 if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
  15.                     sm.checkPermission(
  16.                         SecurityConstants.GET_CLASSLOADER_PERMISSION);
  17.                 }
  18.             }
  19.         }
  20.         return forName0(name, initialize, loader, caller);
  21.     }
复制代码

  • 案例如下
  • 输出结果如下,这里的静态代码块只执行了一次,后面没执行,是因为Student.class已经被前面加载到内存中了,
  1. run Student static codes!
  2. 0
  3. ------------------
  4. run Student codes!
  5. ------------------
  6. 0
  7. ------------------
  8. run Student codes!
  9. ------------------
复制代码

  • 若注释掉第一段39-47行,输出结果如下,可以看到静态代码块是在newInstance()实例化后再加载到,因此可以得出initialize参数控制的是是否初始化,而不是实例化。
  1. 0
  2. ------------------
  3. run Student static codes!
  4. run Student codes!
  5. ------------------
复制代码
构造方法


  • 有以下四种获取方法。getConstructor()、getConstructors()、getDeclaredConstructor()、getDeclaredConstructors(),若构造方法带参数,用对应的数据类型.class即可。
  • 这里注意非public的构造方法需要使用getDeclaredConstructor()方法即可,然后如果需要实例化必须使用setAccessible(true)。
  1. package Reflection;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Method;
  6. public class ReflectGetObject {
  7.     public static void main(String[] args) throws Exception{
  8.         getConstructorsStudy();
  9.     }
  10.     public static void getConstructorsStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
  11.         Class<?> clazz = Class.forName("Reflection.Person");
  12.         System.out.println("所有构造方法");
  13.         Constructor<?>[] constructors = clazz.getDeclaredConstructors();
  14.         for (Constructor<?> constructor:constructors){
  15.             System.out.println(constructor);
  16.         }
  17.         System.out.println();
  18.         System.out.println("public无参数构造方法");
  19. //      默认当前类的无参数构造方法
  20.         Constructor<?> constructor1 = clazz.getConstructor();
  21.         System.out.println(constructor1);
  22.         System.out.println("protected带参数构造方法");
  23.         Constructor<?> constructor2 = clazz.getDeclaredConstructor(long.class);
  24.         System.out.println(constructor2);
  25.         System.out.println("private带参数构造方法");
  26.         Constructor<?> constructor3 = clazz.getDeclaredConstructor(int.class);
  27.         System.out.println(constructor3 + "\n");
  28.         System.out.println("public无参数构造方法创建对象");
  29.         Object person1 = constructor1.newInstance();
  30.         System.out.println(person1);
  31.         System.out.println("protected带参数构造方法创建对象");
  32.         constructor2.setAccessible(true);
  33.         Object person2 = constructor2.newInstance(9528);
  34.         System.out.println(person2);
  35.         System.out.println("private带参数构造方法创建对象");
  36.         constructor3.setAccessible(true);
  37.         Object person3 = constructor3.newInstance(18);
  38.         System.out.println(person3);
  39.         System.out.println("Person.class.newInstance()");
  40.         Class<?> class1 = Reflection.Person.class;
  41.         Object object = class1.newInstance();
  42.         System.out.println(object);
  43.     }
  44. }
复制代码

  • 输出结果展示
  1. 所有构造方法
  2. protected Reflection.Person(long)
  3. private Reflection.Person(int)
  4. public Reflection.Person(int,java.lang.String,long,long,float,int)
  5. public Reflection.Person()
  6. public无参数构造方法
  7. public Reflection.Person()
  8. protected带参数构造方法
  9. protected Reflection.Person(long)
  10. private带参数构造方法
  11. private Reflection.Person(int)
复制代码
成员方法


  • 获取成员方法,也有四个方法。getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods(),invoke(),这里如果是继承,invoke()调用的方法根据实际传入的方法为准,不存在则向上父类寻找。
  • 获取方法属性,getName(),getReturnType(),getParameterTypes(),getModifiers()。
  1. package Reflection;
  2. import java.lang.reflect.*;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. public class ReflectGetObject {
  6.     public static void main(String[] args) throws Exception{
  7.         getMethodStudy();
  8.     }
  9.     public static void getMethodStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
  10.         Class<?> clazz = Class.forName("Reflection.Person");
  11.         Constructor<?> constructor = clazz.getDeclaredConstructor();
  12.         constructor.setAccessible(true);
  13.         Object person = constructor.newInstance();
  14.         Method method = clazz.getMethod("setAge", int.class);
  15.         method.invoke(person,18);
  16.         // getDeclaredMethods学习
  17.         System.out.println("所有方法");
  18.         Method[] methods = clazz.getDeclaredMethods();
  19.         for (Method methodTemp:methods){
  20.             System.out.println(methodTemp.getName() + "\t" + Arrays.toString(methodTemp.getParameterTypes()));
  21.         }
  22.         // getMethod学习
  23.         Method method1 = clazz.getMethod("getAge");
  24.         method1.setAccessible(true);
  25.         System.out.println(method1.invoke(person));;
  26.         //  由于这里和方法为静态方法,因此object为null
  27.         Method method2 = clazz.getDeclaredMethod("sayHello");
  28.         method2.setAccessible(true);
  29.         method2.invoke(null);
  30.         //      多态,依旧根据传入的实例化对象为准,如果没有则向父类寻找
  31.         Class<?> teacherClass = Class.forName("Reflection.Teacher");
  32.         Method method3 = teacherClass.getMethod("sayHello");
  33.         method3.invoke(new Reflection.Student());
  34.     }
  35. }
复制代码

  • 输出结果
  1. 所有方法
  2. setAge        [int]
  3. getAge        []
  4. sayHello        [class java.lang.String]
  5. sayHello        []
  6. setId        [long]
  7. getGrade        []
  8. setGrade        [long]
  9. getScore        []
  10. setScore        [float]
  11. getRank        []
  12. setRank        [int]
  13. toString        []
  14. getName        []
  15. setName        [class java.lang.String]
  16. getId        []
  17. 18
  18. Hello World
  19. run Student static codes!
  20. run Student codes!
  21. Hello Student
复制代码
成员变量


  • 获取成员变量需要知道一下四个方法,getField,getFields,getDeclaredField,getDeclaredFields。
  • 还可获取成员变量属性的三个方法,getType,getModifiers,getName。分别是获取变量类型,修饰符,成员名。
  • 他们分别是获取单个成员,和获取所有成员,获取单个成员(忽略修饰服限制,不包括父类),获取多个成员(忽略修饰服限制,不包括父类)。需注意的是使用后面两个是需使用setAccessible(true)来忽略编译时的安全检查。
  1. package Reflection;
  2. import java.lang.reflect.*;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. public class ReflectGetObject {
  6.     public static void main(String[] args) throws Exception{
  7.         getFieldStudy();
  8.     }
  9.     public static void getFieldStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
  10.         Class<?> clazz = Class.forName("Reflection.Person");
  11.         Constructor<?> constructor = clazz.getConstructor();
  12.         Object person = constructor.newInstance();
  13.         Method setAge = clazz.getMethod("setAge", int.class);
  14.         setAge.invoke(person,18);
  15.         Method getAge = clazz.getMethod("getAge");
  16.         System.out.println(getAge.invoke(person) + "\n");
  17.         // getgetDeclaredFields学习
  18.         System.out.println("所有方法");
  19.         Field[] fields = clazz.getDeclaredFields();
  20.         for (Field field:fields){
  21.             System.out.println(field.getName() + "\t" + field.getModifiers() + "\t" + field.getType());
  22.         }
  23.         // 反射修改私有成员值
  24.         Field field = clazz.getDeclaredField("age");
  25.         field.setAccessible(true);
  26.         field.set(person,20);
  27.         System.out.println(getAge.invoke(person));
  28.     }
  29. }
复制代码

  • 输出结果
  1. 18
  2. 所有方法
  3. age        2        int
  4. name        2        class java.lang.String
  5. id        1        long
  6. grade        1        long
  7. score        4        float
  8. rank        4        int
  9. 20
复制代码
反射Runtime执行命令


  • 代码如下,依次来解释
  1. package Reflection;
  2. import java.lang.reflect.Method;
  3. public class ReflectRuntimeExec {
  4.     public static void main(String[] args) throws Exception{
  5.         Class<?> clazz = Class.forName("java.lang.Runtime");
  6.         Method getRuntime = clazz.getDeclaredMethod("getRuntime");
  7.         Object runtime = getRuntime.invoke(null);
  8.         Method exec = clazz.getDeclaredMethod("exec", String.class);
  9.         exec.invoke(runtime,"open /System/Applications/Calculator.app");
  10.     }
  11. }
复制代码

  • 这里为什么要获取两个方法呢,为什么没有实例化类就可以调用方法了。
  • 跟进Runtime,发现它的构造方法是私有的,发现有一个getRuntime的静态方法,可以返回实例化的对象,就像P神说的,“单例模式 ”因此我们通过获取getRuntime方法来获取Runtime的实例,还可以反射构造方法来获取,只不过后者比前者麻烦。
  • 疑问,这里为什么不可以获取字段来直接获取变量值。我觉得可能是这里只是获取的属性,并没有获取变量的值吧。这里发现可以通过Filed.get(clazz)来获取变量的值,问题解决。
getRuntime命令执行
  1. package Reflection;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.Runtime;
  6. public class ReflectRuntimeExec {
  7.     public static void main(String[] args) throws Exception {
  8.         Class<?> clazz = Class.forName("java.lang.Runtime");
  9.         Method getRuntime = clazz.getDeclaredMethod("getRuntime");
  10.         Method exec = clazz.getDeclaredMethod("exec", String.class);
  11.         Object runtime = getRuntime.invoke(null);
  12.         exec.invoke(runtime, "open /System/Applications/Calculator.app");
  13.     }
  14. }
复制代码
Constructor命令执行


  • 这里没有意外,直接反射操作即可。
  1. package Reflection;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.Runtime;
  6. public class ReflectRuntimeExec {
  7.     public static void main(String[] args) throws Exception {
  8.         Class<?> clazz = Class.forName("java.lang.Runtime");
  9.         Constructor<?> constructor = clazz.getDeclaredConstructor();
  10.         constructor.setAccessible(true);
  11.         Object runtime2 = constructor.newInstance();
  12.         System.out.println(runtime2 instanceof Runtime);
  13.         Method exec2 = clazz.getDeclaredMethod("exec", String.class);
  14.         exec2.invoke(runtime2, "open /System/Applications/Calculator.app");
  15.     }
  16. }
复制代码
Filed命令执行


  • 了解到field.get就可以获取变量值了
  1. package Reflection;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.Runtime;
  6. public class ReflectRuntimeExec {
  7.     public static void main(String[] args) throws Exception {
  8.         Class<?> clazz = Class.forName("java.lang.Runtime");
  9.         Field field = clazz.getDeclaredField("currentRuntime");
  10.         field.setAccessible(true);
  11.         Object runtime1 = field.get(clazz);
  12.         System.out.println(runtime1 instanceof Runtime);
  13.         Method exec1 = clazz.getDeclaredMethod("exec", String.class);
  14.         exec1.invoke(runtime1, "open /System/Applications/Calculator.app");
  15.     }
  16. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表