Ios應(yīng)用安全學習

iOS應(yīng)用安全

攻易防難,唯有縝密、多層的防護網(wǎng)絡(luò)才能可靠的保護我們iOS應(yīng)用程序的安全。那么,一個完善的iOS應(yīng)用安全防護框架都要寫哪些東西呢? 首先,先梳理一下常見的逆向及攻擊工具。

iOS應(yīng)用逆向常用工具

  • Reveal
  • Cycript
  • Class-dump
  • Keychain-Dumper
  • gdb
  • iNalyzer
  • introspy
  • Fishhook
  • removePIE
  • IDA pro or Hopper
  • snoop-it
  • iDB
  • Charles
  • SSL Kill Switch

裸奔app的安全隱患

一部越獄的iOS設(shè)備,外加上述的逆向工具,給裸奔的iOS應(yīng)用程序帶來哪些威脅呢?

  • 任意讀寫文件系統(tǒng)數(shù)據(jù)
  • HTTP(S)實時被監(jiān)測
  • 重新打包ipa
  • 暴露的函數(shù)符號
  • 未加密的靜態(tài)字符
  • 篡改程序邏輯控制流
  • 攔截系統(tǒng)框架API
  • 逆向加密邏輯
  • 跟蹤函數(shù)調(diào)用過程(objc_msgSend)
  • 可見視圖的具體實現(xiàn)
  • 偽造設(shè)備標識
  • 可用的URL schemes
  • runtime任意方法調(diào)用
  • ……

iOS應(yīng)用安全防護開源工具

ios-class-guard 是對抗class-dump的利器,作用是將ObjC類名方法名等重命名為難以理解的字符。

iOS應(yīng)用安全防護框架概述

針對上述安全隱患,我們的iOS應(yīng)用安全防護框架需實現(xiàn)的任務(wù)大致如下:

  • 防護

    • ObjC類名方法名等重命名為難以理解的字符
    • 加密靜態(tài)字符串運行時解密
    • 混淆代碼使其難于反匯編
    • 本地存儲文件防篡改
  • 檢測

    • 調(diào)試狀態(tài)檢測
    • 越獄環(huán)境檢測
    • ObjC的Swizzle檢測
    • 任意函數(shù)的hook檢測
    • 指定區(qū)域或數(shù)據(jù)段的校驗和檢測
  • 自修復

    • 自修復被篡改的數(shù)據(jù)和代碼段

此外,還需要多層的防護,通過高層保護低層的方式來保證整個防護機制不失效。

常用的防護手段

一、加密靜態(tài)字符串運行時解密

一個編譯成功的可執(zhí)行程序,其中已初始化的字符串都是完整可見的。 針對于iOS的Mach-O二進制通??色@得以下幾種字符串信息:

  • 資源文件名
  • 可見的函數(shù)符號名
  • SQL語句
  • format
  • 通知名
  • 加密算法的key
攻擊者如何利用字符串

資源文件名通常用來快速定位逆向分析的入口點。 想要知道判斷購買金幣成功與否的代碼位置?只要確定購買成功時播放的音頻文件名字或者背景圖名字就可以順藤摸瓜了。

kLoginSuccessNotification類似這種通知名稱格外炸眼,利用Cycript發(fā)個此通知試試,也許會有什么意外收獲。 拿到對稱加密算法的key是件很幸福的事情。

字符串異或加解密

是的,字符串需要加密處理,但只需要對高度敏感字符數(shù)據(jù)做加密,比如對稱加密算法的key。 其他的,需要提高編程安全意識來彌補。

常規(guī)辦法是通過異或來加解密,來寫個sample code:


#define XOR_KEY 0xBB

void xorString(unsigned char *str, unsigned char key)
{
    unsigned char *p = str;
    while( ((*p) ^=  key) != '\0')  p++;
}

- (void)testFunction
{
    unsigned char str[] = {(XOR_KEY ^ 'h'),
                           (XOR_KEY ^ 'e'),
                           (XOR_KEY ^ 'l'),
                           (XOR_KEY ^ 'l'),
                           (XOR_KEY ^ 'o'),
                           (XOR_KEY ^ '\0')};
    xorString(str, XOR_KEY);
    static unsigned char result[6];
    memcpy(result, str, 6);
    NSLog(@"%s",result);      //output: hello
}

這樣就無法從二進制中直接分析得到字符串“hello”了。

二、阻止GDB依附

GDB是大多數(shù)hackers的首選,阻止GDB依附到應(yīng)用的常規(guī)辦法是:


#import <sys/ptrace.h>  
 
int main(int argc, charchar *argv[])  
{  
#ifndef DEBUG  
    ptrace(PT_DENY_ATTACH,0,0,0);  
#endif  
    @autoreleasepool {  
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([WQMainPageAppDelegate class]));  
    }  
}

但遺憾的是,iPhone真實的運行環(huán)境是沒有sys/ptrace.h拋出的。雖然 ptrace 方法沒有被拋出, 但是不用擔心,我們可以通過dlopen拿到它。

dlopen: 當path 參數(shù)為0是,他會自動查找 LD_LIBRARY_PATH,DYLD_LIBRARY_PATH, $DYLD_FALLBACK_LIBRARY_PATH 和 當前工作目錄中的動態(tài)鏈接庫。


#import <dlfcn.h>  
#import <sys/types.h>  
 
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);  
#if !defined(PT_DENY_ATTACH)  
#define PT_DENY_ATTACH 31  
#endif  // !defined(PT_DENY_ATTACH)  
 
void disable_gdb() {  
    void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);  
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");  
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);  
    dlclose(handle);  
}  
 
int main(int argc, charchar *argv[])  
{  
#ifndef DEBUG  
    disable_gdb();  
#endif  
    @autoreleasepool {  
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([WQMainPageAppDelegate class]));  
    }  
}

三、自定義安全鍵盤

大部分中文應(yīng)用彈出的默認鍵盤是簡體中文輸入法鍵盤,在輸入用戶名和密碼的時候,如果使用簡體中文輸入法鍵盤,輸入英文字符和數(shù)字字符的用戶名和密碼時,會自動啟動系統(tǒng)輸入法自動更正提示,然后用戶的輸入記錄會被緩存下來。

系統(tǒng)鍵盤緩存最方便拿到的就是利用系統(tǒng)輸入法自動更正的字符串輸入記錄。
緩存文件的地址是:/private/var/mobile/Library/Keyboard/dynamic-text.dat

導出該緩存文件,查看內(nèi)容,欣喜的發(fā)現(xiàn)一切輸入記錄都是明文存儲的。因為系統(tǒng)不會把所有的用戶輸入記錄都當作密碼等敏感信息來處理。

一般情況下,一個常規(guī)iPhone用戶的dynamic-text.dat文件,高頻率出現(xiàn)的字符串就是用戶名和密碼。

所以,一般銀行客戶端app輸入密碼時都不使用系統(tǒng)鍵盤,而使用自己定制的鍵盤,原因主要有2個:
1)避免第三方讀取系統(tǒng)鍵盤緩存
2)防止屏幕錄制 (自己定制的鍵盤按鍵不加按下效果)

那么,如何實現(xiàn)自定義安全鍵盤呢?大致思路如下:
1)首先捕獲系統(tǒng)鍵盤的彈出、收回通知
2)創(chuàng)建一個更高級別的window擋住系統(tǒng)鍵盤
3)需要拋出一個 id<UITextInput>textInput 的弱引用切換焦點。

四、二進制和資源文件自檢

我們把自己的程序發(fā)布到app store,但是不能保證每一個用戶都是從app store下載官方app,也不能保證每一個用戶都不越獄。

換句話說,我們無法保證程序運行環(huán)境在MAC管控策略下就絕對的安全。

所以,在有些情況下,尤其是和錢有關(guān)系的app,我們有必要在和服務(wù)器通信時,讓服務(wù)器知道客戶端到底是不是官方正版的app。

何以判斷自己是不是正版app呢?hackers們破解你的app,無非就2個地方可以動,1個是二進制,1個是資源文件。

二進制都重新編譯過了自然肯定是盜版……

有些低級的hackers喜歡修改人家的資源文件然后貼上自己的廣告,或者給用戶錯誤的指引……修改資源文件是不需要重新編譯二進制的。

因此,我們有必要在敏感的請求報文中,增加正版應(yīng)用的二進制和資源文件的標識,讓服務(wù)器知道,此請求是否來自正版的未經(jīng)修改的app。

在沙盒中,我們可以讀到自己程序的二進制,也可以讀到資源文件簽名文件,這兩個文件都不算大,我們可以對其取md5值然后以某種組合算法得到一個標記字符串,然后發(fā)給服務(wù)器。

我封裝了相關(guān)文件的讀取地址


@implementation WQPathUtilities  
 
+ (NSString *)directory:(NSSearchPathDirectory)dir  
{  
    NSArray *paths = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);  
    NSString *dirStr = [paths objectAtIndex:0];  
    return dirStr;  
}  
 
+ (NSString *)documentsDirectory  
{  
    return [WQPathUtilities directory:NSDocumentDirectory];  
}  
 
+ (NSString *)cachesDirectory  
{  
    return [WQPathUtilities directory:NSCachesDirectory];  
}  
 
+ (NSString *)tmpDirectory  
{  
    return NSTemporaryDirectory();  
}  
 
+ (NSString *)homeDirectory  
{  
    return NSHomeDirectory();  
}  
 
+ (NSString *)codeResourcesPath  
{  
    NSString *excutableName = [[NSBundle mainBundle] infoDictionary][@"CFBundleExecutable"];  
    NSString *tmpPath = [[WQPathUtilities documentsDirectory] stringByDeletingLastPathComponent];  
    NSString *appPath = [[tmpPath stringByAppendingPathComponent:excutableName]  
                         stringByAppendingPathExtension:@"app"];  
    NSString *sigPath = [[appPath stringByAppendingPathComponent:@"_CodeSignature"]  
                         stringByAppendingPathComponent:@"CodeResources"];  
    return sigPath;  
}  
 
+ (NSString *)binaryPath  
{  
    NSString *excutableName = [[NSBundle mainBundle] infoDictionary][@"CFBundleExecutable"];  
    NSString *tmpPath = [[WQPathUtilities documentsDirectory] stringByDeletingLastPathComponent];  
    NSString *appPath = [[tmpPath stringByAppendingPathComponent:excutableName]  
                         stringByAppendingPathExtension:@"app"];  
    NSString *binaryPath = [appPath stringByAppendingPathComponent:excutableName];  
    return binaryPath;  
}  
 
@end

md5方法:


#import "CommonCrypto/CommonDigest.h"  
 
+(NSString *)md5WithString:(NSString *)string  
{  
    const charchar *cStr = [string UTF8String];  
    unsigned char result[CC_MD5_DIGEST_LENGTH];  
    CC_MD5(cStr, strlen(cStr), result);  
 
    return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",  
             result[0], result[1], result[2], result[3],  
             result[4], result[5], result[6], result[7],  
             result[8], result[9], result[10], result[11],  
             result[12], result[13], result[14], result[15]  
             ] lowercaseString];  
}

五、數(shù)據(jù)擦除

對于敏感數(shù)據(jù),我們不希望長時間放在內(nèi)存中,而希望使用完后立即就被釋放掉。

但是不管是ARC還是MRC,自動釋放池也有輪循工作周期,我們都無法控制內(nèi)存數(shù)據(jù)被擦除的準確時間,讓hackers們有機可乘。
本文介紹一個小技巧——及時數(shù)據(jù)擦除。

假如一個View Controller A的一個數(shù)據(jù)被綁在一個property上,


@interface WipingMemoryViewController : UIViewController  
 
@property (nonatomic,copy) NSString *text;  
@end

當A push到 另外一個View Controller B時,該數(shù)據(jù)還是有可能被讀到的

WipingMemoryViewController *lastController = (WipingMemoryViewController *)self.navigationController.viewControllers[0];  
NSLog(@"text = %@",lastController.text);


于是,“用后即擦”變得十分必要:

_text = [[NSString alloc]initWithFormat:@"information"];   
NSLog(@"Origal string = %@",_text);  
//do something...  
charchar *string = (charchar *)CFStringGetCStringPtr((CFStringRef)_text, CFStringGetSystemEncoding());  
memset(string, 0, [_text length]);  
NSLog(@"final text = %@",_text);

Log輸出如下:

WipingMemory[2518:70b] Origal string = information  
WipingMemory[2518:70b] final text =

可以看到,我們想要保護的數(shù)據(jù),被有效的擦除了。

還有提個醒,如果是這樣

_text = @"information";

創(chuàng)建的字符串,是會被分配到data區(qū),而是無法修改的。

如果有興趣也有閑心,可以試試運行下面的代碼,有彩蛋哦:

_text = @"information";  
memset((__bridge voidvoid *)(_text), 0, _text.length - 1);  
NSString *myString = [[NSString alloc]initWithFormat:@"information"];  
NSLog(@"Origal text : %@ \n",myString);

編譯器把兩個information的省略到一個地址了~

六、數(shù)據(jù)保護API

1) 文件保護

文件系統(tǒng)中的文件、keychain中的項,都是加密存儲的。當用戶解鎖設(shè)備后,系統(tǒng)通過UDID密鑰和用戶設(shè)定的密碼生成一個用于解密的密碼密鑰,存放在內(nèi)存中,直到設(shè)備再次被鎖,開發(fā)者可以通過Data Protection API 來設(shè)定文件系統(tǒng)中的文件、keychain中的項應(yīng)該何時被解密。

/* 為filePath文件設(shè)置保護等級 */  
NSDictionary *attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete  
                                                       forKey:NSFileProtectionKey];  
[[NSFileManager defaultManager] setAttributes:attributes  
                                 ofItemAtPath:filePath  
                                        error:nil];

//文件保護等級屬性列表  
NSFileProtectionNone                                    //文件未受保護,隨時可以訪問 (Default)  
NSFileProtectionComplete                                //文件受到保護,而且只有在設(shè)備未被鎖定時才可訪問  
NSFileProtectionCompleteUntilFirstUserAuthentication    //文件收到保護,直到設(shè)備啟動且用戶第一次輸入密碼  
NSFileProtectionCompleteUnlessOpen                      //文件受到保護,而且只有在設(shè)備未被鎖定時才可打開,不過即便在設(shè)備被鎖定時,已經(jīng)打開的文件還是可以繼續(xù)使用和寫入

2) keychain項保護
/* 設(shè)置keychain項保護等級 */  
NSDictionary *query = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,  
                        (__bridge id)kSecAttrGeneric:@"MyItem",  
                        (__bridge id)kSecAttrAccount:@"username",  
                        (__bridge id)kSecValueData:@"password",  
                        (__bridge id)kSecAttrService:[NSBundle mainBundle].bundleIdentifier,  
                        (__bridge id)kSecAttrLabel:@"",  
                        (__bridge id)kSecAttrDescription:@"",  
                        (__bridge id)kSecAttrAccessible:(__bridge id)kSecAttrAccessibleWhenUnlocked};    
 
OSStatus result = SecItemAdd((__bridge CFDictionaryRef)(query), NULL);

//keychain項保護等級列表  
kSecAttrAccessibleWhenUnlocked                          //keychain項受到保護,只有在設(shè)備未被鎖定時才可以訪問  
kSecAttrAccessibleAfterFirstUnlock                      //keychain項受到保護,直到設(shè)備啟動并且用戶第一次輸入密碼  
kSecAttrAccessibleAlways                                //keychain未受保護,任何時候都可以訪問 (Default)  
kSecAttrAccessibleWhenUnlockedThisDeviceOnly            //keychain項受到保護,只有在設(shè)備未被鎖定時才可以訪問,而且不可以轉(zhuǎn)移到其他設(shè)備  
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly        //keychain項受到保護,直到設(shè)備啟動并且用戶第一次輸入密碼,而且不可以轉(zhuǎn)移到其他設(shè)備  
kSecAttrAccessibleAlwaysThisDeviceOnly                  //keychain未受保護,任何時候都可以訪問,但是不能轉(zhuǎn)移到其他設(shè)備

應(yīng)用實例

把一段信息infoStrng字符串寫進文件,然后通過Data Protection API設(shè)置保護。

NSString *documentsPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];  
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"DataProtect"];  
[infoString writeToFile:filePath  
             atomically:YES   
               encoding:NSUTF8StringEncoding  
                  error:nil];  
NSDictionary *attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete  
                                                       forKey:NSFileProtectionKey];  
[[NSFileManager defaultManager] setAttributes:attributes  
                                 ofItemAtPath:filePath  
                                        error:nil];

設(shè)備鎖屏(帶密碼保護)后,即使是越獄機,在root權(quán)限下cat讀取那個文件信息也會被拒絕。

七、越獄檢測

在應(yīng)用開發(fā)過程中,我們希望知道設(shè)備是否越獄,正以什么權(quán)限運行程序,好對應(yīng)采取一些防御和安全提示措施。

iOS7相比之前版本的系統(tǒng)而言,升級了沙盒機制,封鎖了幾乎全部應(yīng)用沙盒可以共享數(shù)據(jù)的入口。即使在越獄情況下,限制也非常多,大大增加了應(yīng)用層攻擊難度。比如,在iOS7之前,我們可以嘗試往沙盒外寫文件判斷是否越獄,但iOS7越獄后也無該權(quán)限,還使用老方法檢測會導致誤判。

那么,到底應(yīng)該如何檢測越獄呢?攻擊者又會如果攻破檢測呢?本文就著重討論一下越獄檢測的攻與防。
首先,你可以嘗試使用NSFileManager判斷設(shè)備是否安裝了如下越獄常用工具:
/Applications/Cydia.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/bin/bash
/usr/sbin/sshd
/etc/apt

但是不要寫成BOOL開關(guān)方法,給攻擊者直接鎖定目標hook繞過的機會。

+(BOOL)isJailbroken{  
    if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){  
        return YES;  
    }  
    // ...  
}

攻擊者可能會改變這些工具的安裝路徑,躲過你的判斷。

那么,你可以嘗試打開cydia應(yīng)用注冊的URL scheme:

if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){  
     NSLog(@"Device is jailbroken");  
}

但是不是所有的工具都會注冊URL scheme,而且攻擊者可以修改任何應(yīng)用的URL scheme。

那么,你可以嘗試讀取下應(yīng)用列表,看看有無權(quán)限獲取:

if ([[NSFileManager defaultManager] fileExistsAtPath:@"/User/Applications/"]){  
        NSLog(@"Device is jailbroken");  
        NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/User/Applications/"  
                                                                               error:nil];  
        NSLog(@"applist = %@",applist);  
}

越了獄的設(shè)備是可以獲取到的:


image

攻擊者可能會hook NSFileManager 的方法,讓你的想法不能如愿。

那么,你可以回避 NSFileManager,使用stat系列函數(shù)檢測Cydia等工具:

#import <sys/stat.h>  
 
void checkCydia(void)  
{  
    struct stat stat_info;  
    if (0 == stat("/Applications/Cydia.app", &stat_info)) {  
        NSLog(@"Device is jailbroken");  
    }  
}

攻擊者可能會利用 Fishhook原理 hook了stat。

那么,你可以看看stat是不是出自系統(tǒng)庫,有沒有被攻擊者換掉:

#import <dlfcn.h>  
 
void checkInject(void)  
{  
    int ret ;  
    Dl_info dylib_info;  
    int (*func_stat)(const charchar *, struct stat *) = stat;  
    if ((ret = dladdr(func_stat, &dylib_info))) {  
        NSLog(@"lib :%s", dylib_info.dli_fname);  
    }  
}

如果結(jié)果不是 /usr/lib/system/libsystem_kernel.dylib 的話,那就100%被攻擊了。
如果 libsystem_kernel.dylib 都是被攻擊者替換掉的……

那也沒什么可防的大哥你隨便吧……

那么,你可能會想,我該檢索一下自己的應(yīng)用程序是否被鏈接了異常動態(tài)庫。

列出所有已鏈接的動態(tài)庫:

#import <mach-o/dyld.h>  
 
void checkDylibs(void)  
{  
    uint32_t count = _dyld_image_count();  
    for (uint32_t i = 0 ; i < count; ++i) {  
        NSString *name = [[NSString alloc]initWithUTF8String:_dyld_get_image_name(i)];  
        NSLog(@"--%@", name);  
    }  
}

通常情況下,會包含越獄機的輸出結(jié)果會包含字符串: Library/MobileSubstrate/MobileSubstrate.dylib 。

攻擊者可能會給MobileSubstrate改名,但是原理都是通過DYLD_INSERT_LIBRARIES注入動態(tài)庫。

那么,你可以通過檢測當前程序運行的環(huán)境變量:

void printEnv(void)  
{  
    charchar *env = getenv("DYLD_INSERT_LIBRARIES");  
    NSLog(@"%s", env);  
}

未越獄設(shè)備返回結(jié)果是null,越獄設(shè)備就各有各的精彩了,尤其是老一點的iOS版本越獄環(huán)境。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375