Java安全学习
Java安全之反射
反射:将类的各个组成部分封装为其他对象
获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
- 类名.class:通过类名的属性class获取
- 对象.getClass():getClass()方法在Object类中定义着
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,无论通过哪一种方式获取的Class对象都是同一个
Class对象功能:
获取功能:
1.获取成员变量
Field[] getFields()
Field getField(String name)
Field[] getDeclareFields()
Field getDeclareField(String name)
2.获取构造方法
Constructor<?>[] getConstructors()
Constructor
Constructor
Constructor<?>[] getDeclareConstructors()
3.获取成员方法
Method[] getMethods()
Method getMethod(String name,类<?>… parameterTypes)
Method[] getDeclareMethods()
Method getDeclareMethod(String name,类<?>… parameterTypes)
4.获取类名
String getName()
getMethod
获取类的⽅法:
forName
实例化类对象的⽅法:
newInstance
获取函数的⽅法:
getMethod
执⾏函数的方法:
invoke
Class.forName
如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName
来获取
如果直接执行这段代码的话是错误的,因为Runtime 类的构造方法是私有的
1 | Class clazz = Class.forName("java.lang.Runtime"); |
只能通过 Runtime.getRuntime() 来获取到 Runtime 对象
1 | package Le1a; |
getConstructor
getConstructor 接收的参数是构造函数列表类型,因为构造函数也支持重载, 所以必须用参数列表类型才能唯一确定一个构造函数
public ProcessBuilder(List
获取到构造函数后,我们使用 newInstance 来执行。 比如,我们常用的另一种执行命令的方式ProcessBuilder,我们使用反射来获取其构造函数,然后调用 start() 来执行命令:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); |
public ProcessBuilder(String… command)
这个构造函数用到了可变长参数(varargs),在编译的时候会编译成一个数组,如下两种写法在底层是等价的
1 | public void hello(String[] names) {} |
String[].class 传给 getConstructor ,获取 ProcessBuilder 的第二种构造函数:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); clazz.getConstructor(String[].class) |
在调用 newInstance 的时候,因为这个函数本身接收的是一个可变长参数,我们传给 ProcessBuilder 的也是一个可变长参数,二者叠加为一个二维数组,所以整个Payload如下:
1 | Class clazz = Class.forName("java.lang.ProcessBuilder"); |
getDeclared
如果一个方法或构造方法是私有方法,就要用到getDeclared
系列的反射
1 | package Le1a; |