Java基礎(chǔ):基于反射和動態(tài)代理的Hook

一. 什么是 Hook

Hook 英文翻譯過來就是「鉤子」的意思,那我們在什么時候使用這個「鉤子」呢福侈?在 Android 操作系統(tǒng)中系統(tǒng)維護(hù)著自己的一套事件分發(fā)機(jī)制。應(yīng)用程序酝陈,包括應(yīng)用觸發(fā)事件和后臺邏輯處理营罢,也是根據(jù)事件流程一步步地向下執(zhí)行。而「鉤子」的意思仲智,就是在事件傳送到終點(diǎn)前截獲并監(jiān)控事件的傳輸买乃,像個鉤子鉤上事件一樣,并且能夠在鉤上事件時钓辆,處理一些自己特定的事件剪验。

image

Hook 的這個本領(lǐng)肴焊,使它能夠?qū)⒆陨淼拇a「融入」被勾住(Hook)的程序的進(jìn)程中功戚,成為目標(biāo)進(jìn)程的一個部分娶眷。API Hook 技術(shù)是一種用于改變 API 執(zhí)行結(jié)果的技術(shù),能夠?qū)⑾到y(tǒng)的 API 函數(shù)執(zhí)行重定向啸臀。在 Android 系統(tǒng)中使用了沙箱機(jī)制茂浮,普通用戶程序的進(jìn)程空間都是獨(dú)立的,程序的運(yùn)行互不干擾壳咕。這就使我們希望通過一個程序改變其他程序的某些行為的想法不能直接實(shí)現(xiàn)席揽,但是 Hook 的出現(xiàn)給我們開拓了解決此類問題的道路。

什么是沙箱機(jī)制

沙箱是一個虛擬系統(tǒng)程序谓厘,沙箱提供的環(huán)境相對于每一個運(yùn)行的程序的進(jìn)程空間都是獨(dú)立的幌羞,程序的運(yùn)行互不干擾,而且不會對現(xiàn)有的系統(tǒng)產(chǎn)生影響竟稳。

二属桦、Hook 分類

1、根據(jù)Android開發(fā)模式

Java層級的Hook他爸;
Native層級的Hook聂宾;

2、根 Hook 對象與 Hook 后處理事件方式

消息Hook诊笤;
API Hook系谐;

3、針對Hook的不同進(jìn)程上來說

全局Hook讨跟;
單個進(jìn)程Hook

四. API Hook 原理

通過對 Android 平臺的虛擬機(jī)注入與 Java 反射的方式纪他,來改變 Android 虛擬機(jī)調(diào)用函數(shù)的方式(ClassLoader),從而達(dá)到 Java 函數(shù)重定向的目的晾匠,這里我們將此類操作稱為 Java API Hook茶袒。

基礎(chǔ)知識

由此可見,Hook的基礎(chǔ)知識凉馆,是反射及基于反射的動態(tài)代理

  • 反射
    反射(Reflection)是什么呢薪寓?

反射有時候也被稱為內(nèi)省(Introspection)澜共,事實(shí)上向叉,反射,就是一種內(nèi)省的方式咳胃,Java不允許在運(yùn)行時改變程序結(jié)構(gòu)或類型變量的結(jié)構(gòu)植康,但它允許在運(yùn)行時去探知、加載展懈、調(diào)用在編譯期完全未知的class销睁,可以在運(yùn)行時加載該class供璧,生成實(shí)例對象(instance object),調(diào)用method冻记,或?qū)ield賦值睡毒。這種類似于“看透”了class的特性被稱為反射(Reflection),我們可以將反射直接理解為:可以看到自己在水中的倒影冗栗,這種操作與直接操作源代碼效果相同演顾,但靈活性高得多。

在之前學(xué)習(xí)熱更新的時候有介紹過反射隅居,詳見 Android熱更新二:理解Java反射 钠至。

  • java 的動態(tài)代理
    首先了解一些代理模式的定義。

為其他對象提供一種代理以控制這個對象的訪問胎源。

從代碼的角度來分棉钧,代理可以分為兩種:一種是靜態(tài)代理,另一種是動態(tài)代理涕蚤。

之前講設(shè)計(jì)模式的時候宪卿,也講過動態(tài)代理,詳見 Android常見設(shè)計(jì)模式五:代理模式万栅。

五佑钾、Hook Activity 的 startActivity

原理知道后,還是實(shí)戰(zhàn)來得舒暢烦粒,下面以startActivity啟動一個activity為例休溶,在Activity中啟動另一個Activity,首先我們需要了解activity的啟動流程,一步步跟蹤撒遣,發(fā)現(xiàn)最終在Activity.startActivityForResult中以如下方式啟動:

 Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }

如果你對Activity啟動熟悉的話會發(fā)現(xiàn)邮偎,此處的mInstrumentation就是ActivityThread通過ativity.attach傳過來的,而ActivityThread一個app唯一的义黎,而mInstrumentation就是在ActivityThread創(chuàng)建后馬上創(chuàng)建的,此時豁跑,是不是感覺這個mInstrumentation符合hook點(diǎn)廉涕,ok,先hook一把

public static void hookCurrentThread(){
        try {
            Class<?> activityThreadCls = Class.forName("android.app.ActivityThread");
            //1.獲取ActivityThread對象
            //hook點(diǎn)艇拍,有public的方法或?qū)傩院桑瑑?yōu)先
            Method currentActThreadMethod = activityThreadCls.getDeclaredMethod("currentActivityThread");
            Object curThreadObj = currentActThreadMethod.invoke(null);
            //獲取mInstrumentation
            Field instrumentationField = curThreadObj.getClass().getDeclaredField("mInstrumentation");
            instrumentationField.setAccessible(true);
            instrumentationField.set(curThreadObj,new InstrumentProxy((Instrumentation) instrumentationField.get(curThreadObj)));
        } catch (Exception e) {
            e.printStackTrace();
        }

其中InstrumentProxy如下:

public class InstrumentProxy extends Instrumentation{
    private Instrumentation realObj;
    public InstrumentProxy(Instrumentation obj){
        this.realObj = obj;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
       
        // 開始調(diào)用原始的方法, 調(diào)不調(diào)用隨你,但是不調(diào)用的話, 所有的startActivity都失效了.
        // 由于這個方法是隱藏的,因此需要使用反射調(diào)用;首先找到這個方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(realObj, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("do not support!!! pls adapt it");
        }
    }
}

此處,先獲取ActivityThread中的mInstrumentation屬性卸夕,然后再采用靜態(tài)代理將其替換掉层释,這樣就hook住系統(tǒng)的方法,我們也就可以在InstrumentProxy中任意插樁快集。

倘若你沒有發(fā)現(xiàn)mInstrumentation符合hook點(diǎn)贡羔,你可以繼續(xù)跟蹤Instrumentation.execStartActivity方法廉白,里面有個非常明顯的hook點(diǎn):

try {
    intent.migrateExtraStreamToClipData();
    intent.prepareToLeaveProcess();
    int result = ActivityManagerNative.getDefault().startActivity(whoThread, who.getBasePackageName(), intent,                    intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null,  requestCode, 0, null, options);
    checkStartActivityResult(result, intent);
} catch (RemoteException e) {

}

對,就是ActivityManagerNative.getDefault()乖寒,我們可以進(jìn)入ActivityManagerNative類猴蹂,發(fā)現(xiàn)getDefault方法實(shí)現(xiàn)如下:

static public IActivityManager getDefault() {
    return gDefault.get();
}

其中g(shù)Default是個static屬性,完全符合hook要求,具體hook如下:

public static void hookAMNative(){
        try {
            Class<?> actManagerNativeCls = Class.forName("android.app.ActivityManagerNative");
            //獲取gDefault
            Field gDefaultField = actManagerNativeCls.getDeclaredField("gDefault");
            gDefaultField.setAccessible(true);
            Object gDefaultObj = gDefaultField.get(null);
//            Method getField = gDefaultObj.getClass().getDeclaredMethod("get");
//            Object activityImpl = getField.invoke(null);
            Class<?> singleton = Class.forName("android.util.Singleton");
            Field mInstanceField = singleton.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
 
            Object activityImpl = mInstanceField.get(gDefaultObj);
 
//            Method activityManagerMethod= actManagerNativeCls.getMethod("getDefault");
//            Object actManagerImpl = activityManagerMethod.invoke(null);
 
            Object actProxy = Proxy.newProxyInstance(activityImpl.getClass().getClassLoader(),
                    activityImpl.getClass().getInterfaces(),new ProxyHandler(activityImpl,null));
            mInstanceField.set(gDefaultObj,actProxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

其中ProxyHandler如下:

public class ProxyHandler implements InvocationHandler{
    private Object realObj;
    public ProxyHandler(Object obj,Object d){
        this.realObj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if("startActivity".equals(methodName)){
            android.util.Log.e("hooktest",">>>>proxyhandler>>>startActivityBefore");
            Object retur = method.invoke(realObj,args);
            android.util.Log.e("hooktest",">>>>proxyhandler>>>startActivityAfter");
            return retur;
        }
 
        return method.invoke(realObj,args);
    }
}

采用動態(tài)代理的方法對IActivityManager進(jìn)行了接管楣嘁,同樣完成了startActivity的hook磅轻。

其實(shí)不選單例、靜態(tài)屬性或共有屬性逐虚,整個private的也是可以的聋溜,還以啟動startActivity為例,考慮到Activity是繼承ContextWrapper叭爱,而ContextWrapper中有個屬性mBase撮躁,如果我們能對mBase hook也是可以的,這樣就需要對ContextImpl來個代理就可以了涤伐,代碼可以如下:

public static void hookContextWrapper(ContextWrapper wrapper) {
        try {
            Field mBaseFiled;
            Class<?> wrapperClass = ContextWrapper.class;
 
            mBaseFiled = wrapperClass.getDeclaredField("mBase");
            mBaseFiled.setAccessible(true);
            mBaseFiled.set(wrapper,new HookWrapper((Context) mBaseFiled.get(wrapper)));
 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

這樣盡管可以馒胆,但是啟動activity就需要注意了,只要能走到mBase.startActivity(intent)接口才生效凝果,如果沒走它祝迂,就hook失效啰,所以hook點(diǎn)選擇很關(guān)鍵器净,盡管都hook到了東西型雳,但是是不是hook住了全部,還需要驗(yàn)證山害。

六纠俭、Hook View 的 OnClickListener

下面通過 Hook View 的 OnClickListener 來說明 Hook 的使用方法荞下。

首先進(jìn)入 View 的 setOnClickListener 方法灶轰,我們看到 OnClickListener 對象被保存在了一個叫做 ListenerInfo 的內(nèi)部類里,其中 mListenerInfo 是 View 的成員變量歇终。ListeneInfo 里面保存了 View 的各種監(jiān)聽事件权纤,比如 OnClickListener钓简、OnLongClickListener、OnKeyListener 等等汹想。

    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

    ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

我們的目標(biāo)是 Hook OnClickListener外邓,所以就要在給 View 設(shè)置監(jiān)聽事件后,替換 OnClickListener 對象古掏,注入自定義的操作损话。

    private void hookOnClickListener(View view) {
        try {
            // 得到 View 的 ListenerInfo 對象
            Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
            getListenerInfo.setAccessible(true);
            Object listenerInfo = getListenerInfo.invoke(view);
            // 得到 原始的 OnClickListener 對象
            Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
            Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
            mOnClickListener.setAccessible(true);
            View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);
            // 用自定義的 OnClickListener 替換原始的 OnClickListener
            View.OnClickListener hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
            mOnClickListener.set(listenerInfo, hookedOnClickListener);
        } catch (Exception e) {
            log.warn("hook clickListener failed!", e);
        }
    }

    class HookedOnClickListener implements View.OnClickListener {
        private View.OnClickListener origin;

        HookedOnClickListener(View.OnClickListener origin) {
            this.origin = origin;
        }

        @Override
        public void onClick(View v) {
            Toast.makeText(MainActivity.this, "hook click", Toast.LENGTH_SHORT).show();
            log.info("Before click, do what you want to to.");
            if (origin != null) {
                origin.onClick(v);
            }
            log.info("After click, do what you want to to.");
        }
    }

到這里,我們成功 Hook 了 OnClickListener槽唾,在點(diǎn)擊之前和點(diǎn)擊之后可以執(zhí)行某些操作丧枪,達(dá)到了我們的目的光涂。下面是調(diào)用的部分,在給 Button 設(shè)置 OnClickListener 后豪诲,執(zhí)行 Hook 操作顶捷。點(diǎn)擊按鈕后,日志的打印結(jié)果是:Before click → onClick → After click屎篱。

        Button btnSend = (Button) findViewById(R.id.btn_send);
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                log.info("onClick");
            }
        });
        hookOnClickListener(btnSend);

七服赎、使用 Hook 攔截應(yīng)用內(nèi)的通知

當(dāng)應(yīng)用內(nèi)接入了眾多的 SDK,SDK 內(nèi)部會使用系統(tǒng)服務(wù) NotificationManager 發(fā)送通知交播,這就導(dǎo)致通知難以管理和控制≈芈牵現(xiàn)在我們就用 Hook 技術(shù)攔截部分通知,限制應(yīng)用內(nèi)的通知發(fā)送操作秦士。

發(fā)送通知使用的是 NotificationManager 的 notify 方法缺厉,我們跟隨 API 進(jìn)去看看。它會使用 INotificationManager 類型的對象隧土,并調(diào)用其 enqueueNotificationWithTag 方法完成通知的發(fā)送提针。

    public void notify(String tag, int id, Notification notification)
    {
        INotificationManager service = getService();
        …… // 省略部分代碼
        try {
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    stripped, idOut, UserHandle.myUserId());
            if (id != idOut[0]) {
                Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
            }
        } catch (RemoteException e) {
        }
    }

    private static INotificationManager sService;

    /** @hide */
    static public INotificationManager getService()
    {
        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService("notification");
        sService = INotificationManager.Stub.asInterface(b);
        return sService;
    }

INotificationManager 是跨進(jìn)程通信的 Binder 類,sService 是 NMS(NotificationManagerService) 在客戶端的代理曹傀,發(fā)送通知要委托給 sService辐脖,由它傳遞給 NMS,具體的原理在這里不再細(xì)究皆愉,感興趣的可以了解系統(tǒng)服務(wù)和應(yīng)用的通信過程嗜价。

我們發(fā)現(xiàn) sService 是個靜態(tài)成員變量,而且只會初始化一次幕庐。只要把 sService 替換成自定義的不就行了么久锥,確實(shí)如此。下面用到大量的 Java 反射和動態(tài)代理异剥,特別要注意代碼的書寫瑟由。

    private void hookNotificationManager(Context context) {
        try {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            // 得到系統(tǒng)的 sService
            Method getService = NotificationManager.class.getDeclaredMethod("getService");
            getService.setAccessible(true);
            final Object sService = getService.invoke(notificationManager);

            Class iNotiMngClz = Class.forName("android.app.INotificationManager");
            // 動態(tài)代理 INotificationManager
            Object proxyNotiMng = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{iNotiMngClz}, new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    log.debug("invoke(). method:{}", method);
                    if (args != null && args.length > 0) {
                        for (Object arg : args) {
                            log.debug("type:{}, arg:{}", arg != null ? arg.getClass() : null, arg);
                        }
                    }
                    // 操作交由 sService 處理,不攔截通知
                    // return method.invoke(sService, args);
                    // 攔截通知冤寿,什么也不做
                    return null;
                    // 或者是根據(jù)通知的 Tag 和 ID 進(jìn)行篩選
                }
            });
            // 替換 sService
            Field sServiceField = NotificationManager.class.getDeclaredField("sService");
            sServiceField.setAccessible(true);
            sServiceField.set(notificationManager, proxyNotiMng);
        } catch (Exception e) {
            log.warn("Hook NotificationManager failed!", e);
        }
    }

Hook 的時機(jī)還是盡量要早错妖,我們在 attachBaseContext 里面操作。

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        hookNotificationManager(newBase);
    }

這樣我們就完成了對通知的攔截疚沐,可見 Hook 技術(shù)真的是非常強(qiáng)大,好多插件化的原理都是建立在 Hook 之上的潮模。

八亮蛔、結(jié)語

以上,我們知道擎厢,hook技術(shù)涉及到的知識點(diǎn)主要有反射究流、代理及android的一些底層知識辣吃,如果想要較好地掌握好hook相關(guān)的內(nèi)容,就需要花更多的時間去學(xué)習(xí)和總結(jié)芬探。

下面總結(jié)一下注意點(diǎn):

  1. Hook 的選擇點(diǎn):靜態(tài)變量和單例神得,因?yàn)橐坏﹦?chuàng)建對象,它們不容易變化偷仿,非常容易定位哩簿。
  1. Hook 過程:

尋找 Hook 點(diǎn),原則是靜態(tài)變量或者單例對象酝静,盡量 Hook public 的對象和方法节榜。
選擇合適的代理方式,如果是接口可以用動態(tài)代理别智。
偷梁換柱——用代理對象替換原始對象宗苍。

  1. Android 的 API 版本比較多,方法和類可能不一樣薄榛,所以要做好 API 的兼容工作讳窟。

另外,市面上游幾個比較成熟的Hook 方案敞恋,如果有需要大量使用此技術(shù)的不妨參考參考:

通過替換 /system/bin/app_process 程序控制 Zygote 進(jìn)程丽啡,使得 app_process 在啟動過程中會加載 XposedBridge.jar 這個 Jar 包,從而完成對 Zygote 進(jìn)程及其創(chuàng)建的 Dalvik 虛擬機(jī)的劫持耳舅。
Xposed 在開機(jī)的時候完成對所有的 Hook Function 的劫持碌上,在原 Function 執(zhí)行的前后加上自定義代碼。

  • Cydia Substrate

    Cydia Substrate 框架為蘋果用戶提供了越獄相關(guān)的服務(wù)框架浦徊,當(dāng)然也推出了 Android 版 馏予。Cydia Substrate 是一個代碼修改平臺,它可以修改任何進(jìn)程的代碼盔性。不管是用 Java 還是 C/C++(native代碼)編寫的霞丧,而 Xposed 只支持 Hook app_process 中的 Java 函數(shù)。

  • Legend

    Legend 是 Android 免 Root 環(huán)境下的一個 Apk Hook 框架冕香,該框架代碼設(shè)計(jì)簡潔蛹尝,通用性高,適合逆向工程時一些 Hook 場景突那。大部分的功能都放到了 Java 層,這樣的兼容性就非常好构眯。
    原理是這樣的愕难,直接構(gòu)造出新舊方法對應(yīng)的虛擬機(jī)數(shù)據(jù)結(jié)構(gòu),然后替換信息寫到內(nèi)存中即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猫缭,一起剝皮案震驚了整個濱河市葱弟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猜丹,老刑警劉巖芝加,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異射窒,居然都是意外死亡藏杖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門轮洋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來制市,“玉大人,你說我怎么就攤上這事弊予∠殚梗” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵汉柒,是天一觀的道長误褪。 經(jīng)常有香客問我,道長碾褂,這世上最難降的妖魔是什么兽间? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮正塌,結(jié)果婚禮上嘀略,老公的妹妹穿的比我還像新娘。我一直安慰自己乓诽,他們只是感情好帜羊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸠天,像睡著了一般讼育。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上稠集,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天奶段,我揣著相機(jī)與錄音,去河邊找鬼剥纷。 笑死痹籍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晦鞋。 我是一名探鬼主播词裤,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刺洒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吼砂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鼎文,失蹤者是張志新(化名)和其女友劉穎渔肩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拇惋,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡周偎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撑帖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓉坎。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胡嘿,靈堂內(nèi)的尸體忽然破棺而出蛉艾,到底是詐尸還是另有隱情,我是刑警寧澤衷敌,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布勿侯,位于F島的核電站,受9級特大地震影響缴罗,放射性物質(zhì)發(fā)生泄漏助琐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一面氓、第九天 我趴在偏房一處隱蔽的房頂上張望兵钮。 院中可真熱鬧,春花似錦舌界、人聲如沸掘譬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屁药。三九已至,卻和暖如春柏锄,著一層夾襖步出監(jiān)牢的瞬間酿箭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工趾娃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缭嫡,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓抬闷,卻偏偏與公主長得像妇蛀,于是被迫代替她去往敵國和親耕突。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容