java基础--反射2

上篇文章对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<String> command)

那么我们有什么方法获取这个函数呢?

可以使用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背景可以评论哦!

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值