Reflection of java learning

Reflection of java learning

0x00 Preface

Today, let's briefly record some things about reflection and annotation. Reflection mechanism is also important for later Java deserialization vulnerability research and code audit.

0x01 overview of reflection mechanism

Java reflection is a very important dynamic feature of Java. By using reflection, we can not only obtain the member methods, member variables, construction methods and other information of any class, but also dynamically create Java class instances, call any class methods, modify any class member variable values, etc. Java reflection mechanism is not only an important embodiment of the dynamics of Java language, but also the soul of the underlying implementation of various Java frameworks.

0x02 java reflection

Java reflection operates on Java Lang. class object, so we need to get the class object first.

How to get class object:

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

Code example:

方式一:
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);

The same bytecode file (*. Class) will only be loaded once during a program run, and the class object obtained by either method is the same.

Class method:

Get member variable method:

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

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

Get construction method:

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

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

Get member method:

* Method[] getmethods()  
			* Method getmethod(String name,类<?>... parameterTypes)  

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

Get full class name:

String getName()  

Member variable settings:

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

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

Construction method:

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

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

Method object:

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

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

Get the member variable using the getfield method

Let's write a person class here.

Person code:

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 class code:


 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);

    }
    

Use getdeclaraedfields to get all member variables

This method does not consider modifiers

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);
            
        }


Use getdeclaraedfield to get the specified member variable

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);

Here, the member variable in the person class is modified by private. If we want to access his value, we must use violent reflection. Violent reflection can ignore the security detection of the access right modifier.

Get construction method

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);

Acquisition method

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");

Get all public modifier methods

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

Get class name

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

These are just some simple methods. Let's look at a small case to understand the specific application of reflection.

step

Generally, the configuration files in Java are based on At the end of properites, define a pro Properites file.

pro. Contents of properites file:

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

Contents of main class:

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);      //调用方法




    }
}

If we need to modify the called method or class, we can modify it directly in the configuration file without modifying the code.

0x03 reflection calling runtime

The runtime function has an exec method that can execute commands locally. Most payloads for JSP command execution may call the exec method of runtime for command execution.

Execute commands without reflection

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);


    }
}


This kind of code is basically fixed. If you want to pass in parameters to execute commands many times, this writing method will certainly not work, so reflection can be used at this time.

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);
    }
}

At this time, you only need to modify the command value, and you can execute other commands without modifying the code.

method. The first parameter of invoke must be class instance object. If the static method is called, the first parameter value can be transferred to null, because calling static method in Java does not require class instances, because it can be directly classified. Method name (parameter).

method. The second parameter of invoke is not necessary. If the current call method has no parameters, the second parameters can not be transmitted. If there are parameters, then the corresponding parameter types must be passed in strict order.

0x04 end

While debugging the code, while coding the article, it's already 5 o'clock unconsciously after writing. Washing and go to bed.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>