- Java安全大部分都是从反射开始,可以动态调用一些Payload而不出现相关特征。
类初始化代码执行顺序
- 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("人类构造函数");
- }
- }
复制代码- 人类静态代码块
- 学生静态代码块
- 人类代码块
- 人类构造函数
- 学生代码块
- 学生构造函数
复制代码 类加载器
环境
- 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命令执行
- 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");
- }
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |