19.优化 - matrix-battery-canary 分析3

前置知识

1.Binder相关 - AIDL

binderandroid中主要充当跨进程通信桥梁的角色,为C/S架构,如app中的startactivity方法,首先app(客户端)会向ASM(服务端)请求一个AMS的本地代理,即ActivityManagerNative,之后appAMS的通信就通过本地代理来实现

AIDL是一种简单化写binder通信的方法。可以理解为原来写binder通信很难,而通过AIDL这种方式来写的话会很方便

Simple:原来的AIDL

interface IInterface {
    void action(int a);
}

编译之后的产物

public interface IInterface extends android.os.IInterface {
    // 远端服务,服务端
    public static abstract class Stub extends android.os.Binder implements IInterface {
        // 获取服务,先查看服务与获取服务的进程是不是同一个,为同一个的话直接返回,不是的话就返回一个服务的代理对象
        public static IInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IInterface))) {
                return ((IInterface) iin);
            }
            return new IInterface.Stub.Proxy(obj);
        }
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        // 具体的通信过程,
        // 1.客户端按照规定格式和顺序写入,
        // 2.服务端按照规定格式和顺序读出并执行客户端要求执行的方法
        // 3.服务端将返回结果按照规定格式和顺序写入
        // 4.客户端按照规定格式和顺序读取出结果,通信完成
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
           java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_action: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    this.aa(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }   

        // 远程服务的代理类,客户端与该类通信等同于与远程服务通信
        private static class Proxy implements IInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }


            @Override
            public void action(int a) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(aa);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_action, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

    }
}
  • 总结通信步骤(以上面AIDL为例):
    • 1.在app里面获取远程服务的代理对象,即new IInterface.Stub.Proxy(obj)
    • 2.在 Proxyaction方法_data写入数据,_reply读出结果
    • 3.stub,数据写入到onTransact方法读出,匹配标识符执行相应方法并向客户端写回数据

获取系统服务的部分代码解析(Android10.0)

以startactivity方法为例

startactivity();
...
ActivityTaskManager.getService().startActivity(...);

// ActivityTaskManager.java
    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };
// Singleton.java
public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

// ServiceManager.java
    @UnsupportedAppUsage
    public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                // 前面请求过,缓存起来
                return service;
            } else {
                // 获取远程服务
                return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }
  • 获取系统服务流程总结:系统服务是在ServiceManager.java类中的getService被获取,在其方法中如果sCache中有被获取的代理对象则直接返回,没有就 new 一个后缓存起来。

matrix-battery-canary hook分析

1. AlarmMonitorFeature

该类的启动结束监控是由onTurnOnonTurnOff方法控制

    public void onTurnOn() {
        super.onTurnOn();
        if (mCore.getConfig().isAmsHookEnabled) {
            mListener = new AlarmManagerServiceHooker.IListener() {
                @Override
                public void onAlarmSet(int type, long triggerAtMillis, long windowMillis, long intervalMillis, int flags, PendingIntent operation, AlarmManager.OnAlarmListener onAlarmListener) {
                    String stack = "";
                    if (mCore.getConfig().isStatAsSample) {
                        // 抛出异常得到 “at android.app.AlarmManager” 开头的异常信息行并拼接
                        stack = BatteryCanaryUtil.polishStack(Log.getStackTraceString(new Throwable()), "at android.app.AlarmManager");
                    }

                    AlarmRecord alarmRecord = new AlarmRecord(type, triggerAtMillis, windowMillis, intervalMillis, flags, stack);
                    MatrixLog.i(TAG, "#onAlarmSet, target = " + alarmRecord);

                    if (operation != null || onAlarmListener != null) {
                        int traceKey = operation != null ? operation.hashCode() : onAlarmListener.hashCode();
                        mAlarmTracing.onSet(traceKey, alarmRecord);
                    }
                }

                @Override
                public void onAlarmRemove(PendingIntent operation, AlarmManager.OnAlarmListener onAlarmListener) {
                    if (operation != null || onAlarmListener != null) {
                        int traceKey = operation != null ? operation.hashCode() : onAlarmListener.hashCode();
                        mAlarmTracing.onRemove(traceKey);
                    }
                }
            };
            AlarmManagerServiceHooker.addListener(mListener);
        }

    }

// AlarmManagerServiceHooker.java
    private static SystemServiceBinderHooker sHookHelper = new SystemServiceBinderHooker(Context.ALARM_SERVICE, "android.app.IAlarmManager", sHookCallback);

    private static List<IListener> sListeners = new ArrayList<>();

    public synchronized static void addListener(IListener listener) {
        if (listener == null) {
            return;
        }

        if (sListeners.contains(listener)) {
            return;
        }

        sListeners.add(listener);
        checkHook();
    }
    private static void checkHook() {
        if (sTryHook) {
            return;
        }

        if (sListeners.isEmpty()) {
            return;
        }

        boolean hookRet = sHookHelper.doHook();
        MatrixLog.i(TAG, "checkHook hookRet:%b", hookRet);
        sTryHook = true;
    }


  • SystemServiceBinderHooker.java

    之前执行过 private static SystemServiceBinderHooker sHookHelper = new SystemServiceBinderHooker(Context.ALARM_SERVICE, "android.app.IAlarmManager", sHookCallback);

public class SystemServiceBinderHooker {
    private static final String TAG = "Matrix.battery.SystemServiceBinderHooker";

    public interface HookCallback {
        void onServiceMethodInvoke(Method method, Object[] args);
        @Nullable Object onServiceMethodIntercept(Object receiver, Method method, Object[] args) throws Throwable;
    }

    private final String mServiceName;
    private final String mServiceClass;
    private final HookCallback mHookCallback;

    @Nullable private IBinder mOriginServiceBinder;
    @Nullable private IBinder mDelegateServiceBinder;

    public SystemServiceBinderHooker(final String serviceName, final String serviceClass, final HookCallback hookCallback) {
        mServiceName = serviceName;
        mServiceClass = serviceClass;
        mHookCallback = hookCallback;
    }

    @SuppressWarnings({"PrivateApi", "unchecked", "rawtypes"})
    public boolean doHook() {
        MatrixLog.i(TAG, "doHook: serviceName:%s, serviceClsName:%s", mServiceName, mServiceClass);
        try {
            // 反射构造出了 IAlarmManager 的代理对象
            BinderProxyHandler binderProxyHandler = new BinderProxyHandler(mServiceName, mServiceClass, mHookCallback);
            IBinder delegateBinder = binderProxyHandler.createProxyBinder();

            // 将代理对象放到 ServiceManager 的 sCache 容器中,这样当下次 getService 时如果有相应的服务则直接返回,这里为直接返回我们自己反射构造的对象
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Field cacheField = serviceManagerCls.getDeclaredField("sCache");
            cacheField.setAccessible(true);
            Map<String, IBinder> cache = (Map) cacheField.get(null);
            cache.put(mServiceName, delegateBinder);

            mDelegateServiceBinder = delegateBinder;
            mOriginServiceBinder = binderProxyHandler.getOriginBinder();
            return true;

        } catch (Throwable e) {
            MatrixLog.e(TAG, "#doHook exp: " + e.getLocalizedMessage());
        }
        return false;
    }

    @SuppressWarnings({"PrivateApi", "unchecked", "rawtypes"})
    public boolean doUnHook() {
        if (mOriginServiceBinder == null) {
            MatrixLog.w(TAG, "#doUnHook mOriginServiceBinder null");
            return false;
        }
        if (mDelegateServiceBinder == null) {
            MatrixLog.w(TAG, "#doUnHook mDelegateServiceBinder null");
            return false;
        }

        try { 
            // 通过反射执行 ServiceManager::getService 拿到 IAlarmManager 对象,优先从 ServiceManager 中的 sCache 中取
            IBinder currentBinder = BinderProxyHandler.getCurrentBinder(mServiceName);
            if (mDelegateServiceBinder != currentBinder) {
                MatrixLog.w(TAG, "#doUnHook mDelegateServiceBinder != currentBinder");
                return false;
            }
            // 放回没有被动态代理过的对象
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Field cacheField = serviceManagerCls.getDeclaredField("sCache");
            cacheField.setAccessible(true);
            Map<String, IBinder> cache = (Map) cacheField.get(null);
            cache.put(mServiceName, mOriginServiceBinder);
            return true;
        } catch (Throwable e) {
            MatrixLog.e(TAG, "#doUnHook exp: " + e.getLocalizedMessage());
        }
        return false;
    }


    static final class BinderProxyHandler implements InvocationHandler {
        private final IBinder mOriginBinder;
        private final Object mServiceManagerProxy;

        BinderProxyHandler(String serviceName, String serviceClass, HookCallback callback) throws Exception {
            // 通过反射执行 ServiceManager::getService 拿到 IAlarmManager 对象
            mOriginBinder = getCurrentBinder(serviceName);
            // 通过反射执行 IAlarmManager$Stub 对象的 asInterface 方法,拿到 IAlarmManager 这个远程服务对象的本地代理
            // 并通过动态代理 IAlarmManager 这个接口,将其执行的方法通过 callback 回调出去
            mServiceManagerProxy = createServiceManagerProxy(serviceClass, mOriginBinder, callback);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 原来 queryLocalInterface 方法一般都是返回空,但是我们在构造函数中先反射构造出了其代理对象,所以可以直接返回
            // ServiceManager::getService 之后需要返回一个远程服务的本地代理,会先执行其 queryLocalInterface 这个方法,这个方法不为空则直接返回其代理,
            // 为空则执行对应的 XXXManager$Stub::asInterface 方法返回一个程服务的本地代理对象,但是在构造函数中我们直接反射执行了 XXXManager$Stub::asInterface 该方法,拿到了返回对象
            if ("queryLocalInterface".equals(method.getName())) {
                return mServiceManagerProxy;
            }
            return method.invoke(mOriginBinder, args);
        }

        public IBinder getOriginBinder() {
            return mOriginBinder;
        }

        @SuppressWarnings({"PrivateApi"})
        // 动态代理 IBinder 这个接口,当前发起 ServiceManager::getService 这个请求之后,开始执行 queryLocalInterface 这个方法时,将构造函数中的 mServiceManagerProxy 返回回去 
        public IBinder createProxyBinder() throws Exception  {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            ClassLoader classLoader = serviceManagerCls.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("Can not get ClassLoader of " + serviceManagerCls.getName());
            }
            return (IBinder) Proxy.newProxyInstance(
                    classLoader,
                    new Class<?>[]{IBinder.class},
                    this
            );
        }

        @SuppressWarnings({"PrivateApi"})
        // 通过反射执行 ServiceManager::getService 拿到 IAlarmManager 对象
        static IBinder getCurrentBinder(String serviceName) throws Exception {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Method getService = serviceManagerCls.getDeclaredMethod("getService", String.class);
            return  (IBinder) getService.invoke(null, serviceName);
        }

        @SuppressWarnings({"PrivateApi"})
        private static Object createServiceManagerProxy(String serviceClassName, IBinder originBinder, final HookCallback callback) throws Exception  {
            Class<?> serviceManagerCls = Class.forName(serviceClassName);
            // 通过反射执行 IAlarmManager$Stub 对象的 asInterface 方法,拿到 IAlarmManager 这个远程服务对象的本地代理
            Class<?> serviceManagerStubCls = Class.forName(serviceClassName + "$Stub"); // android.app.IAlarmManager$Stub
            ClassLoader classLoader = serviceManagerStubCls.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("get service manager ClassLoader fail!");
            }
            Method asInterfaceMethod = serviceManagerStubCls.getDeclaredMethod("asInterface", IBinder.class);
            final Object originManagerService = asInterfaceMethod.invoke(null, originBinder);
            // 通过动态代理 IAlarmManager 这个接口,将其执行的方法通过 callback 回调出去
            return Proxy.newProxyInstance(classLoader,
                    new Class[]{IBinder.class, IInterface.class, serviceManagerCls},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if (callback != null) {
                                callback.onServiceMethodInvoke(method, args);
                                Object result = callback.onServiceMethodIntercept(originManagerService, method, args);
                                if (result != null) {
                                    return result;
                                }
                            }
                            return method.invoke(originManagerService, args);
                        }
                    }
            );
        }
    }
}

 // 在 IAlarmManager 被动态代理之后,其方法执行都会被会回调到 sHookCallback 这里来,回调的方法有
 // 9.0 中
 //    void set(String callingPackage, int type, long triggerAtTime, long windowLength,
//             long interval, int flags, in PendingIntent operation, in IAlarmListener listener,
//             String listenerTag, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock);
//    boolean setTime(long millis);
//    void setTimeZone(String zone);
//    void remove(in PendingIntent operation, in IAlarmListener listener);
//    long getNextWakeFromIdleTime();
//    AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
//    long currentNetworkTimeMillis();
 private static SystemServiceBinderHooker.HookCallback sHookCallback = new SystemServiceBinderHooker.HookCallback() {
        @Override
        public void onServiceMethodInvoke(Method method, Object[] args) {
            MatrixLog.v(TAG, "onServiceMethodInvoke: method name %s", method.getName());
            // 根据不同情况分发记录
            dispatchListeners(method, args);
        }

        @Nullable
        @Override
        public Object onServiceMethodIntercept(Object receiver, Method method, Object[] args) {
            return null;
        }
    };
    
    private static void dispatchListeners(Method method, Object[] args) {
        if (method.getName().equals("set")
                //jb-release ics-mr0-release
                || method.getName().equals("setRepeating") || method.getName().equals("setInexactRepeating")) {
            dispatchSet(args);
        } else if (method.getName().equals("remove")) {
            dispatchCancel(args);
        }
    }

    private static void dispatchSet(Object[] args) {
        // 根据参数个数拼接,每个版本的参数有可能不一样
        SetArgs setArgs = SetArgsCompatible.createSetArgs(args);
        if (setArgs == null) {
            MatrixLog.w(TAG, "dispatchSet setArgs null");
            return;
        }

        synchronized (AlarmManagerServiceHooker.class) {
            for (int i = 0; i < sListeners.size(); i++) {
                sListeners.get(i).onAlarmSet(setArgs.type, setArgs.triggerAtMillis, setArgs.windowMillis,
                        setArgs.intervalMillis, setArgs.flags, setArgs.operation, setArgs.onAlarmListener);
            }
        }
    }

    private static void dispatchCancel(Object[] args) {
        CancelArgs cancelArgs = CancelArgsCompatible.createCancelArgs(args);
        if (cancelArgs == null) {
            MatrixLog.w(TAG, "dispatchCancel cancelArgs null");
            return;
        }

        synchronized (AlarmManagerServiceHooker.class) {
            for (int i = 0; i < sListeners.size(); i++) {
                sListeners.get(i).onAlarmRemove(cancelArgs.operation, cancelArgs.onAlarmListener);
            }
        }
    }
    
// 而 sListeners 中有最初在 AlarmMonitorFeature 类中的 onTurn 方法中放入的监听对象,最终会执行
  public void onTurnOn() {
        super.onTurnOn();
        if (mCore.getConfig().isAmsHookEnabled) {
            mListener = new AlarmManagerServiceHooker.IListener() {
                @Override
                public void onAlarmSet(int type, long triggerAtMillis, long windowMillis, long intervalMillis, int flags, PendingIntent operation, AlarmManager.OnAlarmListener onAlarmListener) {
                    String stack = "";
                    if (mCore.getConfig().isStatAsSample) {
                        // 通过抛出异常得到堆栈并记录, “at android.app.AlarmManager” 开头的异常信息行并拼接
                        stack = BatteryCanaryUtil.polishStack(Log.getStackTraceString(new Throwable()), "at android.app.AlarmManager");
                    }

                    AlarmRecord alarmRecord = new AlarmRecord(type, triggerAtMillis, windowMillis, intervalMillis, flags, stack);
                    MatrixLog.i(TAG, "#onAlarmSet, target = " + alarmRecord);

                    if (operation != null || onAlarmListener != null) {
                        int traceKey = operation != null ? operation.hashCode() : onAlarmListener.hashCode();
                        mAlarmTracing.onSet(traceKey, alarmRecord);
                    }
                }

                @Override
                public void onAlarmRemove(PendingIntent operation, AlarmManager.OnAlarmListener onAlarmListener) {
                    if (operation != null || onAlarmListener != null) {
                        int traceKey = operation != null ? operation.hashCode() : onAlarmListener.hashCode();
                        mAlarmTracing.onRemove(traceKey);
                    }
                }
            };
            AlarmManagerServiceHooker.addListener(mListener);
        }

    }
  • 总结:闹钟的监控是基于动态代理闹钟服务 android.app.IAlarmManager ,记录其执行的方法。
  • 闹钟监控的步骤:
    • 1.反射构造闹钟服务 android.app.IAlarmManager 的本地代理,(通过执行android.app.IAlarmManager$Stub::asInterface
    • 2.动态代理android.app.IAlarmManager 接口,当其执行方法时就记录下来并回调出去
    • 3.反射提前设置 ServiceManagersCache 容器中的服务代理对象,当其调用 getService 时直接返回自己设置的服务代理对象

2. WakeLockMonitorFeature

该类监控的是IPowerManageracquireWakeLockreleaseWakeLock方法,和AlarmMonitorFeature实现几乎一样。

  • 补充WakeLock知识

    • 1.WakeLock可以保持 CPU 处于唤醒状态。当app息屏一段时间之后原本 CPU 就减少运行或者休眠了,使用 WakeLock机制可以使 app 息屏之后 CPU 处于运用状态
    • 2.使用
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    // PowerManager.PARTIAL_WAKE_LOCK 标记 cpu开启 屏幕键盘关闭(有别的标志位可选)
    WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"WakelockTag");
    wakeLock.acquire();
    wakeLock.release();
    

3.其他Feature

其他的Feature实现原理都和AlarmMonitorFeature几乎一致,都是反射出自己的系统服务本地代理并通过动态代理来记录相关函数的调用。

  • 1.WifiMonitorFeature

    监控的是IWifiManagerstartScangetScanResults方法

  • 2.LocationMonitorFeature

    监控的是ILocationManagerrequestLocationUpdates方法

  • 3.BlueToothMonitorFeatur

    监控的是IBluetoothManagerregisterAdaptergetBluetoothGattregisterScanner startScan startScanForIntent)方法

  • 4.NotificationMonitorFeature

    监控的是INotificationManagercreateNotificationChannelsenqueueNotificationWithTag方法

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,321评论 6 543
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,559评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,442评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,835评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,581评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,922评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,931评论 3 447
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,096评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,639评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,374评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,591评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,104评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,789评论 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,196评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,524评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,322评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,554评论 2 379

推荐阅读更多精彩内容