- Java安全大部分都是从反射开始,可以动态调用一些Payload而不出现相关特征。
类初始化代码执行顺序
- https://www.cnblogs.com/wugongzi/p/11849266.html
- 首先new一个类,加载的顺序是(父)静态变量->(父)静态代码块->(父)代码块->(父)构造方法。
- 如果初始化同一个类,静态变量、静态代码块只执行一次。
package Advance; public class ExecutionOrder { public static void main(String[] args) { Student student = new Student(); } } class Student extends Person { public Student() { System.out.println("学生构造函数"); } { System.out.println("学生代码块"); } static { System.out.println("学生静态代码块"); } } class Person { public static String staticName = "Pan3a"; public String name = "Forever"; { System.out.println("人类代码块"); } static { System.out.println("人类静态代码块"); } public Person() { System.out.println("人类构造函数"); } }
- 输出结果
人类静态代码块 学生静态代码块 人类代码块 人类构造函数 学生代码块 学生构造函数
类加载器
- 这里暂时不去深究吧,后面学习参考文章
- 类加载器和Class.forName区别
- https://javasec.org/javase/ClassLoader/
环境
package Reflection; public class ReflectData { public ReflectData() { } } class Person { private int age; private String name; public long id = 9527; public long grade; protected float score; protected int rank; public Person() { } protected Person(long id) { this(18, "Pan3a", id, 9, 9999, 31); } private Person(int age) { this(age, "Pan3a", 9527, 9, 9999, 30); } public Person(int age, String name, long id, long grade, float score, int rank) { this.age = age; this.name = name; this.id = id; this.grade = grade; this.score = score; this.rank = rank; } 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; } public long getId() { return id; } public void setId(long id) { this.id = id; } public long getGrade() { return grade; } public void setGrade(long grade) { this.grade = grade; } public float getScore() { return score; } public void setScore(float score) { this.score = score; } public int getRank() { return rank; } public void setRank(int rank) { this.rank = rank; } private static void sayHello() { System.out.println("Hello World"); } private void sayHello(String name) { System.out.println("Hello " + name); } @Override public String toString() { final StringBuffer stringBuffer = new StringBuffer("Person{"); stringBuffer.append("age=").append(age); stringBuffer.append(", name='").append(name).append('''); stringBuffer.append(", id=").append(id); stringBuffer.append(", grade=").append(grade); stringBuffer.append(", score=").append(score); stringBuffer.append(", rank=").append(rank); stringBuffer.append('}'); return stringBuffer.toString(); } } class Teacher extends Person { private String role = "Teacher"; public void sayHello() { System.out.println("Hello Teacher"); } } class Student extends Teacher { private String role = "Student"; static { System.out.println("run Student static codes!"); } { System.out.println("run Student codes!"); } @Override public void sayHello() { System.out.println("Hello Student"); } public Student(){ } public Student(String name){ System.out.printf("hello %s", name); } }
获取对象
- 获取对象有三种方式,Class.forName(全类名)、对象.getclass()、类名.class。
- 这里的三种方式虽然作用都一样,但是都有各自的缺点。class.forName()需要知道类名的全路径。对象名.getclass()需要存在已经实例化的对象,类名.class需要提前在编译前知道类名。
- 这里都返回true了,说明这里返回的对象都是同一个,因此Person.class只加载了一次。用instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。
package Reflection; public class ReflectGetObject { public static void main(String[] args) throws ClassNotFoundException{ compareReflectGetObject(); } public static void compareReflectGetObject() throws ClassNotFoundException { Student student = new Student(); Teacher teacher = new Teacher(); Person person = new Person(); if(student instanceof Person){ System.out.println("Student 是Person子类"); } Class> clazz1 = Class.forName("Reflection.Person"); System.out.println(clazz1.getName()); Class> clazz2 = person.getClass(); System.out.println(clazz2.getName()); Class> clazz3 = Person.class; System.out.println(clazz3.getName()); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); } }
- 输出结果如下
run Student static codes! run Student codes! Student 是Person子类 Reflection.Person Reflection.Person Reflection.Person true true
- 这里的Class.forName()有两个函数重载,我们可以跟进看看。第一个函数是我们平常用的,第二个有参数boolean initialize、ClassLoader loader,可以指定类是否在加载时初始化,和指定类加载器。
- 重点,这里的boolean initialize参数是控制是否初始化,而不是实例化,初始化会加载静态变量、静态代码块。
@CallerSensitive public static Class> forName(String className) throws ClassNotFoundException { Class> caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
@CallerSensitive public static Class> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class> caller = null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { // Reflective call to get caller class is only needed if a security manager // is present. Avoid the overhead of making this call otherwise. caller = Reflection.getCallerClass(); if (sun.misc.VM.isSystemDomainLoader(loader)) { ClassLoader ccl = ClassLoader.getClassLoader(caller); if (!sun.misc.VM.isSystemDomainLoader(ccl)) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader, caller); }
- 案例如下
- 输出结果如下,这里的静态代码块只执行了一次,后面没执行,是因为Student.class已经被前面加载到内存中了,
run Student static codes! 0 ------------------ run Student codes! ------------------ 0 ------------------ run Student codes! ------------------
- 若注释掉第一段39-47行,输出结果如下,可以看到静态代码块是在newInstance()实例化后再加载到,因此可以得出initialize参数控制的是是否初始化,而不是实例化。
0 ------------------ run Student static codes! run Student codes! ------------------
构造方法
- 有以下四种获取方法。getConstructor()、getConstructors()、getDeclaredConstructor()、getDeclaredConstructors(),若构造方法带参数,用对应的数据类型.class即可。
- 这里注意非public的构造方法需要使用getDeclaredConstructor()方法即可,然后如果需要实例化必须使用setAccessible(true)。
package Reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectGetObject { public static void main(String[] args) throws Exception{ getConstructorsStudy(); } public static void getConstructorsStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class> clazz = Class.forName("Reflection.Person"); System.out.println("所有构造方法"); Constructor>[] constructors = clazz.getDeclaredConstructors(); for (Constructor> constructor:constructors){ System.out.println(constructor); } System.out.println(); System.out.println("public无参数构造方法"); // 默认当前类的无参数构造方法 Constructor> constructor1 = clazz.getConstructor(); System.out.println(constructor1); System.out.println("protected带参数构造方法"); Constructor> constructor2 = clazz.getDeclaredConstructor(long.class); System.out.println(constructor2); System.out.println("private带参数构造方法"); Constructor> constructor3 = clazz.getDeclaredConstructor(int.class); System.out.println(constructor3 + "n"); System.out.println("public无参数构造方法创建对象"); Object person1 = constructor1.newInstance(); System.out.println(person1); System.out.println("protected带参数构造方法创建对象"); constructor2.setAccessible(true); Object person2 = constructor2.newInstance(9528); System.out.println(person2); System.out.println("private带参数构造方法创建对象"); constructor3.setAccessible(true); Object person3 = constructor3.newInstance(18); System.out.println(person3); System.out.println("Person.class.newInstance()"); Class> class1 = Reflection.Person.class; Object object = class1.newInstance(); System.out.println(object); } }
- 输出结果展示
所有构造方法 protected Reflection.Person(long) private Reflection.Person(int) public Reflection.Person(int,java.lang.String,long,long,float,int) public Reflection.Person() public无参数构造方法 public Reflection.Person() protected带参数构造方法 protected Reflection.Person(long) private带参数构造方法 private Reflection.Person(int)
成员方法
- 获取成员方法,也有四个方法。getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods(),invoke(),这里如果是继承,invoke()调用的方法根据实际传入的方法为准,不存在则向上父类寻找。
- 获取方法属性,getName(),getReturnType(),getParameterTypes(),getModifiers()。
package Reflection; import java.lang.reflect.*; import java.util.ArrayList; import java.util.Arrays; public class ReflectGetObject { public static void main(String[] args) throws Exception{ getMethodStudy(); } public static void getMethodStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { Class> clazz = Class.forName("Reflection.Person"); Constructor> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Object person = constructor.newInstance(); Method method = clazz.getMethod("setAge", int.class); method.invoke(person,18); // getDeclaredMethods学习 System.out.println("所有方法"); Method[] methods = clazz.getDeclaredMethods(); for (Method methodTemp:methods){ System.out.println(methodTemp.getName() + "t" + Arrays.toString(methodTemp.getParameterTypes())); } // getMethod学习 Method method1 = clazz.getMethod("getAge"); method1.setAccessible(true); System.out.println(method1.invoke(person));; // 由于这里和方法为静态方法,因此object为null Method method2 = clazz.getDeclaredMethod("sayHello"); method2.setAccessible(true); method2.invoke(null); // 多态,依旧根据传入的实例化对象为准,如果没有则向父类寻找 Class> teacherClass = Class.forName("Reflection.Teacher"); Method method3 = teacherClass.getMethod("sayHello"); method3.invoke(new Reflection.Student()); } }
- 输出结果
所有方法 setAge [int] getAge [] sayHello [class java.lang.String] sayHello [] setId [long] getGrade [] setGrade [long] getScore [] setScore [float] getRank [] setRank [int] toString [] getName [] setName [class java.lang.String] getId [] 18 Hello World run Student static codes! run Student codes! Hello Student
成员变量
- 获取成员变量需要知道一下四个方法,getField,getFields,getDeclaredField,getDeclaredFields。
- 还可获取成员变量属性的三个方法,getType,getModifiers,getName。分别是获取变量类型,修饰符,成员名。
- 他们分别是获取单个成员,和获取所有成员,获取单个成员(忽略修饰服限制,不包括父类),获取多个成员(忽略修饰服限制,不包括父类)。需注意的是使用后面两个是需使用setAccessible(true)来忽略编译时的安全检查。
package Reflection; import java.lang.reflect.*; import java.util.ArrayList; import java.util.Arrays; public class ReflectGetObject { public static void main(String[] args) throws Exception{ getFieldStudy(); } public static void getFieldStudy() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class> clazz = Class.forName("Reflection.Person"); Constructor> constructor = clazz.getConstructor(); Object person = constructor.newInstance(); Method setAge = clazz.getMethod("setAge", int.class); setAge.invoke(person,18); Method getAge = clazz.getMethod("getAge"); System.out.println(getAge.invoke(person) + "n"); // getgetDeclaredFields学习 System.out.println("所有方法"); Field[] fields = clazz.getDeclaredFields(); for (Field field:fields){ System.out.println(field.getName() + "t" + field.getModifiers() + "t" + field.getType()); } // 反射修改私有成员值 Field field = clazz.getDeclaredField("age"); field.setAccessible(true); field.set(person,20); System.out.println(getAge.invoke(person)); } }
- 输出结果
18 所有方法 age 2 int name 2 class java.lang.String id 1 long grade 1 long score 4 float rank 4 int 20
反射Runtime执行命令
- 代码如下,依次来解释
package Reflection; import java.lang.reflect.Method; public class ReflectRuntimeExec { public static void main(String[] args) throws Exception{ Class> clazz = Class.forName("java.lang.Runtime"); Method getRuntime = clazz.getDeclaredMethod("getRuntime"); Object runtime = getRuntime.invoke(null); Method exec = clazz.getDeclaredMethod("exec", String.class); exec.invoke(runtime,"open /System/Applications/Calculator.app"); } }
- 这里为什么要获取两个方法呢,为什么没有实例化类就可以调用方法了。
- 跟进Runtime,发现它的构造方法是私有的,发现有一个getRuntime的静态方法,可以返回实例化的对象,就像P神说的,“单例模式 ”因此我们通过获取getRuntime方法来获取Runtime的实例,还可以反射构造方法来获取,只不过后者比前者麻烦。
- 疑问,这里为什么不可以获取字段来直接获取变量值。我觉得可能是这里只是获取的属性,并没有获取变量的值吧。这里发现可以通过Filed.get(clazz)来获取变量的值,问题解决。
getRuntime命令执行
package Reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.Runtime; public class ReflectRuntimeExec { public static void main(String[] args) throws Exception { Class> clazz = Class.forName("java.lang.Runtime"); Method getRuntime = clazz.getDeclaredMethod("getRuntime"); Method exec = clazz.getDeclaredMethod("exec", String.class); Object runtime = getRuntime.invoke(null); exec.invoke(runtime, "open /System/Applications/Calculator.app"); } }
Constructor命令执行
- 这里没有意外,直接反射操作即可。
package Reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.Runtime; public class ReflectRuntimeExec { public static void main(String[] args) throws Exception { Class> clazz = Class.forName("java.lang.Runtime"); Constructor> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Object runtime2 = constructor.newInstance(); System.out.println(runtime2 instanceof Runtime); Method exec2 = clazz.getDeclaredMethod("exec", String.class); exec2.invoke(runtime2, "open /System/Applications/Calculator.app"); } }
Filed命令执行
- 了解到field.get就可以获取变量值了
package Reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.Runtime; public class ReflectRuntimeExec { public static void main(String[] args) throws Exception { Class> clazz = Class.forName("java.lang.Runtime"); Field field = clazz.getDeclaredField("currentRuntime"); field.setAccessible(true); Object runtime1 = field.get(clazz); System.out.println(runtime1 instanceof Runtime); Method exec1 = clazz.getDeclaredMethod("exec", String.class); exec1.invoke(runtime1, "open /System/Applications/Calculator.app"); } }
网安零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享
网安零基础入门
对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。
同时每个成长路线对应的板块都有配套的视频提供:
当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享