Java安全之Java反射(一)

  • 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("人类构造函数");
    }

} 
  • 输出结果
人类静态代码块
学生静态代码块
人类代码块
人类构造函数
学生代码块
学生构造函数 

类加载器

环境

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 initializeClassLoader 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大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值