Javaweb security learning - Java reflection

Java reflection mechanism

Java reflection ( Reflection) is a very important dynamic feature of Java. By using reflection, we can not only obtain information such as member methods ( Methods), member variables ( Fields), and constructors ( ) of any class Constructors, but also dynamically create Java class instances and call any class methods, modify arbitrary class member variable values, etc. The Java reflection mechanism is an important embodiment of the dynamic nature of the Java language , and it is also the soul of the underlying implementation of various Java frameworks.

Get Class object

Java reflection operates on java.lang.Classobjects, so we need to find a way to get the Class object first. Usually we have the following ways to get the Class object of a class:

  1. Class.forName("ClassName");
  2. ClassLoader.loadClass("ClassName");//In actual use, you need to specify a specific loader, such as ClassLoader.getSystemClassLoader(), and then callloadClass方法
  3. ClassName.class;// class has been loaded, just get its java.lang.Classobject
  4. obj.getClass();//Available when there is an instance obj of a class in the context

Class.forName(String)It is the most commonly used way to obtain Class, forname()there are two overloaded methods:

  • public static Class<?> forName(String className)
    
  • public static Class<?> forName(String name,
                   boolean initialize,
                   ClassLoader loader)
    

The first method can be understood as a package of the second method, and calling this method is equivalent to the second method.

Class.forName("Foo")

is equivalent to:

Class.forName("Foo", true, this.getClass().getClassLoader())

It is worth noting that the second and third parameters of the second method, the second parameter indicates whether to initialize, and the third parameter is ClassLoader(mentioned in the previous section).

The second parameter initialize refers to the initialization of the class, which 初始化can be understood as the initialization of the class. In the example in the previous section, it Class.forname()is also mentioned that 类初始化when the static{}statement block is called (write a malicious class, put the malicious code into the static statement block to load it), p The example given by God in Java Security Talk shows the difference between the three "initialization" methods, and the order in which they are called.

insert image description here

Another method ClassLoader.loadClass(String)also has a method overload as above

Class<?> loadClass(String name)   //调用此方法等效于调用loadClass(name, false)
Class<?> loadClass(String name,
                 boolean resolve)

The first ( Class.forName();) will:

  • Use the class loader that loads the class that calls this code
  • Initialize the class (that is, all static initializers will run)

Another ( ClassLoader.getSystemClassLoader().loadClass();) will:

  • Use the "system" class loader ( overridable )
  • class is not initialized

Example - get the Runtime class Class object code snippet:

String className     = "java.lang.Runtime";
Class  runtimeClass1 = Class.forName(className);
Class  runtimeClass2 = ClassLoader.getSystemClassLoader().loadClass(className);
Class  runtimeClass3 = java.lang.Runtime.class;

get inner class

$It needs to be used instead when calling inner classes by reflection ., for example Common$Inner, by Class.forname("Common$Inner");loading

class Common {
    
    
    static {
    
    
        System.out.printf("CommonClass: %s\n", Common.class);
    }
    class Inner{
    
    
        Inner(){
    
    
            System.out.printf("InnerClass: %s\n", Inner.class);}}}

When compiled, it will be two files Common$Inner.classand Common.classjust like two classes.

The foreshadowing is over, let’s get to the point below.

Generate and manipulate objects using reflection

The Class object can get the:

  • method ( Method对象represented by ), by Method对象executing the method;
  • the constructor ( Constructor对象represented by ), by Constructor对象which the constructor is called;
  • member variable value ( Field对象represented by ), by Field对象directly accessing and modifying the object's member variable value;

Create object (class instance)

You need to use the method Class对象to obtain the specified one 构造方法(Constructor对象), and then call the method of the Constructor object newInstance()(the function is to call the acquired no-argument constructor) to create an instance of the corresponding class of the Class object.

There are generally two ways to get the constructor in the Class class:

getConstructor*()method, only public methods can be obtained.

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

getDeclaredConstructor*()method to get the private constructor of the class (including constructors with other modifiers), must be set setAccessible()to true.

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

call method

After obtaining an instance of a class (Class object), the method is obtained through the getMethod*()OR getDeclaredMethod*()of the Class object.

getMethod*()method to get all public methods of the class, including itself, all public methods inherited from base classes, and all public methods implemented from interfaces.

Method[] getMethods()  //全部方法 返回Method对象
Method getMethod(String name,<?>... parameterTypes)   //指定方法 返回Method数组

getDeclaredMethod*()method, you can get all the methods declared by the class itself, including public, protected and private methods.

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

Then, after the Method object is obtained, invoke()its corresponding method can be called through the method of the Method object.

Object invoke(Object obj, Object... args) 
    obj - 被调用方法的类实例对象(静态方法则为类)
	args - 用于方法调用的参数类型列表(Java存在方法重载,以此确定要调用的方法)

When calling the private method of an object, you need to use Methon.setAccessible(true)the restrictions set to ignore access rights first.

call member variable

The specified member variables contained in the class can be obtained through the getField*()or method of the Class object .getDeclaredField*()

getField*()Only publicly decorated variables can be accessed.

Field[] getFields() //全部变量
Field getField(String name)  //指定变量

getDeclaredField*()You can freely access all member variables of the specified object, including private member variables.

Field[] getDeclaredFields()
Field getDeclaredField(String name)

Get member variable value:

Object obj = field.get(类实例对象);

Modify member variable value:

field.set(类实例对象, 修改后的值);

When manipulating private variables, field.setAccessible(true)you can ignore access restrictions on access to member variables.

To modify a member variable modified by finala keyword, you need to modify the set method first.

// 反射获取Field类的modifiers
Field modifiers = field.getClass().getDeclaredField("modifiers");

// 设置modifiers修改权限
modifiers.setAccessible(true);

// 修改成员变量的Field对象的modifiers值
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

// 修改成员变量值
field.set(类实例对象, 修改后的值);

The above three parts all mentioned calling a setAccessible()method to solve the permission problem of the modifier. The method does not belong to one of the three categories, but setAccessible()belongs to the parent class of the Field, Method and Constructor objects AccessibleObject. It provides the ability to suppress default Java language access control checks when using a reflective object, its override property defaults to false, and the callable setAccessible()method changes. So Field, Method, Constructor can call this method.

Example - reflection java.lang.Runtime command execution

// 获取Runtime类对象
Class runtimeClazz = Class.forName("java.lang.Runtime");

// 获取Runtime类的无参构造方法(该方法为私有方法)
Constructor constructor = runtimeClazz.getDeclaredConstructor();
//修改方法的访问权限(constructor.setAccessible(true))
constructor.setAccessible(true);

// 创建Runtime类实例,等价于 Runtime rt = new Runtime();
Object runtimeInstance = constructor.newInstance();

// 获取Runtime的exec(String cmd)方法
Method runtimeMethod = runtimeClazz.getMethod("exec", String.class);

// 调用exec方法,等价于 rt.exec(cmd);
runtimeMethod.invoke(runtimeInstance, "calc.exe");

Runtime is the most commonly used class when writing commands to execute payloads, and the Runtime class is in singleton mode.insert image description here

The constructor is private, but a static method is given getRuntime()to get the Runtime object, so you can also use the following payload

Class clazz = Class.forName("java.lang.Runtime");
Method execMethod = clazz.getMethod("exec", String.class);
Method getRuntimeMethod = clazz.getMethod("getRuntime");
Object runtime = getRuntimeMethod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");

Summary of Java reflection mechanism

The Java reflection mechanism is the most important manifestation of the dynamic nature of Java. Using the reflection mechanism, we can easily implement the dynamic invocation of Java classes. Most of Java's frameworks are implemented using reflection mechanisms (such as: Spring MVC, ORM框架etc.), and Java reflection plays a crucial role in writing exploit codes, code auditing, and bypassing RASP method restrictions.
thod.invoke(clazz);
execMethod.invoke(runtime, "calc.exe");


## Java反射机制总结

Java反射机制是Java动态性中最为重要的体现,利用反射机制我们可以轻松的实现Java类的动态调用。Java的大部分框架都是采用了反射机制来实现的(如:`Spring MVC`、`ORM框架`等),Java反射在编写漏洞利用代码、代码审计、绕过RASP方法限制等中起到了至关重要的作用。

Guess you like

Origin blog.csdn.net/weixin_43610673/article/details/123894582
Recommended