Java反射(二)
在一个类中没有无参构造方法,也没有类似单列模式留下的静态方法
我们可以使用新方法 getConstructor .
和getMethod类似,getConstructor 接收的参数是构造函数列表类型,因为构造函数也支持重载,
所以必须用参数列表类型才能唯一确定一个构造函数。
我们常用的另一种执行方法是 ProcessBuilder,我们使用反射来获取其构造函数,然后调用**start()**来执行函数
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.Constructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
ProcessBuilder有两个构造函数
- public ProcessBuilder(List
command) - public ProcessBuilder(String… command)
先讲上面那个进行反射转换
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe")));

因为ProcessBuilder有两个构造函数,所以我们还有另外一种写法
public ProcessBuilder(String… command) 这个构造函数涉及到了变长参数(varargs)
对于可变长参数,Java其实在编译的时候会编译成一个数组,也就是说,如下这两种写法在底层是等价
的(也就不能重载):
public void Hello(String[] names){}
public void Hello(String...names){}
那么对于反射来说,如果要获取的目标函数里包含可变长参数,其实我们认为它是数组就行了。
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getConstructor(String[].class)
在调用 newInstance 的时候,因为这个函数本身接收的是一个可变长参数,我们传给
ProcessBuilder 的也是一个可变长参数,二者叠加为一个二维数组,所以整个Payload如下:
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
((ProcessBuilder)clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}})).start();
进行反射转换
Class<?> clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(String[].class).newInstance(new String[][]{{"calc.exe"}}));

如果一个方法是构造方法或是私有方法,我们该如何执行
使用方法getDeclared系列的反射,与普通的getMethod、getConstructor的区别是:
- getMethod 系列方法获取的是当前类中所有公共方法,包括从父类继承的方法
- getDeclaredMethod 系列方法获取的是当前类中“声明”的方法,是实在写在这个类里的,包括私有的方法,但从父类里继承来的就不包含了
Class<?> clazz = Class.forName("java.lang.Runtime");
Constructor m = clazz.getDeclaredConstructor();
m.setAccessible(true);
clazz.getMethod("exec",String.class).invoke(m.newInstance(),"calc.exe");
这里使用了一个方法 setAccessible ,这个是必须的。我们在获取到一个私有方法后,必须用
setAccessible 修改它的作用域,否则仍然不能调用
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Antifrag!







