上篇文章对java反射有了一些基本的了解,对反射的理解可以是通过奇技淫巧来执行自己要执行的代码,但表达肯定没有实际 *** 作来的明白,下面就使用使用反射来进行命令执行
一. 环境版本
JDK:1.8.0_311.jdk
二.实验过程
1.Runtime新建一个reflection.java
首先是最基本的调用
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
然后下面是使用反射执行,可以发现使用反射的话就不会出现Runtime类的定义
Class name = Class.forName("java.lang.Runtime");
Method method = name.getMethod("exec", String.class);
Method getruntime = name.getMethod("getRuntime");
Object obj = getruntime.invoke(getruntime);
method.invoke(obj,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
这里就出现了问题,为啥要获取getRuntime方法!invoke咋用!
首先是第一个问题,这里就涉及到Runtime这个类的定义了,我们先看一下定义
发现了吧,其实Runtime.Runtime是个私有方法!如果依照我们上篇文章的使用正常的反射来构建
Class name = Class.forName("java.lang.Runtime");
Runtime run = (Runtime) name.newInstance();
run.exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
那么会报错,说反射不能调用私有方法
所以我们不能将其实例化成Runtime对象,那我们就需要从Runtime本身入手,发现其存在getRuntime()方法,会返回currentRuntime,而currentRuntime会new Runtime(),这种就是一个简单的gadget,就需要上面的那种反射调用了。
invoke的用法具体的理解就是:method.invoke(obj,args),我的理解就是想使用某个类的某个方法,前提是能获取到,通过invoke就可以执行这个类方法,invoke中的第一个参数是获取的class,第二个参数是给这个要执行的方法的参数
例:Runtime.getRuntime exec()
获取到class=Runtime.getRuntime.
获取到method=exec()
执行exec method.invoke(class,"calc.exe")
2.ProcessBuilder其实使用Runtime执行命令最终也会来到ProcessBuilder中,最后调用start()方法
根据这个的利用,我们可以知道需要在新建ProcessBuilder时传入我们的命令,然后调用start函数即可,那么我们需要先看一下ProcessBuilder.start()是不是私有的,或者有其它方法使用,发现是public,那么可以直接使用。
那么问题来了,我们怎么给获取的class来传参呢?
问的好!据我所知,我不会!但是我们可以找ProcessBuilder其中的方法,就比如 ProcessBuilder(List
那么我们有什么方法获取这个函数呢?
可以使用getConstructor来获取特征类型或者个数的参数的函数
那么我们可以这样,获取List类型的ProcessBuilder
Class name = Class.forName("java.lang.ProcessBuilder");
Method method = name.getMethod("start");
Constructor cons = (Constructor) name.getConstructor(List.class);
Object obj = cons.newInstance();
但是,运行报错了,可能有人会问,为啥还有别的参数类型和个数和这个的一样,你咋能确定获取到这个呢?
因为:
根据这我们可以大致知道, getConstructor会获取与类名相同的方法,具体原因还不是很能说明白,等稍微我去找找资料。
那么回到上面的话题,报错了!且错误是参数个数不对
这证明我们需要添加参数,那么只能往newInstance中添加了,这正好能达到我们传参数的目的吧,但参数类型还需要为List.class,那么可以使用Arrays.asList();那么最终为
Class name = Class.forName("java.lang.ProcessBuilder");
Method method = name.getMethod("start");
Constructor cons = (Constructor) name.getConstructor(List.class);
Object obj = cons.newInstance(Arrays.asList("/System/Applications/Calculator.app/Contents/MacOS/Calculator"));
method.invoke(obj);
在看p神的文章的时候学习到了一些有用的知识,正好也可以使用。
在ProcessBuilder中有两个ProcessBuilder
两者的差异就是参数不同,第一个好理解,但第二个是String... ,这个其实也好理解,就是支持传入多个参数,也是为了方便使用,这种我们可以传参字符串数组即String[],看这函数中对传入的command也是放在一个ArrayList中,于是我们传入字符串数组即可,在这个中利用如下
Class name = Class.forName("java.lang.ProcessBuilder");
Method method = name.getMethod("start");
Constructor cons = (Constructor) name.getConstructor(String[].class);
Object obj = cons.newInstance(new String[][] {{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}});
method.invoke(obj);
到这也可能会有疑问,为啥传入的是二位数组啊?
问的好!
因为这个newInstance也是个...,即可变长参数,所以需要二维数组传参数。
3.ProcessImpl其实在使用ProcessBuilder中,其最终还是调用ProcessImpl的start方法
首先我们看一下它的内置方法
发现start是static(关于这些将在下一篇文章写写如static,public... )的,我们试试直接调用看看,这些参数就是定义名加class即可
Class name = Class.forName("java.lang.ProcessImpl");
Method method = name.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
method.invoke(name);
报错说不能调用static方法
那么我们可以使用setAccessible(true),设置作用域
Class name = Class.forName("java.lang.ProcessImpl");
Method method = name.getDeclaredMethod("start", String[].class,Map.class,String.class,ProcessBuilder.Redirect[].class,boolean.class);
method.setAccessible(true);
method.invoke(null, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}, null, null, null, false);
然后即可实现成功。
有了这个,那么我们再回到Runtime
首先我么使用getDeclaredConstructors获取到该类的与类名相同的方法,即Runtime()
可以看到是private类型,那么我们使用setAccessible(true),然后构造发现可以执行了!
Class name = Class.forName("java.lang.Runtime");
Constructor cons = name.getDeclaredConstructor();
cons.setAccessible(true);
Object obj = cons.newInstance();
Method method = name.getMethod("exec", String.class);
method.invoke(obj,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
那么可以使用这种方法来执行private类型的方法
4.ScriptEngineManager本质还是上面三种方法,主要是体现命令执行哈,没体现反射
public class EvalTest {
public static void main(String[] args) throws ScriptException {
Object result = new ScriptEngineManager().getEngineByExtension("js").eval("java.lang.Runtime.getRuntime().exec(\"calc\")");
System.out.println(result);
}
}
——————————————————————————————————————————
需要同款IDEA背景可以评论哦!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)