java安全(1)-java反射

一、前言


跟着小迪学web第67天,之前学了反射没理解本质,学到java rce黑名单绕过还有反序列化的时候来要债了QAQ,于是打算更新一个java安全基础系列文章,一部分自己学到的东西能够做个知识沉淀,另一部分是想帮助一部分想入门java安全的小伙伴。内容基本和主流内容相似,会添加一些自己的理解,小白读完壁咚,大佬轻喷>.<


二、反射的概念


在Java 程序中,JVM 加载完一个类后,在堆内存中就会产生该类的一个 Class 对象,一个类在堆内存中最多只会有一个 Class 对象,这个Class 对象包含了该类的完整结构信息,我们通过这个 Class 对象便可以得到该类的完整结构信息。(注:大写的Class是名为Class的实例对象,先理解Class的存在)

由于JVM为每个加载的类class创建了对应的Class类对象,并在实例中保存了该类class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等。因此,如果获取了某个Class类对象,我们就可以通过这个Class类对象获取到其对应的类class的所有信息,这个过程形象的成为反射。
反射机制是 Java实现动态语言的关键,也就是通过反射实现类的动态加载

Class对象,注意:这个Class类对象是 JVM 内部创建的,如果我们查看 JDK 源码,可以发现Class类的构造方法是private,即只有 JVM 能创建Class类对象,我们程序员自己的 Java 程序是无法创建Class类对象的。

public final class Class { private Class() {} }

三、反射组成相关的类

java反射机制组成需要重点注意以下的类:(下面都会讲到具体的使用)

java.lang.Class:类对象;

java.lang.reflect.Constructor:类的构造器对象;

java.lang.reflect.Field:类的属性对象;

java.lang.reflect.Method:类的方法对象;

四、反射常见使用的方法

获取类的方法:forname

实例化类对象的方法:newInstance

获取函数的方法:getMethod

执行函数的方法:invok

后续会在代码演示中依次介绍  invok()函数在后续漏洞利用中会嵌套使用(个人理解)注意后方高能

五、为什么要学它呢?

其实从官方定义中就能找到其存在的价值,在运行时获得程序或程序集中每一个类型的成员和成员的信息,从而动态的创建、修改、调用、获取其属性,而不需要事先知道运行的对象是谁。划重点:在运行时而不是编译时。(不改变原有代码逻辑,自行运行的时候动态创建和编译即可

其次,安全应用场景:
构造利用链,触发命令执行
反序列化中的利用链构造
动态获取或执行任意类中的属性或方法
动态代理的底层原理是反射技术
rmi反序列化也涉及到反射操作

六、代码演示

Java-反射-Class对象类获取

//1、根据类名:类名.class Class userClass = User.class; //2、根据对象:对象.getClass() User user = new User(); Class aClass = user.getClass(); //3、根据全限定类名:Class.forName("全路径类名") Class aClass1 = Class.forName("com.example.reflectdemo.User"); //4、通过类加载器获得Class对象://ClassLoader.getSystemClassLoader().loadClass("全路径类名"); ClassLoader clsload=ClassLoader.getSystemClassLoader(); Class aClass2 = clsload.loadClass("com.example.reflectdemo.User");

package com.example.reflectdemo;

public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {
        //1,根据全限定类名:Class.forName("全路径类名“);适用于在运行时动态加载类。当类名是以字符串形式存在时(例如从配置文件或用户输入获取),可以用这种方式来获取类的元信息。
        Class aClass =Class.forName("com.example.reflectdemo.User");
        System.out.println(aClass);

        //2.根据类名:类名.class;适用于在编译时已知类名的情况。相比 Class.forName,这种方式更简单和安全,编译时即可检查类是否存在。
        Class userClass = User.class;
        System.out.println(userClass);

        //3.根据对象:对象.getClass();适用于在运行时已有某个类的实例对象的情况。通过对象的 getClass 方法可以获取到该对象对应的类的 Class 对象。
        User user =new User();
        Class aClass1 = user.getClass();
        System.out.println(aClass1);

        //4、通过类加载器获得Class对象://ClassLoader.getSystemClassLoader().loadClass("全路径类名");
        //获取系统类加载器;使用类加载器加载指定路径的类,并返回 Class 对象
        ClassLoader clsload=ClassLoader.getSystemClassLoader();
        Class aClass2 = clsload.loadClass("com.example.reflectdemo.User");
        System.out.println(aClass2);


    }
}

运行结果:

class com.example.reflectdemo.User
class com.example.reflectdemo.User
class com.example.reflectdemo.User
class com.example.reflectdemo.User

Java-反射-Field成员变量类获取

获取成员变量Field位于java.lang.reflect.Field包中

Field[] getFields() :获取所有public修饰的成员变量

Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符

Field getField(String name) 获取指定名称的 public修饰的成员变量

Field getDeclaredField(String name) 获取指定的成员变量

package com.example.reflectdemo;

import java.lang.reflect.Field;

public class GetFiled {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class aClass =Class.forName("com.example.reflectdemo.User");
        //1.获取public属性的成员变量
//        Field[] fields = aClass.getFields();   //接收所有成员变量时用数组接收
//        for(Field fd:fields){
//            System.out.println(fd);
//        }
        //2.获取所有的成员变量
        Field[] fieldss = aClass.getDeclaredFields();

        //3.获取单个的pubilc属性成员变量
        Field fd1 = aClass.getField("name");

        //4.获取单个的任意属性成员变量
        Field fd = aClass.getDeclaredField("job");
        System.out.println(fd);

        //获取公共成员变量age的值
        User u = new User();
        Field field=aClass.getField("age");
        //取值
        Object a=field.get(u);
        System.out.println(a);
        //赋值
        field.set(u,30);
        Object aa=field.get(u);
        System.out.println(aa);


    }
}

Java-反射-Method成员方法类获取

Method getMethod(String name, 类<?>... parameterTypes) //返回该类所声明的public方法

Method getDeclaredMethod(String name, 类<?>... parameterTypes) //返回该类所声明的所有方法

//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型

Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法

Method[] getDeclaredMethods() // 获取该类中的所有方法

package com.example.reflectdemo;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class aClass =Class.forName("com.example.reflectdemo.User");

        //1.获取包括继承的公共成员方法
        Method[] methods = aClass.getMethods();
        for (Method m:methods){
            System.out.println(m);
        }

        //2.获取不包括继承的所有成员方法
        Method[] method = aClass.getDeclaredMethods();
       for(Method me:method){
            System.out.println(me);
        }

        //3.获取单个的成员方法,通过方法名和参数区分是哪个方法
        Method users = aClass.getDeclaredMethod("users", String.class);
        System.out.println(users);

        //对成员方法进行执行
        User u = new User();
        Method user = aClass.getDeclaredMethod("users", String.class);
        user.invoke(u,"gay1");//调用






    }
}

运行结果:

public void com.example.reflectdemo.User.useinfo(java.lang.String,int,java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
protected void com.example.reflectdemo.User.users(java.lang.String)
public void com.example.reflectdemo.User.useinfo(java.lang.String,int,java.lang.String)
protected void com.example.reflectdemo.User.users(java.lang.String)
users成员方法:gay1

Java-反射-Constructor构造方法类获取

Constructor<?>[] getConstructors() :只返回public构造函数

Constructor<?>[] getDeclaredConstructors() :返回所有构造函数

Constructor<> getConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的public构造函数

Constructor<> getDeclaredConstructor(类<?>... parameterTypes) : 匹配和参数配型相符的构造函数

package com.example.reflectdemo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class aClass =Class.forName("com.example.reflectdemo.User");

        //1.获取公共的构造方法
        Constructor[] constructors = aClass.getConstructors();
        for(Constructor con:constructors){
            System.out.println(con);
        }

        //2.获取所有的构造方法
        Constructor[] constructorss = aClass.getDeclaredConstructors();
        for(Constructor con:constructorss){
            System.out.println(con);
        }
        
        //3.获取单个pubilc构造方法 or 获取带相同参数的成员方法
        Constructor constructor = aClass.getConstructor(String.class);
        System.out.println(constructor);

        //4.获取单个构造方法
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class,int.class);
        System.out.println(declaredConstructor);

        //5.对构造方法进行操作(两个参数String,int)
        Constructor con2=aClass.getDeclaredConstructor(String.class,int.class);
        //临时开启对私有的访问
        con2.setAccessible(true);
        User uu=(User) con2.newInstance("congratulation",30);
        System.out.println(uu);

        //6.对构造方法进行操作(1个参数String)
        Constructor con3=aClass.getDeclaredConstructor(String.class);
        con3.newInstance("123");
    }
}

运行结果如下:

public com.example.reflectdemo.User(java.lang.String)
public com.example.reflectdemo.User()
private com.example.reflectdemo.User(java.lang.String,int)
public com.example.reflectdemo.User(java.lang.String)
public com.example.reflectdemo.User()
public com.example.reflectdemo.User(java.lang.String)
private com.example.reflectdemo.User(java.lang.String,int)
congratulation
30
com.example.reflectdemo.User@4554617c
我的名字123

Java-反射-不安全命令执行&反序列化链构造

package com.example.reflectdemo;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetRunExec {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //原生调用 JDK自带的rt.jar
        //Runtime.getRuntime().exec("calc");


        //如果是第三方的jar包呢
        Class aClass = Class.forName("java.lang.Runtime");
        //获取所有公共包括继承的成员方法
//        Method[] methods = aClass.getMethods();
//        for(Method me:methods){
//            System.out.println(me);
//        }
        //获取exec成员方法
        Method exec = aClass.getMethod("exec", String.class);
        //获取getRuntime成员方法
        Method getRuntimeMethod = aClass.getMethod("getRuntime");
        //调用 getRuntime 方法 返回给 Runtime 的实例
        Object runtime = getRuntimeMethod.invoke(aClass);//相当于Runtime.getRuntime()
        //调用 exec 方法执行命令 "calc.exe"
        exec.invoke(runtime, "calc.exe");
        //相当于Runtime.getRuntime().exec("calc");



    }
}

注意:刚刚说的invoke()函数的高能使用在这里,之后会在各种绕过中使用这种方法,这次一定要理解懂,不然只能像我一样回来复习,然后怒写一篇博客来复习

七、小结

通过java反射机制的学习,能够为我们为我们后面的java漏洞分析调试,java漏洞poc测试和服务端模板引擎注入等有着十分重要的意义。因为文章讲的比较通俗简单,主要是面向开始没有涉及一些漏洞分析与源码分析,后面java安全基础系列会慢慢加入这些并详细讲解。其次如果大家对文章有什么问题和觉得需要改进的地方,可以在评论下方留言,大家一起探讨交流下。

参考分析:

https://zhuanlan.zhihu.com/p/165273855
利用结合:https://xz.aliyun.com/t/7031(反序列化利用链)

代码来自小迪sec:www.xiaodi8.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值