JAVA反射
反射的概念
java反射机制不是很陌生的东西,总结一波反射中重要的概念:
- Java 反射机制允许运行中的Java程序获取自身的信息, 操作类和对象的内部属性.
==>“ 动态获取信息 “ 和 “ 动态调用属性方法 “
==>对象实例可以通过反射机制获取它的类(类类型)
==>类可以通过反射机制获取它的所有方法和属性
==>获取的属性可以设值,获取的方法可以调用
反射最重要的用途是开发各种通用框架 . 很多框架都是通过XML文件来进行配置的( 例如 struts.xml , spring-*.xml 等 ) , 即所谓的框架核心配置文件 . 为了确保框架的通用性 , 程序运行时需要根据配置文件中对应的内容加载不同的类或对象 , 调用不同的方法 , 这也依赖于 Java 反射机制 .
总的来说,通过反射我们可以::
- 获得一个对象所属的类
- 获得任意一个类的成员变量和方法
- 构造任意一个类的对象
- 任意调用一个对象的方法
纸上得来终觉浅,还是需要多实践来获得更深的理解
反射的具体应用,在上一篇里面写的很详细
通过反射RCE
java.lang.Runtime
第一个写的详细一点
java.lang.Runtime 类的 exec() 方法是Java中最常见的执行命令的方式
我们可以通过forName方法来加载任何类,当然也可以加载Runtime。
调用exec方法的两个条件:
一个实例对象,可以通过newInstance方法拿到
获取到具体的exec方法,反射中要获取一个方法需要知道方法名和参数类型
方式1
1.获取exec方法
(这里抛出了很多异常,暂时不讨论)
2.获取实例对象然后尝试调用
报错了,查看报错信息:
class com.io.EncodingDemo cannot access a member of class java.lang.Runtime (in module java.base) with modifiers “private”
跟进到Runtime方法里,发现他的构造方法是private,而newInstance会默认调用无参构造方法,这里牵扯到java的设计模式中的单例设计,比如在学习Servlet的时候遇到的ServletContext。
单例模式有 3 个特点:
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点。
因此不能直接调用newInstance,要通过单例模式设计对象中的获取实例的方法,来获取一个runtime实例
由于exec返回的是一个Process对象,我们要通过字节流把结果读出来
https://zhuanlan.zhihu.com/p/44957705
方式2
1 | public class EncodingDemo { |
看Epicccal师傅的博客中得知
我们可以通过 对象.方法名
来调用实例方法 , 类名.方法名
来调用静态方法 , 那么反过来 , 方法名.invoke(对象)
可以映射成 方法名.invoke(类)
接着通过p牛的漫谈,思考下面几个问题
类没有无参构造方法,没有单例模式里的静态方法
我们可以使用getConstructor方法,获取构造函数信息,它被封装在java.lang.reflect.Constructor中,当然他也需要特定的函数名和参数列表来获取这个唯一的构造函数
获取到构造函数之后,用newInstance来执行
例如ProcessBuilder类
1 | Class<?> clazz = Class.forName("java.lang.ProcessBuilder"); |
这里进行了强制类型转换,也就是直接调用了实例之后的ProcessBuilder,我们也可用反射来调用start
1 | Class<?> clazz = Class.forName("java.lang.ProcessBuilder"); |
构造方法是私有方法,如何执行
例子就是上面的Runtime类
我们使用getDeclaredMethod方法,它能获取的是当前类中“声明”的方法,包括了私有方法,但不包括从父类继承过来的方法
getMethod方法只能获取公共方法和从父类继承的方法
我们在获取到一个私有方法后,必须用setAccessible 修改它的作用域,否则仍然不能调用。
1 | Class<?> clazz = Class.forName("java.lang.Runtime"); |
java.lang.ProcessBuilder
上文的Runtime类的exec方法,实质上就是通过ProcessBuilder来执行命令,通过跟进exec方法我们可以发现:
我们直接通过ProcessBuilder执行命令:
1 | Class<?> clazz = Class.forName("java.lang.ProcessBuilder"); |
可以通过cmdarray,传入数组来执行带参数的数据
1 | Arrays.asList("ping","127.0.0.1") |
推荐阅读
王哥,我的超人