iOS底層系列15 -- dyld與objc之間的關(guān)聯(lián)

iOS逆向07 -- Dyld動(dòng)態(tài)加載器 這篇文章中,詳細(xì)闡述了App啟動(dòng)運(yùn)行過程中Dyld的工作流程膛锭,本篇是在此基礎(chǔ)上來探討dyld與objc之間是如何互動(dòng)關(guān)聯(lián)的粮坞;

_objc_init源碼分析
  • _objc_init是objcLib中的函數(shù),其底層實(shí)現(xiàn)如下:
void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
  • 【第一步:environ_init()】環(huán)境變量的初始化
  • 下面通過修改源碼初狰,去除所有限制條件捞蚂,在控制臺(tái)上打印出所有環(huán)境變量,
    源碼修改如下所示:
Snip20210303_2.png
  • 控制臺(tái)上打印的環(huán)境變量如下:
objc[38442]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[38442]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[38442]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[38442]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[38442]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[38442]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[38442]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[38442]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[38442]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[38442]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[38442]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[38442]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[38442]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[38442]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[38442]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[38442]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[38442]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[38442]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[38442]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[38442]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[38442]: OBJC_PRINT_CUSTOM_CORE: log classes with custom core methods
objc[38442]: OBJC_PRINT_CUSTOM_RR: log classes with custom retain/release methods
objc[38442]: OBJC_PRINT_CUSTOM_AWZ: log classes with custom allocWithZone methods
objc[38442]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[38442]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[38442]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[38442]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[38442]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[38442]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[38442]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[38442]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[38442]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[38442]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[38442]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[38442]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[38442]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[38442]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[38442]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[38442]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
  • 上面的環(huán)境變量均可通過 target -- Edit Scheme -- Run --Arguments -- Environment Variables 進(jìn)行配置
Snip20210303_3.png
  • 下面介紹幾個(gè)常用的環(huán)境變量:
  • DYLD_PRINT_STATISTICS:設(shè)置DYLD_PRINT_STATISTICS為YES時(shí)跷究,控制臺(tái)就會(huì)打印 App 的加載時(shí)長(zhǎng)姓迅,包括整體加載時(shí)長(zhǎng)和動(dòng)態(tài)庫加載時(shí)長(zhǎng),即main函數(shù)之前的啟動(dòng)時(shí)間(查看pre-main耗時(shí))俊马,可以通過設(shè)置了解其耗時(shí)部分丁存,并對(duì)其進(jìn)行啟動(dòng)優(yōu)化;
  • 設(shè)置如下所示:
Snip20210303_4.png
  • 控制臺(tái)打印結(jié)果如下所示:
Snip20210303_6.png
  • OBJC_DISABLE_NONPOINTER_ISA:杜絕生成相應(yīng)的nonpointer isa(nonpointer isa指針地址 末尾為1 )柴我,生成的都是普通的isa

  • OBJC_PRINT_LOAD_METHODS:打印出所有Class 及 Category 的load類方法的調(diào)用信息解寝;

  • NSDoubleLocalizedStrings:項(xiàng)目做國際化本地化(Localized)的時(shí)候是一個(gè)挺耗時(shí)的工作,想要檢測(cè)國際化翻譯好的語言文字UI會(huì)變成什么樣子艘儒,可以指定這個(gè)啟動(dòng)項(xiàng)聋伦。可以設(shè)置 NSDoubleLocalizedStrings 為YES界睁;

  • NSShowNonLocalizedStrings:在完成國際化的時(shí)候觉增,偶爾會(huì)有一些字符串沒有做本地化,這時(shí)就可以設(shè)置NSShowNonLocalizedStrings 為YES翻斟,所有沒有被本地化的字符串全都會(huì)變成大寫逾礁。

  • 【第二步:tls_init()】:線程key的綁定,主要涉及本地線程池的初始化及析構(gòu)访惜;

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
  • 【第三步:static_init()】:運(yùn)行系統(tǒng)級(jí)別的C++靜態(tài)構(gòu)造函數(shù)
    主要是運(yùn)行系統(tǒng)級(jí)別的C++靜態(tài)構(gòu)造函數(shù)嘹履,在dyld調(diào)用我們的靜態(tài)構(gòu)造函數(shù)之前腻扇,libc調(diào)用_objc_init方法,即系統(tǒng)級(jí)別的C++構(gòu)造函數(shù) 先于 自定義的C++構(gòu)造函數(shù) 運(yùn)行砾嫉;
/***********************************************************************
* static_init
* Run C++ static constructor functions.
* libc calls _objc_init() before dyld would call our static constructors, 
* so we have to do it ourselves.
**********************************************************************/
static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
}
  • 【第四步:runtime_init()】:運(yùn)行時(shí)的初始化
  • 運(yùn)行時(shí)的初始化包含兩個(gè)部分:分類與類的初始化幼苛;
void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}
  • 【第五步:exception_init()】:初始化libobjc的異常處理系統(tǒng)
  • 主要是初始化libobjc的異常處理系統(tǒng),注冊(cè)異常處理的回調(diào)焕刮,從而監(jiān)控異常的處理蚓峦,源碼如下:
/***********************************************************************
* exception_init
* Initialize libobjc's exception handling system.
* Called by map_images().
**********************************************************************/
void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}
  • 當(dāng)有crash發(fā)生時(shí),會(huì)執(zhí)行_objc_terminate函數(shù)济锄,走到uncaught_handler扔出異常暑椰;
static void _objc_terminate(void)
{
    if (PrintExceptions) {
        _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
        // No current exception.
        (*old_terminate)();
    }else {
        // There is a current exception. Check if it's an objc exception.
        @try {
            __cxa_rethrow();
        } @catch (id e) {
            // It's an objc object. Call Foundation's handler, if any.
            (*uncaught_handler)((id)e);
            (*old_terminate)();
        } @catch (...) {
            // It's not an objc object. Continue to C++ terminate.
            (*old_terminate)();
        }
    }
}
  • 搜索uncaught_handler App應(yīng)用層會(huì)傳入一個(gè)回調(diào)函數(shù),專門用來處理異常的回調(diào)函數(shù)荐绝,如下所示一汽,其中fn即為傳入的函數(shù),即 uncaught_handler 等于 fn
objc_uncaught_exception_handler 
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
{
//    fn為設(shè)置的異常句柄 傳入的函數(shù)低滩,為外界給的
    objc_uncaught_exception_handler result = uncaught_handler;
    uncaught_handler = fn; //賦值
    return result;
}

crash分類

  • 應(yīng)用程序出現(xiàn)crash召夹,主要是因?yàn)槭盏搅宋刺幚淼男盘?hào),信號(hào)主要來源于3個(gè)地方:

    • kernel 內(nèi)核恕沫;
    • 其他進(jìn)行监憎;
    • App本身
  • 所以這三種未處理的信號(hào)婶溯,對(duì)應(yīng)了三種類型的crash鲸阔;

    • Mach異常:是指最底層的內(nèi)核級(jí)異常。用戶態(tài)的開發(fā)者可以直接通過Mach API設(shè)置thread迄委,task褐筛,host的異常端口,來捕獲Mach異常叙身。
    • Unix信號(hào):又稱BSD 信號(hào)渔扎,如果開發(fā)者沒有捕獲Mach異常,則會(huì)被host層的方法ux_exception()將Mach異常轉(zhuǎn)換為對(duì)應(yīng)的UNIX信號(hào)信轿,并通過方法threadsignal()將信號(hào)投遞到出錯(cuò)線程晃痴。可以通過方法signal(x, SignalHandler)來捕獲single财忽;Mach異常Unix信號(hào)異常是同一個(gè)異常在不同層級(jí)中處理;
    • NSException 應(yīng)用級(jí)異常:它是未被捕獲的Objective-C異常倘核,導(dǎo)致程序向自身發(fā)送了SIGABRT信號(hào)而崩潰,對(duì)于未捕獲的Objective-C異常定罢,是可以通過try catch來捕獲的笤虫,或者通過NSSetUncaughtExceptionHandler()機(jī)制來捕獲;
  • 針對(duì)應(yīng)用級(jí)異常祖凫,可以通過注冊(cè)異常捕獲的函數(shù)琼蚯,即NSSetUncaughtExceptionHandler機(jī)制,實(shí)現(xiàn)線程被菘觯活遭庶,收集上傳崩潰日志;

  • 【第六步:cache_init()】:緩存初始化

void cache_init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
    mach_msg_type_number_t count = 0;
    kern_return_t kr;

    while (objc_restartableRanges[count].location) {
        count++;
    }

    kr = task_restartable_ranges_register(mach_task_self(),
                                          objc_restartableRanges, count);
    if (kr == KERN_SUCCESS) return;
    _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
                kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}
  • 【第七步:_imp_implementationWithBlock_init()】:?jiǎn)?dòng)回調(diào)機(jī)制
  • 在某些進(jìn)程中渴望加載libobjc-trampolines.dylib稠屠,一些程序(最著名的是嵌入式Chromium的較早版本使用的QtWebEngineProcess)啟用了嚴(yán)格限制的沙盒配置文件峦睡,從而阻止了對(duì)該dylib的訪問,如果有任何調(diào)用imp_implementationWithBlock的操作(如AppKit開始執(zhí)行的操作)权埠,那么我們將在嘗試加載它時(shí)崩潰榨了,將其加載到此處可在啟用沙箱配置文件之前對(duì)其進(jìn)行設(shè)置并阻止它;
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
    // Eagerly load libobjc-trampolines.dylib in certain processes. Some
    // programs (most notably QtWebEngineProcess used by older versions of
    // embedded Chromium) enable a highly restrictive sandbox profile which
    // blocks access to that dylib. If anything calls
    // imp_implementationWithBlock (as AppKit has started doing) then we'll
    // crash trying to load it. Loading it here sets it up before the sandbox
    // profile is enabled and blocks it.
    //
    // This fixes EA Origin (rdar://problem/50813789)
    // and Steam (rdar://problem/55286131)
    if (__progname &&
        (strcmp(__progname, "QtWebEngineProcess") == 0 ||
         strcmp(__progname, "Steam Helper") == 0)) {
        Trampolines.Initialize();
    }
#endif
}
  • 【第八步:_dyld_objc_notify_register(&map_images, load_images, unmap_image)
  • objcDyld注冊(cè)回調(diào)函數(shù)_dyld_objc_notify_register()函數(shù)的聲明與實(shí)現(xiàn)是在dyld中:
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
// dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
// initializers in that image.  This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);

從注釋可以看出:

  • 此函數(shù)僅僅提供給objc運(yùn)行時(shí)調(diào)用的攘蔽;

  • 當(dāng)objc的鏡像被mapped龙屉,unmapped,initialized時(shí)满俗,注冊(cè)的回調(diào)函數(shù)會(huì)被調(diào)用转捕;

  • dyld將會(huì)通過一個(gè)包含objc-image-info的鏡像文件的數(shù)組回調(diào)mapped函數(shù)。

  • objc調(diào)用_dyld_objc_notify_register()函數(shù)傳入的三個(gè)參數(shù)含義如下:

    • map_images:dyld將image(鏡像文件)加載進(jìn)內(nèi)存時(shí)唆垃,會(huì)觸發(fā)該函數(shù)五芝;
    • load_images:dyld初始化image會(huì)觸發(fā)該函數(shù);
    • unmap_image:dyld將image移除時(shí)辕万,會(huì)觸發(fā)該函數(shù)枢步;

Dyld與Objc之間的關(guān)聯(lián)

  • Dyld中_dyld_objc_notify_register()函數(shù)實(shí)現(xiàn)如下:
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}
  • 根據(jù)在objc中調(diào)用傳入的參數(shù),存在下面的對(duì)應(yīng)關(guān)系:

    • mapped等價(jià)于 map_images
    • init等價(jià)于 load_images
    • unmapped等價(jià)于 unmap_image
  • 內(nèi)部調(diào)用了registerObjCNotifiers()渐尿,實(shí)現(xiàn)如下:

void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    sNotifyObjCInit     = init;
    sNotifyObjCUnmapped = unmapped;

    // call 'mapped' function with all images mapped so far
    try {
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    }
    catch (const char* msg) {
        // ignore request to abort during registration
    }

    // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
    for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
        ImageLoader* image = *it;
        if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
            (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
        }
    }
}
  • 所以存在下面的等價(jià)關(guān)系:
    • sNotifyObjCMapped == mapped == map_images
    • sNotifyObjCInit == init == load_images
    • sNotifyObjCUnmapped == unmapped == unmap_image

map_images回調(diào)函數(shù)的調(diào)用時(shí)機(jī)

  • 在dyld源碼中全局搜索sNotifyObjCMapped价捧,發(fā)現(xiàn)sNotifyObjCMapped是在notifyBatchPartial中被調(diào)用的,再全局搜索notifyBatchPartial發(fā)現(xiàn)是在registerObjCNotifiers中調(diào)用的涡戳;
Snip20210304_17.png

load_images回調(diào)函數(shù)的調(diào)用時(shí)機(jī)

  • load_images的調(diào)用時(shí)機(jī)在 iOS App運(yùn)行加載中已經(jīng)詳細(xì)闡述了结蟋,是在第二次notifySingle()中調(diào)用的,所以map_images要先于load_images調(diào)用渔彰;

unmap_image回調(diào)函數(shù)的調(diào)用時(shí)機(jī)

  • 在dyld源碼中全局搜索sNotifyObjCUnmapped嵌屎,發(fā)現(xiàn)sNotifyObjCUnmapped是在removeImage()中被調(diào)用的,即移除image鏡像時(shí)恍涂,調(diào)用unmap_image回調(diào)函數(shù)宝惰;
總結(jié)
  • Mach-O文件從Dyld的動(dòng)態(tài)鏈接加載,到Objc將所有文件的加載進(jìn)入內(nèi)存再沧,調(diào)用類與分類的load方法尼夺,最后到Application的Main函數(shù)入口,其整體的流程如下圖所示:
Dyld_Objc.png
  • objc調(diào)用_dyld_objc_notify_register(&map_images, load_images, unmap_image)函數(shù),然后dyld接收到這三個(gè)方法參數(shù) dyld::registerObjCNotifiers(mapped, init, unmapped)淤堵,并用全局變量保存寝衫,對(duì)應(yīng)關(guān)系如下:
    • map_images <==> sNotifyObjCMapped
    • load_images <==> sNotifyObjCInit
    • unmap_image <==> sNotifyObjCUnmapped
    • 最后dyld在合適的時(shí)機(jī),分別調(diào)用這三個(gè)函數(shù)方法拐邪;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慰毅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扎阶,更是在濱河造成了極大的恐慌汹胃,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件东臀,死亡現(xiàn)場(chǎng)離奇詭異着饥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)惰赋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門宰掉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谤逼,你說我怎么就攤上這事贵扰。” “怎么了流部?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵戚绕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我枝冀,道長(zhǎng)舞丛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任果漾,我火速辦了婚禮球切,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绒障。我一直安慰自己吨凑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布户辱。 她就那樣靜靜地躺著鸵钝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庐镐。 梳的紋絲不亂的頭發(fā)上恩商,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音必逆,去河邊找鬼怠堪。 笑死揽乱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粟矿。 我是一名探鬼主播凰棉,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嚷炉!你這毒婦竟也來了渊啰?” 一聲冷哼從身側(cè)響起探橱,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤申屹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后隧膏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哗讥,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年胞枕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杆煞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腐泻,死狀恐怖决乎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情派桩,我是刑警寧澤构诚,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站铆惑,受9級(jí)特大地震影響范嘱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜员魏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一丑蛤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撕阎,春花似錦受裹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至魄眉,卻和暖如春砰盐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坑律。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工岩梳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囊骤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓冀值,卻偏偏與公主長(zhǎng)得像也物,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子列疗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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