前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >初探java安全之反射(2)

初探java安全之反射(2)

作者头像
pankas
发布2022-10-27 14:55:39
4080
发布2022-10-27 14:55:39
举报

前言

前面总结了,java反射的一些函数的相关用法,本篇主要总结如何利用这些函数来构造执行函数。

重要方法

其实上篇总结过,这里再加几个

  • 获取类的方法 foName
  • 实例化类对象的方法 newInstance
  • 获取函数的方法 getMethod
  • 执行函数的方法 invoke
  • 获得构造方法 getConstructor
newInstance()

该方法属于 Class 类,执行后返回一个 Object,可以利用这个方法来实例化对应的类,作用就是调用这个类的无参构造函数。但要求要实例化的类必须要有无参构造函数,并且这个构造函数不是私有的。

Runtime类的分析

进入 Runtime 类中可以看到

image-20221009132921776
image-20221009132921776

发现 Runtime 类使用的单例模式,即只能通过 getRuntime() 方法来获取 Runtime 对象。同时注意 getRuntime 对象是静态方法,即可以直接通过 类名.方法名 的方法调用。

java执行命令

Runtime run = Runtime.getRuntime();//获取 Runtime 对象
run.exec("calc");//执行calc命令弹出计算器
getMethod()

该方法可获取一个 Method 对象,即获取类中的方法,通常要和 invoke() 方法一起使用,这里重点关注下它的各项参数

image-20221009133822176
image-20221009133822176

第一个参数 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 获取的方法,看看它的相关参数

image-20221009135348425
image-20221009135348425

第一个参数是函数所在的对象或**类(执行静态方法可用)**,第二个参数是所执行的函数的对象参数

依旧拿 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");

利用反射执行命令

总结上面的 getMethodinvoke ,大白话说就是我们一般这个样执行函数

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)

image-20221009225753892
image-20221009225753892

该方法的参数是获取到的构造方法中对应参数的数据类型,该方法返回的 Constructor 对象中有个 newInstance 方法,看看这个 newInstance 方法的参数

image-20221009225056940
image-20221009225056940

newInstance 方法的参数是调用的构造方法对应的参数。

比如我们利用 ProcessBuilder 类来执行命令

new ProcessBuilder("calc").start();

注意这里用到的 ProcessBuilder 重载的构造方法

image-20221009231722895
image-20221009231722895

String... command 等价于 String[] command

使用反射构造

 Class clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder) clazz.getConstructor(String[].class).newInstance(new String[][] {{"calc"}})).start();

简单解释下这两个参数 String[].classProcessBuilder 类构造函数的参数类型,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");
image-20221009234159815
image-20221009234159815

可以看到经过最终会跳转到 exec 的另一个重载方法上。

上面也说过可以直接利用 ProcessBuilder 类执行命令

new ProcessBuilder("calc").start();
image-20221009234720867
image-20221009234720867

ProcessBuilder 类中的一个构造方法参数是可变参数,即可以传递 String[] 类型

Runtime.getRuntime().exec("calc");

底层就是执行了

new ProcessBuilder(new String[] {"calc"}).start();
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 重要方法
    • newInstance()
      • Runtime类的分析
        • getMethod()
          • invoke()
            • 利用反射执行命令
              • getConstructor和getDeclaredConstructor
              • ProcessBuilder类探究
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档