初探java安全之反射(2)
前言
前面总结了,java反射的一些函数的相关用法,本篇主要总结如何利用这些函数来构造执行函数。
重要方法
其实上篇总结过,这里再加几个
- 获取类的方法
foName
- 实例化类对象的方法
newInstance
- 获取函数的方法
getMethod
- 执行函数的方法
invoke
- 获得构造方法
getConstructor
newInstance()
该方法属于 Class
类,执行后返回一个 Object
,可以利用这个方法来实例化对应的类,作用就是调用这个类的无参构造函数。但要求要实例化的类必须要有无参构造函数,并且这个构造函数不是私有的。
Runtime类的分析
进入 Runtime
类中可以看到
发现 Runtime
类使用的单例模式,即只能通过 getRuntime()
方法来获取 Runtime 对象。同时注意 getRuntime
对象是静态方法,即可以直接通过 类名.方法名
的方法调用。
java执行命令
Runtime run = Runtime.getRuntime();//获取 Runtime 对象
run.exec("calc");//执行calc命令弹出计算器
getMethod()
该方法可获取一个 Method
对象,即获取类中的方法,通常要和 invoke()
方法一起使用,这里重点关注下它的各项参数
第一个参数 name
是所要获取方法的方法名,第二个参数 parameterTypes
是所获得到的方法中参数的类型,parameterTypes
是个 Class 类型的数组,用的是java的可变长参数的写法,<?>
是泛型的表示,这里不探讨。
即 参数类型 Class<?>... parameterTypes
其实等价于 Class<?>[] parameterTypes
。传递的对应参数是所获取方法的对应的参数类型。比如 Runtime
中的 exec(String command)
函数,获取它就是
Class clazz = Class.forName("java.lang.Runtime");
Method cmd = clazz.getMethod("exec", String.class);
invoke()
invoke()
方法用于执行 getMethod
获取的方法,看看它的相关参数
第一个参数是函数所在的对象或**类(执行静态方法可用)**,第二个参数是所执行的函数的对象参数
依旧拿 exec(String command)
方法来说
Runtime run = Runtime.getRuntime();//获取Runtime对象
Class clazz = Class.forName("java.lang.Runtime");
Method cmd = clazz.getMethod("exec", String.class);
cmd.invoke(run, "calc");//执行函数
当然 invoke
的第一个参数也可以是 类 ,但执行的方法必须是静态方法
比如我们要执行上面所说的 Runtime
类中的静态方法 getRuntime()
去获取对象
Class clazz = Class.forName("java.lang.Runtime");
//调用静态方法getRuntime()获取Runtime对象
Runtime run = (Runtime) clazz.getMethod("getRuntime").invoke(clazz);
run.exec("calc");
等价于 Runtime.getRuntime().exec("calc");
利用反射执行命令
总结上面的 getMethod
和 invoke
,大白话说就是我们一般这个样执行函数
Object.Method(arg1, arg2, ...)
,或执行静态方法 Class.Method(arg1, arg2, ...)
用反射就是 Method.invoke(Object, arg1, arg2, ...)
, Method.invoke(Class, arg1, arg2, ...)
所以可以利用纯反射执行吗,命令
//获取 Runtime 类
Class clazz = Class.forName("java.lang.Runtime");
clazz.getMethod("exec",String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz),
"calc");
写到一起就是
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")), "calc");//执行calc命令
getConstructor和getDeclaredConstructor
这两个方法均用于获取类的构造方法,后者可以获取私有的构造方法
前面利用 Class
中的 newInstance
方法获取一个类对象只能是对应无参的构造函数来实例化类,而利用这两个函数可以调用类的有参构造函数来实例化一个对象。
这里以 getConstructor
为例,getDeclaredConstructor
用法相同只是后续要加个 setAccessible(true)
该方法的参数是获取到的构造方法中对应参数的数据类型,该方法返回的 Constructor
对象中有个 newInstance
方法,看看这个 newInstance
方法的参数
newInstance
方法的参数是调用的构造方法对应的参数。
比如我们利用 ProcessBuilder
类来执行命令
new ProcessBuilder("calc").start();
注意这里用到的 ProcessBuilder
重载的构造方法
String... command
等价于 String[] command
使用反射构造
Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) clazz.getConstructor(String[].class).newInstance(new String[][] {{"calc"}})).start();
简单解释下这两个参数 String[].class
是 ProcessBuilder
类构造函数的参数类型,newInstance
参数也是可变长参数,等价于 Object[] initargs
,即套两层数组 new String[][] {{"calc"}}
上述用到了强制类型转换,将上述payload改改,用完全反射的方法
Class clazz = Class.forName("java.lang.ProcessBuilder");
Constructor m = clazz.getConstructor(String[].class);
clazz.getMethod("start").invoke(m.newInstance(new String[][] {{"calc"}}));
写成一行
Class.forName("java.lang.ProcessBuilder").getMethod("start").invoke(Class.forName("java.lang.ProcessBuilder").getConstructor(String[].class).newInstance(new String[][] {{"calc"}}));
ProcessBuilder类探究
先来看看 Runtime
类是如何处理命令的。
如果直接 exec
传入的是字符串的话
Runtime.getRuntime().exec("calc");
可以看到经过最终会跳转到 exec
的另一个重载方法上。
上面也说过可以直接利用 ProcessBuilder
类执行命令
new ProcessBuilder("calc").start();
ProcessBuilder
类中的一个构造方法参数是可变参数,即可以传递 String[]
类型
即
Runtime.getRuntime().exec("calc");
底层就是执行了
new ProcessBuilder(new String[] {"calc"}).start();
相关文章
- Java对象数组
- java 把对象转成map_Java对象转换成Map[通俗易懂]
- Java服务器接收上传的文件
- java 图片识别 tess4j_JAVA使用Tess4J进行ocr识别
- Java进阶:java开源商城系统源码
- C语言和JAVA的区别[通俗易懂]
- java最新漏洞_JavaMelody XXE漏洞(CVE-2018-15531)分析
- MySQL字段类型如何转为java_Java JDBC中,MySQL字段类型到JAVA类型的转换
- 手机版java编译器_Java编译器[通俗易懂]
- java线程池参数详解
- java网页安全提示_win7系统打开网页提示“应用程序已被JAVA安全阻止”的解决方法…
- java arraylist遍历_遍历ArrayList的4种方法
- java 构造器 构造方法_Java构造器(构造方法/constructor)
- docker启动mysql命令_docker部署java环境
- c 线程安全的单例模式-c多线程并发处理方式_Java多线程面试题:线程锁+线程池+线程同步等
- 如何使用Ermir研究Java RMI Registry安全
- Java获取本地机器MAC地址详解编程语言
- Java实现插入排序详解编程语言
- 学习如何使用Linux环境运行Java程序(linux下的java)
- Java五子棋游戏(附带源码和解析)
- key失效Redis Java API实现key的自动失效(redisjava过期)
- 时间设置Java中Redis设置Key的过期时间(redisjava过期)
- 时间解决Java中Redis数据过期时间问题(redisjava过期)
- Java程序在Linux系统中实现命令操作(java运行linux命令)
- Java程序构建Oracle数据库直连(java直连oracle)
- 一部分Oracle驱动Java的成功之路(java是oracle的)
- 深入Java不可变类型的详解
- 深入解析java中的静态代理与动态代理