[Android] 深入 Binder 拦截 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iofomo
V2EX    Android

[Android] 深入 Binder 拦截

  •  1
     
  •   iofomo 2024-04-23 12:33:07 +08:00 9574 次点击
    这是一个创建于 605 天前的主题,其中的信息可能已经有所发展或是发生改变。

    基于底层动态拦截技术,实现对 Android 平台下应用进程 Binder 通信协议的动态分析和拦截。

    Gthub    Gitee

    说明

    Binder作为Android系统跨进程通信的核心机制。网上也有很多深度讲解该机制的文章,如:

    这些文章和系统源码可以很好帮助我们理解 Binder 的实现原理和设计理念,为拦截做准备。借助 Binder 拦截可以我们可以扩展出那些能力呢:

    1. 虚拟化的能力,多年前就出现的应用免安装运行类产品如:VirtualApp/DroidPlugin/平行空间/双开大师/应用分身等。
    2. 测试验证的能力,通常为Framework层功能开发。
    3. 检测第三方SDK或模块系统服务调用访问情况(特别是敏感API调用)。
    4. 逆向分析应用底层服务接口调用实现。
    5. 第三方ROM扩展Framework服务。

    现有方案

    一直以来实时分析和拦截进程的Binder通信是通过Java层的AIDL接口代理来实现的。借助于Android系统Binder服务接口设计的规范,上层的接口均继承于IBinder

    如一下为代理目标对象的所有的接口API的方法:

    import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; private static void getInterface(Class<?> cls, final HashSet<Class<?>> ss) { Class<?>[] ii; do { ii = cls.getInterfaces(); for (final Class<?> i : ii) { if (ss.add(i)) { getInterface(i, ss); } } cls = cls.getSuperclass(); } while (cls != null); } private static Class<?>[] getInterfaces(Class<?> cls) { final HashSet<Class<?>> ss = new LinkedHashSet<Class<?>>(); getInterface(cls, ss); if (0 < ss.size()) { return ss.toArray(new Class<?>[ss.size()]); } return null; } public static Object createProxy(Object org, InvocationHandler cb) { try { Class<?> cls = org.getClass(); Class<?>[] cc = getInterfaces(cls); return Proxy.newProxyInstance(cls.getClassLoader(), cc, cb); } catch (Throwable e) { Logger.e(e); } finally { // TODO release fix proxy name } return null; } 

    1 、对于已经生成的Binder服务对象,在应用进程可参与实现逻辑之前就已经缓存了,我们需要找到并且进行替换(AMS 、PMS 、WMS 等),如AMS在 Android 8.0 之后的缓存如下:

    // source code: http://aospxref.com/android-9.0.0_r61/xref/frameworks/base/core/java/android/app/ActivityManager.java package android.app; public class ActivityManager { public static IActivityManager getService() { return IActivityManagerSingleton.get(); } private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } }; } 

    因此我们需要找到并且替换它,如:

    Object obj; if (Build.VERSION.SDK_INT < 26) {// <= 7.0 obj = ReflectUtils.getStaticFieldValue("android.app.ActivityManagerNative", "gDefault"); } else {// 8.0 <= obj = ReflectUtils.getStaticFieldValue("android.app.ActivityManager", "IActivityManagerSingleton"); } Object inst = ReflectUtils.getFieldValue(obj, "mInstance"); ReflectUtils.setFieldValue(obj, "mInstance", createProxy(inst)); 

    2 、对于后续运行过程中才获取的Binder服务,则需要代理ServiceManager,源码如下:

    // source code: http://aospxref.com/android-9.0.0_r61/xref/frameworks/base/core/java/android/os/ServiceManager.java package android.os; public final class ServiceManager { private static final String TAG = "ServiceManager"; private static IServiceManager sServiceManager; } 

    因此我们的代理如下:

    Class<?> cls = ReflectUtils.findClass("android.os.ServiceManager"); Object org = ReflectUtils.getStaticFieldValue(cls, "sServiceManager"); Object pxy = new createProxy(org); if (null != pxy) { ReflectUtils.setStaticFieldValue(getGlobalClass(), "sServiceManager", pxy); } 

    这样每次在第一次访问该服务时,就会调用IServiceManager中的getService的方法,而该方法已经被我们代理拦截,我们可以通过参数可以识别当前获取的是哪个服务,然后将获取的服务对象代理后在继续返回即可。

    但是:这样的方案并不能拦截进程中所有的Binder服务。我们面临几大问题:

    1. 首先,Android 源码越来越庞大,了解所有的服务工作量很大,因此有哪些服务已经被缓存排查非常困难。

    2. 其次,厂商越来越钟情于扩展自定义服务,这些服务不开源,识别和适配更加耗时。

    3. 再次,有一部分服务只有native实现,并不能通过Java层的接口代理进行拦截(如:Sensor/Audio/Video/Camera 服务等)。

      // source code: http://aospxref.com/android-13.0.0_r3/xref/frameworks/av/camera/ICamera.cpp class BpCamera: public BpInterface<ICamera> { public: explicit BpCamera(const sp<IBinder>& impl) : BpInterface<ICamera>(impl) { } // start recording mode, must call setPreviewTarget first status_t startRecording() { ALOGV("startRecording"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); remote()->transact(START_RECORDING, data, &reply); return reply.readInt32(); } } 

    新方案:基于底层拦截

    原理

    我们都知道Binder在应用进程运行原理如下图:

    不管是Java层还是native层的接口调用,最后都会通过ioctl函数访问共享内存空间,达到跨进程访问数据交换的目的。因此我们只要拦截ioctl函数,即可完成对所有Binder通信数据的拦截。底层拦截有以下优势:

    1 )可以拦截所有的 Binder 通信。

    2 )底层拦截稳定,高兼容性。从Android 4.xAndroid 14,近 10 年的系统版本演进,涉及到Binder底层通信适配仅两次;一次是支持 64 位进程(当时需要同时兼容 32 位和 64 位进程访问Binder服务)。另一次是华为鸿蒙系统的诞生,华为ROMBinder通信协议中增加了新的标识字段。

    要解决的问题

    如何拦截

    C/C++层的函数拦截,并不像Java层一样系统提供了较为稳定的代理工具,在这里不是我们本期讨论的重点,可以直接采用网上开源的Hook框架:

    如何过滤

    ioctl函数为系统底层设备访问函数,调用及其频繁,而Binder通信调用只是其中调用者之一,因此需要快速识别非Binder通信调用,不影响程序性能。

    函数定义:

    #include <sys/ioctl.h> int ioctl(int fildes, unsigned long request, ...); 

    request的参数定义:

    // source code: http://aospxref.com/android-14.0.0_r2/xref/bionic/libc/kernel/uapi/linux/android/binder.h #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32) #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) #define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) #define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) #define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) #define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) #define BINDER_GET_EXTENDED_ERROR _IOWR('b', 17, struct binder_extended_error) 

    对应的源码:

    // source code: http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder/IPCThreadState.cpp void IPCThreadState::threadDestructor(void *st) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received) { return ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info); } status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { return ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0); } void IPCThreadState::logExtendedError() { ioctl(self()->mProcess->mDriverFD, BINDER_GET_EXTENDED_ERROR, &ee) < 0); } status_t IPCThreadState::talkWithDriver(bool doReceive) { // 实际 Binder 调用通信 return ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr); } 

    快速过滤:

    static int ioctl_hook(int fd, int cmd, void* arg) { if (cmd != BINDER_WRITE_READ || !arg || g_ioctl_disabled) { return g_ioctl_func(fd, cmd, arg); } } 
    如何解析

    目标源码: http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder

    重点解析发送(即BC_TRANSACTIONBC_REPLY)和接收(即BR_TRANSACTIONBR_REPLY)的类型数据。

    如何修改数据

    修改数据分为以下几种:

    1 )修复调用时参数数据。

    2 )修复调用后返回的结果数据。

    如果数据修复不改变当前数据的长度,只是内容的变化,则可以直接通过地址进行修改。否则需要创建新的内存进行修改后将新的数据地址设置到BINDER_WRITE_READ结构的buffer成员。此时处理好内存的释放问题。

    3 )直接拦截本次调用。

    为了保障稳定性,不打断Binder的调用流程(通常这也是拦截和逆向方案保障稳定的最重要原则之一)。我们可以将目标函数code修改成父类处理的通用方法,然后通过修复调用的返回值即可完成拦截。

    方案实现

    数据解析

    Binder 调用数据结构如下:

    解析bwr

    bwrbinder_write_read,从源码中了解到ioctlBINDER_WRITE_READ类型的arg数据结构为:

    struct binder_write_read { // 调用时传入的数据 binder_size_t write_size;// call data binder_size_t write_consumed;// call data binder_uintptr_t write_buffer;// call data // 结果返回数据 binder_size_t read_size;// recv data binder_size_t read_consumed;// recv data binder_uintptr_t read_buffer;// recv data }; 

    不管是传入还是返回的数据,都是一组 BC 命令或 BR 命令,也就是说一次调用上层会打包几个命令一起传递。因此我们需要通过循环来找到我们的命令。

    void binder_find_for_bc(struct binder_write_read& bwr) { binder_uintptr_t cmds = bwr.write_buffer; binder_uintptr_t end = cmds + (binder_uintptr_t)bwr.write_size; binder_txn_st* txn = NULL; while (0 < cmds && cmds < end && !txn) { // 由于每次 Binder 通信数据量的限制,Binder 设计每次调用有且仅包含一个有效的参数命令,因此只要找到即可,其他类型则直接跳过忽略 cmds = binder_parse_cmds_bc(cmds, txn); } } 

    dump数据如下:

    write_buffer:0xb400007107d1d400, write_consumed:68, write_size:68 00000000: 00 63 40 40 14 00 00 00 00 00 00 00 00 00 00 00 .c@@............ 00000010: 00 00 00 00 01 00 00 00 12 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00 ....T........... 00000030: 00 00 00 00 00 4d 3a ac 70 00 00 b4 00 00 00 00 .....M:.p....... 00000040: 00 00 00 00 .... BR_NOOP: 0x720c BR_TRANSACTION_COMPLETE: 0x7206 BR_REPLY: 0 
    解析txn

    txnbinder_transaction_data,Binder 方法调用的方法参数信息定义如下:

    struct binder_transaction_data { union { __u32 handle; binder_uintptr_t ptr; } target;// 目标服务句柄,server 端使用 binder_uintptr_t cookie;// 缓存的 Binder 进行访问 __u32 code;//方法编号 __u32 flags;// 标识,如是否为 oneway __s32 sender_pid; __u32 sender_euid; binder_size_t data_size;// 数据长度 binder_size_t offsets_size;// 若包含对象,则对象数据大小 union { struct { binder_uintptr_t buffer;// Binder 方法参数值地址 binder_uintptr_t offsets;// Binder 方法参数对象数据地址 } ptr; __u8 buf[8]; } data; }; 

    dumo数据如下:

    Trace : target: 1 cookie: 0 code: 23 flags: 0x12(READ REPLY) Trace : pid: 0 uid: 0 size: 196 offs:8 Trace : 00000000: 00 00 00 80 ff ff ff ff 54 53 59 53 1c 00 00 00 ........TSYS.... Trace : 00000010: 61 00 6e 00 64 00 72 00 6f 00 69 00 64 00 2e 00 a.n.d.r.o.i.d... Trace : 00000020: 61 00 70 00 70 00 2e 00 49 00 41 00 63 00 74 00 a.p.p...I.A.c.t. Trace : 00000030: 69 00 76 00 69 00 74 00 79 00 4d 00 61 00 6e 00 i.v.i.t.y.M.a.n. Trace : 00000040: 61 00 67 00 65 00 72 00 00 00 00 00 85 2a 62 73 a.g.e.r......*bs Trace : 00000050: 13 01 00 00 00 38 dd 2a 71 00 00 b4 00 05 e9 31 .....8.*q......1 Trace : 00000060: 71 00 00 b4 01 00 00 0c 1a 00 00 00 63 00 6f 00 q...........c.o. Trace : 00000070: 6d 00 2e 00 69 00 66 00 6d 00 61 00 2e 00 74 00 m...i.f.m.a...t. Trace : 00000080: 72 00 61 00 6e 00 73 00 65 00 63 00 2e 00 63 00 r.a.n.s.e.c...c. Trace : 00000090: 6f 00 6e 00 74 00 61 00 69 00 6e 00 65 00 72 00 o.n.t.a.i.n.e.r. Trace : 000000a0: 00 00 00 00 08 00 00 00 73 00 65 00 74 00 74 00 ........s.e.t.t. Trace : 000000b0: 69 00 6e 00 67 00 73 00 00 00 00 00 00 00 00 00 i.n.g.s......... Trace : 000000c0: 01 00 00 00 .... Trace : binder object offs:0x4c type:0x73622a85 flags:0x113 ptr:0x2add3800 cookie:0x31e90500 
    解析服务名

    Binder通信数据头如下,即可解析出目标服务名:

    void find_server_name(const binder_txn_st* txn) { const int32_t* ptr = reinterpret_cast<const int32_t*>(txn->data.ptr.buffer); ++ ptr;// skip strict model if (29 <= sdkVersion()) ++ ptr;// 10.0 <=, skip flags(ff ff ff ff) int32_t nameLen = *ptr; const uint16_t* name16 = (const uint16_t*)(ptr+1); } 
    解析方法名

    Binder通信数据中标识该服务方法的参数是txn->codeAIDL定义类在编译后会为每个方法自动生成静态的方法。

    如定义的Binder接口方法为:

    interface IDemo { void test(); void test2(); } 

    则编译后生成的类为:

    class IDemo$Stub { void test(); void test2(); static final int TRANSACTION_test = 1; static final int TRANSACTION_test2 = 2; } 

    因此我们可以通过反射的方式,找到服务名对应的类所有静态成员变量,然后找到与code值相等的成员即为此方法。

    这里可能需要解决私有 API 的限制解除问题。

    // 可直接使用工程工具类 TstClassPrinter.printStubByCodes("android.app.IActivityManager", 13, 16, 67); 

    日志输出如下:

    解析数据

    首先需要借助数据封装类Parcel

    // souce code: // http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/include/binder/Parcel.h // http://aospxref.com/android-14.0.0_r2/xref/frameworks/native/libs/binder/Parcel.cpp 

    借助该类可以解析一些比较简单的数据,快速的找到目标内容。而对于比较复杂的数据,如参数值为Intent,该参数类型嵌套了多层的Parcelable成员,因此在native层通过Parcel来解析,兼容性比较差。因此我们选择通过回调到 Java 层来解析,修改后再格式化为nativebuffer数据。这里需要处理好Javanative层的数据交换问题,以及回收。

    native层:

    // 创建 jobject obtain(JNIEnv* env) { jclass jcls = env->FindClass("android/os/Parcel"); jmethodID method = env->GetStaticMethodID(jcls, "obtain", "()Landroid/os/Parcel;"); if (!method) return NULL; mParcelObj = env->CallStaticObjectMethod(jcls, method); if (!mParcelObj) return NULL; if (0 < mUparcel->dataSize()) { method = env->GetMethodID(sParcelClass, "setDataPosition", "(I)V"); if (method) { unmarshall(env, mUparcel->data(), mUparcel->dataSize()); env->CallVoidMethod(mParcelObj, method, mUparcel->dataPosition()); } } return mParcelObj; } // 回收 void recycle(JNIEnv* env) { jclass jcls = env->FindClass("android/os/Parcel"); jmethodID method = env->GetMethodID(jcls, "recycle", "()V"); if (method) { env->CallVoidMethod(mParcelObj, method); } if (mParcelObj) { env->DeleteLocalRef(mParcelObj); } mParcelObj = NULL; } 

    Java层:

    public static void clearHttpLink(Parcel p/*IN*/, Parcel q/*OUT*/) { try { Intent ii = Intent.CREATOR.createFromParcel(pp); // TODO something ... // write new data q.appendFrom(p, p.dataPosition(), p.dataAvail()); } catch (Throwable e) { e.printStackTrace(); } } 

    数据拦截

    Binder 的数据解析和打印不会改变原数据内容,因此相对简单,如果要对数据进行修改,则相对复杂一些。修复的数据需要替换原数据,因此需要进行如下操作。

    1 、数据替换。

    txn中方法参数的数据指针指向新创建的数据区。

     int binder_replace_txn_for_br(binder_txn_st *txn, ParcelEx* reply, binder_size_t _pos) { size_t size = reply->ipcDataSize(); uint8_t* repData = (uint8_t*)malloc(size + txn->offsets_size); memcpy(repData, reply->data(), size); if (0 < txn->offsets_size) { binder_replace_objects(txn, repData, _pos, ((int)size) - ((int)(txn->data_size))); } txn->data.ptr.buffer = reinterpret_cast<uintptr_t>(repData); txn->data_size = size; return 0; } 

    2 、修正对象指针。

    如果传入的参数包含 Binder 对象,如register方法的Observe。因此修复的数据可能导致偏移的地址前移或者后移,因此需要重新计算偏移,如:

    void replaceObjects(binder_txn_st *txn, uint8_t* objData, binder_size_t _pos, int _off) { binder_size_t* offs = reinterpret_cast<binder_size_t*>(txn->data.ptr.offsets); unsigned count = txn->offsets_size / sizeof(binder_size_t); while (0 < count--) { if (0 != memcmp(objData + (int)(*offs), (uint8_t*)txn->data.ptr.buffer + (int)(*offs), sizeof(binder_size_t))) { *offs += _off; } ++ offs; } } 

    3 、内存释放。

    需要保存原地址A和新的地址AA的映射关系到自定义的内存池中。

    Binder通信命令出现BC_FREE_BUFFERBR_FREE_BUFFER时,则通过该命令要释放的AA地址,然后从内存池找到与之对应A的地址,并设置回去让上层继续释放,完成内存使用的闭环。

    case BC_FREE_BUFFER: { uintptr_t* buffPtr = (uintptr_t *)cmd; uintptr_t ptr = MemPool::detach(*buffPtr); if (__UNLIKELY(0 != ptr)) { *buffPtr = ptr;// set origin buffer } cmd += sizeof(uintptr_t);// move to next command } break; 

    附:

    如果你有需要,可以直接使用我们已经封装好的SDK来实现相应的功能,该项目已经开源,可以直接使用,参考 [集成文档]

    12 条回复    2024-04-28 09:37:27 +08:00
    nnegier
        1
    nnegier  
       2024-04-23 13:55:27 +08:00 via Android
    大佬好,我想借此问一下 Binder 一次传输的大小是不是有限制?如果有,这个限制怎么解决呢?(问题来源在我使用 Shizuku 时感觉到)
    52MF4yM298P2vX26
        2
    52MF4yM298P2vX26  
       2024-04-23 13:55:59 +08:00
    有什么使用的场景
    iofomo
        4
    iofomo  
    OP
       2024-04-23 17:57:59 +08:00
    @nnegier 是的,你的问题很赞。
    一次 Binder 通信数据有最大的 1M 限制( Android 源码默认),如果超过这个大小了,在 Binder 通信中会有多次的数据交互,如常见的 IPackageManager 的 getInstalledPackages 就会碰到。
    这时底层通信拦截的处理办法是:
    我们在首次调用的时候 cache 住 binder_object 的 handle ( binder_txn_st* txn 中的 handle ),当本次 binder 通信为结束,但是 Code 为 1 ( FIRST_CALL_TRANSACTION ),接口方法 Code 均大于 1 ( binder_txn_st* txn 中的 Code ),此时匹配 handle 值于之前 cache 的 handle 值是否一致,一致则解析。
    如 cache:
    unsigned int handle = target_get_binder_obj_ptr(txn);
    if (0 < handle) {
    TlsCache* poolItem = TlsUtils::getPool();
    if (poolItem) {
    poolItem->setInt(handle);// TODO save cache
    } else {
    // TODO cleat
    }
    }
    如:解析
    if (token.unequal(__PACKAGE_LENGTH__, __PACKAGE_HASH__)) {
    if (FIRST_CALL_TRANSACTION == txn->code) {
    TlsCache *tlsPool = TlsUtils::getPool();
    if (tlsPool->getInt() == txn->target.handle) {
    // TODO
    }
    }
    }
    iofomo
        5
    iofomo  
    OP
       2024-04-23 18:00:08 +08:00
    @beasonshu 一般容器,沙箱,虚拟化,隐私保护,安全防护等产品会用到,以及研究某些应用使用了那些系统接口的特性功能等
    lee1997
        6
    lee1997  
       2024-04-23 20:17:04 +08:00
    beriru
        7
    beriru  
       2024-04-27 02:19:41 +08:00
    beriru
        8
    beriru  
       2024-04-27 02:30:08 +08:00
    > 首先,Android 源码越来越庞大,了解所有的服务工作量很大,因此有哪些服务已经被缓存排查非常困难。
    > 其次,厂商越来越钟情于扩展自定义服务,这些服务不开源,识别和适配更加耗时。
    > 再次,有一部分服务只有 native 实现,并不能通过 Java 层的接口代理进行拦截(如:Sensor/Audio/Video/Camera 服务等)。

    如果只是为了解决 1,2 的话

    https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/os/Binder.java;drc=074bb2dc367bde42b837a8de4d4e88f26c074c32;l=986

    感觉这样就能解决大部分场景
    xingda920813
        9
    xingda920813  
       2024-04-27 22:18:28 +08:00
    native hook 不稳定啊, 容易有 native crash. 一旦出现, 非 hook 框架的作者根本解决不了, 没法用于线上产品. 还是考虑纯 Java 方案吧.
    iofomo
        10
    iofomo  
    OP
       2024-04-28 09:32:33 +08:00
    @beriru
    iofomo
        11
    iofomo  
    OP
       2024-04-28 09:35:14 +08:00
    @beriru 如果只是想要获得调用方法是可以使用这个全局的,如果需要拿到 Binder 通信来回的数据,并且进行修改,则做不到
    iofomo
        12
    iofomo  
    OP
       2024-04-28 09:37:27 +08:00
    @xingda920813 稳定性还可以,特定场景使用的,用户量巨大的 ToC 应用自然不会作为主功能,通常为辅助的安全检测,数据采集的独立功能,就算异常也不会影响业务
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1319 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 17:04 PVG 01:04 LAX 09:04 JFK 12:04
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86