java_cc链小结
##前言
感觉自己最近越来越浮躁了,也没有静下心来好好分析点什么,所以这次打算好好整理一下之前学过的CC链
##总览
##CC1
1.适用版本:commons-collections 3.2.1
2.细节描述
在构造链子的过程中一般是倒着来看感觉会比较顺一点,所以我们从InvokeTransform
来看:
在InvokeTransform
中transform
方法中实现了getMethod和invoke方法,刚好可以将我们的Runtime.exe那一段进行命令执行
这里用了一个chainedTransform数组进行一个整体的transform起到简化代码的作用,接下来我们只需要寻找哪里调用了transform就可以,
可以看到,在LazyMap
中的get
方法中调用了transform
,接下来关注一下这factory
是如何传入的,可以发现,这边是LazyMap的构造方法,由于是protected,还需要使用反射的方式来进行赋值
但是再往下看,发现其decorate
方法会直接返回一个LazyMap对象
,所以我们直接调用这个方法进行赋值就行了
具体赋值方式如下:
接下来我们要看一下哪里调用了这个get,在annotationinvocationhandler的invoke方法中就调用了这个get,但是需要思考的是,如何触发这个invoke方法,这里我们回想一下php里面的__call方法,通过调用不存在的方法,就会调用到invoke方法中,那么接下来我们就只需要寻找符合条件的即可这里需要注意的是,我们要使用无参的方法
,正好在readObject中就有一个符合条件的,但是要在其他类的内部进行这操作,我们需要借助代理
因此赋值方式如下:
首先关注一下其构造方法,需要传入的类型,直接传入的话就会对我们需要的memberValue
赋值了
//反射调用AnnotationInvocationHandler类 |
另外一条cc1链比较简单,就不细讲了,
##CC6
1.适用版本:没有版本限制
在CC6中,主要是前半部分有区别,在调用到get部分采用的是TiedMapEntry.hashCode
方法,因为在hashcode中会调用到getValue方法
并且在getValue中会调用到get方法
而这里是对map进行调用了,我们看看map是如何赋值的,直接构造方法穿参就可以了
接下来就是寻找哪里有调用这个hashcode了,可以看到在HashMap的readObject中就有调用
那么解析来就来看一下两处是如何赋值的:TiedMapEntry.hashcode
就是采用直接赋值的形式就可以了,因为他有这个构造函数
接下来就是赋值到hashmap里面去,因为,hashmap其实是一个键值对,前面是key,后面是value
然后我们接下来就是对其赋值,但是在hashmap里面,要对其键值对进行赋值需要调用put函数,但是这还不够,由于在put的时候就有hash了
也就是这里
然后接下来他就会走一遍我们之前的链子了,那么这样会造成什么后果呢
我们往下继续看一下,在反序列化的过程中,此时由于我们前面put过一次了,所以此时就contains那个key,那么就进不到这个transform里面,因此我们这条链就断了
所以此时,我们首先需要将这个a给remove掉,在反序列化的时候才能进入
ok,到这里我们捋一下
- 由于在对map2进行put的赋值操作的时候,走到了get方法,此时说get方法就会判断是否还有key,因为是第一次走到这里,所以肯定是没有这个key的,那么就进入到transform中,并且进行了一个put的操作,此时这个hashmap就有了这个key了
- 针对以上问题,我们首先要在put以后将这个key给删掉,也就是使用上面的remove操作
接下来是第二个问题,如何解决序列化过程就命令执行这一问题,那么就是在一开始的先赋值一个不存在命令执行效果的
然后再利用反射更改值
##cc3
1.invoketransform被过滤、需要调用字节码
2.代码执行—(动态类加载)
我们要使用defineclass
调用字节码实现动态类加载以达到命令执行的目的,那么就需要找到一处有调用newinstance
的
首先,我们找到的是TemplatesImpl.newTransformer
里面有一个getTransletInstance
在这里面就有一个newInstance
而这个newInstance前面调用的内容,来源于上面这个函数
可以发现这里就实现了使用defineclass对_class[i]
的赋值
首先我们来看这一部分如何赋值:
在上面的代码中,我们发现,要到newInstance
的位置,需要对__name 和__bytecodes(传入的字节码)进行赋值
看看构造函数,发现是无参的,所以我们直接利用反射来更改值就行
根据前面的分析,我们进行了一下的赋值
但是此时还是无法成功命令执行
因为在defineclass中,如果没有满足父类是ABSTRACT_TRANSLET
则会报错
因此,我们在前面些字节码的类中,还需要加上下面的extends
此时才可以成功执行
而这部分内容,其实是我们为了在序列化的时候测试能否成功命令执行而加上去的
这个_tfactory的属性是transient,是无法被序列化的,但是在readObject中会自动赋值,所以我们可以不用管
前面的部分进行transform方法以后就可以实现命令执行了,接下来就只需要将后面的方法完善好就行,如果invoketansform还能用的话,直接进行以下方法的调用即可
在cc3中,和前面两条链子最大的不同就是后半段,前面是适用invoketransform来进行命令执行的触发,这里需要对invoketransform
进行一下解释,这个类,里面的tansform实现了getMethod和invoke方法,那就是可以反射调用任意的方法并执行,所以前面才说调用newTransformer和templatesImple就呼应上了,但是如果过滤了invoketransform
,那么此时我们就要另外寻找调用了newTransformer的地方——TrAXFilter
这个类
还有一种是利用TrAXFilter
的构造方法
在这里调用了newTransformer,所以我们直接对其构造函数进行赋值就行了,接下来就是找哪里有触发获取构造函数的位置,在InstantiateTransformer
的transform
中使用了getConstructor
跟进去可以发现,这里就会调用前面的TrAXFilter
的构造方法,并对其赋值为templatesImple
接下来就看看如何对其进行赋值,这里参考一下InstantiateTransformer
的构造函数以及参数就可以了
cc3主要是梳理这些即可
##cc4
1.版本:cc4
##cc2
1.这条cc链是没有用到数组的
然后这里的put我们跟一下,连同上面的cc4一起理解
InvokerTransformer<Object,Object> invokerTransformer = new InvokerTransformer<>("newTransformer",new Class[]{},new Object[]{}); |
首先size要大于等于2,不然无法进入到siftDown,也就无法执行后续的链条了
接下来是哪个部分放template的问题,可以发现如果是第一个元素是其他值,那么就会造成这里发生错误,因为1.getClass明显是有问题的
##cc5
##cc7
往上调用equals,最后调用到父类的,然后转到get
c