Java学习之反射篇

Java学习之反射篇0x00前言今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要。0x01反射机制概述Java反射是Java非

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

Java学习之反射篇

0x00 前言

今天简单来记录一下,反射与注解的一些东西,反射这个机制对于后面的java反序列化漏洞研究和代码审计也是比较重要。

0x01 反射机制概述

Java反射是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法、成员变量、构造方法等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。Java反射机制是Java语言的动态性的重要体现,也是Java的各种框架底层实现的灵魂。

0x02 Java反射

Java反射操作的是java.lang.Class对象,所以我们需要要先获取到Class对象。

获取Class对象的方式:

1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
		 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
	2. 类名.class:通过类名的属性class获取
		 多用于参数的传递
	3. 对象.getClass():getClass()方法在Object类中定义着。
		 多用于对象的获取字节码的方式

代码实例:

方式一:
Class cls1 = Class.forName("Domain.Person");

        System.out.println(cls1);
方式二:
        Class cls2 = Person.class;
        System.out.println(cls2);
        
方式三:

        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

class类方法:

获取成员变量方法:

1. 获取成员变量们
			* Field[] getFields() :获取所有public修饰的成员变量
			* Field getField(String name)   获取指定名称的 public修饰的成员变量

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

获取构造方法:

* Constructor<?>[] getConstructors()  
			* Constructor<T> getConstructor(类<?>... parameterTypes)  

			* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
			* Constructor<?>[] getDeclaredConstructors()  

获取成员方法:

* Method[] getMethods()  
			* Method getMethod(String name, 类<?>... parameterTypes)  

			* Method[] getDeclaredMethods()  
			* Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

获取全类名:

String getName()  

成员变量设置:

 Field:成员变量
	* 操作:
		1. 设置值
			* void set(Object obj, Object value)  
		2. 获取值
			* get(Object obj) 

		3. 忽略访问权限修饰符的安全检查
			* setAccessible(true):暴力反射

构造方法:

创建对象:
		* T newInstance(Object... initargs)  

		* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

方法对象:

执行方法:
		* Object invoke(Object obj, Object... args)  

	* 获取方法名称:
		* String getName:获取方法名

使用getField方法获取成员变量

我们现在这里编写一个person类。

person代码:

package Domain;

public class Person {
    private String name  ;
    private int age;
    public String a ;


    public Person() {
    }

    public void eat(){
        System.out.println("eat");
    }
    public void eat(String food){
        System.out.println("eat "+food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                '}';
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

main类代码:


 public static void main(String[] args) throws Exception {
        Class cls = Class.forName("Domain.Person");
        Field a = cls.getField("a"); //获取成员a变量
        Person person = new Person();
        Object o = a.get(person);  //获取成员变量的值
        System.out.println(o);
        a.set(person,"abc");  //修改成员a变量的值为abc
        System.out.println(person);

    }
    

使用getDeclaredFields获取所有成员变量

该方法不考虑修饰符

public static void main(String[] args) throws Exception {
        Class cls = Class.forName("Domain.Person");
    System.out.println(person);
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
            
        }


使用getDeclaredField获取指定成员变量

Class cls = Class.forName("Domain.Person");
Field b = cls.getDeclaredField("b");
        b.setAccessible(true);  //使用暴力反射机制,忽略访问权限修饰符的安全检测
        Person person = new Person();
        Object o1 = b.get(person);
        System.out.println(o1);

这里person该类中的成员变量是被private修饰的,我们想要访问他的值必须使用暴力反射,暴力反射
可以,忽略访问权限修饰符的安全检测。

获取构造方法

Class cls = Class.forName("Domain.Person");

        Constructor constructor = cls.getConstructor(String.class,int.class);//获取构造器
        System.out.println(constructor);
        //有参构造
        Object o = constructor.newInstance("123", 18); //创建对象
        System.out.println(o);
        //无参构造
        Object o1 = constructor.newInstance();
        
        System.out.println(o1);

获取方法

Class cls = Class.forName("Domain.Person");
        //无参数方法
        Method eat = cls.getMethod("eat");
        Person person = new Person();
        eat.invoke(person);   //调用eat方法
        //有参数方法
        Method eat1 = cls.getMethod("eat", String.class);  //获取eat方法并且设置参数
        eat1.invoke(person,"fish");

获取所有public修饰方法

Class cls = Class.forName("Domain.Person");
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

获取类名

Class cls = Class.forName("Domain.Person");
        String name = cls.getName();
        System.out.println(name);

前面这些只是简单的一些方法的使用,后面来看一个小案例来了解反射的具体应用。

步骤

1.首先我们需要创建一个配置文件,然后定义需要创建的兑现和需要执行的方法定义在配置文件里面。

2.在程序中读取配置文件

3.使用反射机制加载类文件进内存

4.创建对象

5.执行方法

一般java里面的配置文件都是以.properites结尾,那么就定义一个pro.properites文件。

pro.properites文件内容:

className=Domain.Person  //写入需要加载的类
methodName=eat //写入需要加载的方法

main类里面内容:

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Properties properties = new Properties(); //创建properties对象
        ClassLoader classLoader = Person.class.getClassLoader();  //获取加载
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properites"); //获取路径文件流
        properties.load(resourceAsStream); //加载文件
        //获取配置文件定义的数据
        String className = properties.getProperty("className"); //获取类名
        String methodName = properties.getProperty("methodName");//获取方法名

        Class cls = Class.forName(className);  //将类加载进内存
        Object o = cls.newInstance(); //创建无参构造对象

        Method method = cls.getMethod(methodName);   //创建方法
        method.invoke(o);      //调用方法




    }
}

如果我们需要修改调用的方法或者说类,可以直接在配置文件里面进行修改,无需修改代码。

0x03 反射调用Runtime

Runtime这个函数有exec方法可以本地执行命令,大部分关于jsp命令执行的payload可能都是调用Runtime进行Runtime的exec方法进行命令执行的。

不利用反射执行命令

package com;

import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

public class Test {
    public static void main(String[] args) throws IOException {
        InputStream ipconfig = Runtime.getRuntime().exec("ipconfig").getInputStream();
        String s = IOUtils.toString(ipconfig,"gbk"); //使用IOUtils.toString静态方法将字节输入流转换为字符
        System.out.println(s);


    }
}


这样的代码基本都是固定死的,如果要多次传入参数执行命令的话,这样的写法肯定是不行的,那么这时候就可以用到反射。

package com;

import org.apache.commons.io.IOUtils;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test2 {
    public static void main(String[] args) throws Exception {
        String command = "ipconfig";
        Class cls = Class.forName("java.lang.Runtime"); //Runtime加载进内存
        Constructor declaredConstructor = cls.getDeclaredConstructor(); //获取构造方法
        declaredConstructor.setAccessible(true);  //暴力反射
        Object o = declaredConstructor.newInstance(); //创建Runtime类
        Method exec = cls.getMethod("exec", String.class); //获取exec方法,设置需要参数string类型参数
        Process process = (Process) exec.invoke(o,command);   //执行exec方法,并传入ipconfig参数
//        System.out.println(process);
        InputStream inputStream = process.getInputStream();    //获取输出的数据
        String ipconfig = IOUtils.toString(inputStream,"gbk"); //字节输出流转换为字符
        System.out.println(ipconfig);
    }
}

这时候只需要修改command的值,无需修改代码就可以执行其他的命令了。

Java学习之反射篇

method.invoke的第一个参数必须是类实例对象,如果调用的是static方法那么第一个参数值可以传null,因为在java中调用静态方法是不需要有类实例的,因为可以直接类名.方法名(参数)的方式调用。

method.invoke的第二个参数不是必须的,如果当前调用的方法没有参数,那么第二个参数可以不传,如果有参数那么就必须严格的依次传入对应的参数类型。

0x04 结尾

一边调试代码,一边码文章,写完不知不觉已经5点了。还是洗洗睡吧。

Java学习之反射篇

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119946.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 中小型酒店管理系统[通俗易懂]

    中小型酒店管理系统[通俗易懂][摘要]计算机网络如果结合使用信息管理系统,能够提高管理员管理的效率,改善服务质量。优秀的中小型酒店管理系统能够更有效管理用户预订酒店业务规范,帮助管理者更加有效管理用户预订酒店,可以帮助提高克服人工管理带来的错误等不利因素。所以一个优秀的中小型酒店管理系统能够带来很大的作用。本中小型酒店管理系统使用了计算机语言Java和存放数据的仓库MySQl,采用了MVC设计模式来实现。本系统使用了框架SpringBoot实现了中小型酒店管理系统应有的功能,系统主要角色包括管理员、第三方管理员和酒店管理员。[关键词]

    2022年9月25日
    0
  • idea202112激活码下载[最新免费获取]

    (idea202112激活码下载)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlS32PGH0SQB-eyJsaWNlbnNlSWQi…

    2022年3月25日
    54
  • 如何下载离线地图数据(支持谷歌、百度、高德等所有地图源)

    下载离线地图数据(支持谷歌、百度、高德等所有地图源)发布时间:2019-03-12版权:离线地图分为两种:一种叫“金字塔瓦片”数据,一种叫“矢量地图数据”。我们看的在线地图比如,百度,谷歌,高德等等网页上的地图,都是金字塔瓦片;另外一种是手机上用来导航的,比如高德地图APP,也可以下载离线地图数据,这个就是矢量的。卫星影像地图都是金字塔瓦片,无矢量数据。…

    2022年4月5日
    455
  • 彻底澄清子网掩码、反掩码、通配符掩码以及ospf network命令误区

    彻底澄清子网掩码、反掩码、通配符掩码以及ospf network命令误区1.子网掩码(IPsubnetmask)用途:标识一个IP地址的网络位,主机位网络设备判断目的IP跟自己是否同一网段的依据。特点:1和0绝对不可能间隔,1总在0的前面。网络通信角度,子网掩码只具有本地意义。跟对端没有匹配的硬性要求。误区:一条链路两端的子网掩码必须一致(是习惯不是必须)例外:ospf多路访问网络中,掩码不一致会影响ospf邻居关系建立2.通配符掩码wildcardmask用途:选出一组符合否规则的IP地址特点:0表匹配…

    2022年7月19日
    25
  • Android 【实现自动轮询的RecycleView】

    Android 【实现自动轮询的RecycleView】

    2021年3月12日
    163
  • webpackdevtool配置简单对比简书_钢铁雄心4toolpack

    webpackdevtool配置简单对比简书_钢铁雄心4toolpack官方手册传送门官方对devtool配置的定义很简单:选择一种sourcemap格式来增强调试过程,不同的值会明显影响到构建build和重新构建rebuild的速度。不过,什么是sourcemap,官方用提供了许多种的sourcemap,其中的区别是什么,我们在开发中应该怎么选择,都是我们要学习的。1.什么是sourcemap现在的前端代码会通过babel编译或者各类的…

    2022年9月29日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号