基于LLVM IR的防Hook方案

1. 什么是LLVM IR

當(dāng)我們點(diǎn)擊Xcode進(jìn)行編譯時(shí),查看日志可以看到每一個(gè)編譯單元都有指定大量的編譯參數(shù),我們跳過(guò)編譯前的預(yù)處理和語(yǔ)法分析,使用 clang -emit-llvm XXX -S -o XXX.ll 直接導(dǎo)出查看其生成的IR(Intermediate Representation)。

也許你對(duì)于IR很陌生,但是Bitcode肯定會(huì)知道 。實(shí)際上,當(dāng)我們?cè)O(shè)置了 Enable Bitcode=YES ,進(jìn)行Archive時(shí),Bitcode會(huì)被嵌入到鏈接后的Mach-O中,用于提交到App Store。實(shí)際上,Bitcode就是二進(jìn)制格式的IR。

非Archive編譯時(shí),Enable Bitcode 將只增加一個(gè)編譯參數(shù) -fembed-bitcode-marker, 該參數(shù)用于在Mach-O中作為占位。因?yàn)楸镜鼐幾g調(diào)試時(shí)并不需要bitcode,去掉這個(gè)步驟可以大大加快編譯速度。

對(duì)于靜態(tài)庫(kù)等打開了Bitcode編譯,通過(guò)MachOview查看會(huì)發(fā)現(xiàn)有一個(gè)__LLVM, __bitcode 段;而全工程編譯出來(lái)對(duì)應(yīng)的是 __LLVM, __bundle 段;可以使用 segedit 命令將指定的Section導(dǎo)出:

segedit XXX.o -extract __LLVM __bitcode result.bc


2. IR文件結(jié)構(gòu)

如下,IR的結(jié)構(gòu)可分為3部分。

1.Module

可以理解為一個(gè)類文件對(duì)應(yīng)一個(gè)Module,作為一個(gè)獨(dú)立的編譯單元。其內(nèi)部包含聲明以及定義的函數(shù),全局變量等,以及架構(gòu)信息等。

2.Function

Function相當(dāng)于C里面的方法,其必須存在于Module中,內(nèi)部由參數(shù),返回類型以及多個(gè)BasicBlock組成,每個(gè)Function的起始block是一個(gè)EntryBlock,也是列表的第一個(gè)BasicBlock。

3.Basic Block

BasicBlock則是Instruction存放的地方,Instruction對(duì)應(yīng)的就是我們真正的可執(zhí)行代碼。Instruction可分為普通指令以及Terminator指令,并且BasicBlock都是以Terminator Instruction結(jié)尾,包括跳轉(zhuǎn),返回,異常等。


3. 語(yǔ)法格式

以下是一些基礎(chǔ)的語(yǔ)法,可以幫助我們大致看懂一些簡(jiǎn)單的實(shí)現(xiàn)。

  1. 以@開頭為全局標(biāo)識(shí)符(函數(shù),全局變量);以%開頭為局部變量。
  2. %a = alloca i32, align 4 ,alloca相當(dāng)于malloc,用于內(nèi)存分配且自動(dòng)釋放;i32為占有幾位,此為4個(gè)字節(jié);align字節(jié)對(duì)齊。
  3. label 嚴(yán)格的講它也是一種數(shù)據(jù)類型(type),但它可以標(biāo)識(shí)入口,相當(dāng)于代碼標(biāo)簽。
  4. 函數(shù)的聲明使用declare,函數(shù)的定義使用define。
  5. 數(shù)組類型用[count x ix]表示,其中count表示數(shù)組的大小,ix表示數(shù)組中每一個(gè)元素對(duì)應(yīng)的數(shù)據(jù)類型,比如字符串”Hello IR”表示為[9 x i8],9表示該字符串包含9個(gè)元素(末尾包含一個(gè)\0),每個(gè)元素大小為i8即c語(yǔ)言中的char類型大小。

接著,我們可以通過(guò) clang -emit-llvm XXX -S -o XXX.ll 導(dǎo)出一個(gè)OC類用Sublime或其他文本編輯器打開來(lái)看看更深入的結(jié)構(gòu)。

  1. target datalayout: 該字符串指定如何在內(nèi)存中布局?jǐn)?shù)據(jù),例如:
target datalayout = "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32"
    // e表示小端對(duì)齊
  // m指定在輸出中進(jìn)行名字重整,以混亂的轉(zhuǎn)義字符\01為前綴的符號(hào)將直接傳遞給匯編程序,而不包含轉(zhuǎn)義字符。 m:o Mach-O mangling風(fēng)格,私有符號(hào)添加L前綴,其他符號(hào) _前綴
  // p:32:32 32-bit的指針進(jìn)行32bit對(duì)齊
  // Fi8 指定函數(shù)指針的對(duì)齊方式,i表示函數(shù)指針的對(duì)齊與函數(shù)本身是獨(dú)立的,8則函數(shù)指針的對(duì)齊方式是函數(shù)上指定的顯式對(duì)齊方式的倍數(shù),即8倍
  // f64:32:64 double類型有32bits的ABI對(duì)齊但是優(yōu)先64Bits對(duì)齊
  // v64:32:64 64-bit vector同上
  // v128:32:128 同上
  // a:0:32 聚合類型(數(shù)組和結(jié)構(gòu)體)32位對(duì)齊
  // n32 指定目標(biāo)CPU本地整數(shù)寬度為32bits
  // S32 未指定的堆棧對(duì)齊為32bits
  1. Opaque Structure Types: 不透明結(jié)構(gòu)類型用于表示沒有指定主體的已命名結(jié)構(gòu)類型。
%0 = type opaque
  1. Attribute groups:IR中對(duì)象引用的屬性組。它們對(duì)于保持.ll文件可讀性很重要,因?yàn)樵S多功能將使用同一組屬性。在與.ll單個(gè).c文件對(duì)應(yīng)的文件的退化情況下 ,單個(gè)屬性組將捕獲用于構(gòu)建該文件的重要命令行標(biāo)志。
attributes #2 = { nounwind readnone speculatable willreturn }
attributes #3 = { nounwind }
  1. Module Flags Metadata: 整個(gè)模塊的信息如果僅僅依靠IR是很難傳遞給LLVM的子系統(tǒng)的。llvm.module.flags 的元數(shù)據(jù)就是為了解決這個(gè)問(wèn)題,這些標(biāo)志以鍵/值對(duì)的形式出現(xiàn),類似于字典,使得任何關(guān)心標(biāo)志的子系統(tǒng)都可以很容易地進(jìn)行查找。
// 三元組的第一個(gè)元素是行為標(biāo)志,指定當(dāng)多個(gè)模塊合并在一起時(shí)的行為,并且元數(shù)據(jù)是相同的ID
        1 表示Error,當(dāng)兩個(gè)值不同時(shí)發(fā)出錯(cuò)誤,否則結(jié)果值為操作數(shù)
    2 表示W(wǎng)arning,如果兩個(gè)值不一致,則發(fā)出警告。結(jié)果值將是被鏈接的第一個(gè)模塊的標(biāo)志的操作數(shù),或者如果其他模塊使用max,則為max(在這種情況下,結(jié)果標(biāo)志將是max)
        。。。

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 14, i32 0]}
!1 = !{i32 1, !"Objective-C Version", i32 2}
// !0的ID為!"SDK Version",值為2個(gè)數(shù)組元素分別為14和0,行為則是如果出現(xiàn)兩個(gè)以上的!"SDK Version"并且他們的值不相等,則拋出error
// !1的ID為!"Objective-C Version",值為2,當(dāng)出現(xiàn)多個(gè)!"Objective-C Version"且值不同時(shí)則發(fā)出warning
      
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}
  1. DICompileUnit:表示一個(gè)編譯單元,enums:,retainedTypes:,globals:,macros: 這些字段是一些內(nèi)部包含與編譯單元相關(guān)調(diào)試信息的元組,與代碼優(yōu)化無(wú)關(guān)(有些節(jié)點(diǎn)只有在指令引用它們時(shí)才會(huì)發(fā)出)。
!11 = distinct !DICompileUnit(language: DW_LANG_ObjC, file: !12, producer: "Apple clang version 12.0.0 (clang-1200.0.32.2)", isOptimized: false, runtimeVersion: 2, emissionKind: FullDebug, enums: !13, retainedTypes: !14, imports: !23, nameTableKind: None, sysroot: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk", sdk: "iPhoneOS14.0.sdk")

  // DIFile節(jié)點(diǎn)表示文件
!12 = !DIFile(filename: "/Users/XXX/Desktop/TestSpeed/TestSpeed/AppDelegate.m", directory: "/Users/XXX/Desktop/bcTest/vm")

!13 = !{}
!14 = !{!15}
// DICompositeType 表示由其他類型組成的類型,如結(jié)構(gòu)體,unions
!15 = !DICompositeType(tag: DW_TAG_structure_type, name: "AppDelegate", scope: !17, file: !16, line: 11, size: 32, flags: DIFlagObjcClassComplete, elements: !18, runtimeLang: DW_LANG_ObjC)
  
  // Represents a module in the programming language, for example, a Clang module, or a Fortran module.
!22 = !DIModule(scope: null, name: "UIKit", configMacros: "\22-DNS_BLOCK_ASSERTIONS=1\22 \22-DOBJC_OLD_DISPATCH_PROTOTYPES=0\22", includePath: "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework")
!23 = !{!24}
// DIImportedEntity節(jié)點(diǎn)表示導(dǎo)入到編譯單元的實(shí)體
!24 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !11, entity: !22, file: !16, line: 9)
  
  // 編譯單元的描述符則由!llvm.dbg.cu收集,用于跟蹤全局變量,類型信息 & 導(dǎo)入的實(shí)體(聲明和namespace)
!llvm.dbg.cu = !{!11, !25, !27, !29}
  1. Automatic Linker Flags Named Metadata: 一些目標(biāo)支持在單個(gè)對(duì)象文件中嵌入標(biāo)記到鏈接器,通常,它與語(yǔ)言擴(kuò)展一起使用,語(yǔ)言擴(kuò)展允許源文件包含鏈接器命令行選項(xiàng),并通過(guò)目標(biāo)文件將這些選項(xiàng)自動(dòng)傳輸?shù)芥溄悠鳌_@些標(biāo)志使用 !llvm.link .options 的命名元數(shù)據(jù)在IR中編碼。每個(gè)操作數(shù)都應(yīng)該是一個(gè)元數(shù)據(jù)節(jié)點(diǎn),而元數(shù)據(jù)節(jié)點(diǎn)應(yīng)該是其他元數(shù)據(jù)節(jié)點(diǎn)的列表,每個(gè)元數(shù)據(jù)節(jié)點(diǎn)應(yīng)該是定義鏈接器選項(xiàng)的元數(shù)據(jù)字符串列表。
//如下,指定了幾組linker options,鏈接iOS相關(guān)庫(kù)
!llvm.linker.options = !{!31, !32, !33, !34, !35, !36}
!31 = !{!"-framework", !"UIKit"}
!32 = !{!"-framework", !"FileProvider"}
!33 = !{!"-framework", !"UserNotifications"}
!34 = !{!"-framework", !"CoreText"}
!35 = !{!"-framework", !"QuartzCore"}
!36 = !{!"-framework", !"CoreImage"}
  1. DISubprogram:表示來(lái)自源語(yǔ)言的函數(shù),可以使用!dbg元數(shù)據(jù)將一個(gè)不同的DISubprogram附加到函數(shù)定義中,唯一的DISubprogram可以附加到用于call site調(diào)試信息的函數(shù)聲明中。
!48 = distinct !DISubprogram(name: "-[AppDelegate application:didFinishLaunchingWithOptions:]", scope: !17, file: !17, line: 19, type: !49, scopeLine: 19, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !11, retainedNodes: !13)
 
 // DIFile節(jié)點(diǎn)表示文件
!17 = !DIFile(filename: "TestSpeed/TestSpeed/AppDelegate.m", directory: "/Users/XXX/Desktop")

  //DISubroutineType節(jié)點(diǎn)表示子例程類型,types字段引用一個(gè)元組,第一個(gè)操作數(shù)為返回類型,其次依次為形參的類型,即!50。 如果第一個(gè)參數(shù)為null,則表示函數(shù)的返回值為void
!49 = !DISubroutineType(types: !50)
!50 = !{!51, !56, !58, !61, !64}

//DIDerivedType節(jié)點(diǎn)表示從其他類型(比如限定類型)派生的類型。DW_TAG_typedef用于為baseType提供一個(gè)名稱
!51 = !DIDerivedType(tag: DW_TAG_typedef, name: "BOOL", scope: !53, file: !52, line: 81, baseType: !55)
//DIBasicType節(jié)點(diǎn)表示基本類型,比如int、bool和float。標(biāo)簽:默認(rèn)為DW_TAG_base_type。
!55 = !DIBasicType(name: "signed char", size: 8, encoding: DW_ATE_signed_char)

  1. getelementptr: 用于獲取聚合數(shù)據(jù)結(jié)構(gòu)(數(shù)組或結(jié)構(gòu)體)的子元素的地址。它只執(zhí)行地址計(jì)算,不訪問(wèn)內(nèi)存。該指令也可用于計(jì)算vector的地址。例如:
struct RT {
  char A;
  int B[10][20];
  char C;
};
struct ST {
  int X;
  double Y;
  struct RT Z;
};
///定義了RI ST結(jié)構(gòu)體并在foo中使用
int *foo(struct ST *s) {
  return &s[1].Z.B[5][13];
}

///在IR中表示
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }

define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
entry:
//第一個(gè)參數(shù)i64 1指向struct.ST類型,即%struct.ST*結(jié)構(gòu)體的一個(gè)指針
//第二個(gè)參數(shù)i32 2表示指向ST結(jié)構(gòu)體的第2個(gè)元素,即RT
//第三個(gè)參數(shù)i32 1表示指向RT的第一個(gè)元素,array B[10][20]
//最后兩個(gè)則就是取出數(shù)組的對(duì)應(yīng)下標(biāo)的值
  %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
  ret i32* %arrayidx
}
//于是上面的arrayidx拆分下來(lái)等價(jià)于如下:第一步拿到struct.ST,然后取出ST位于index 2處的struct.RT,隨后struct.RT的index 1處為int二維數(shù)組,最后對(duì)B[5][13]進(jìn)行設(shè)置偏移
  %t1 = getelementptr %struct.ST, %struct.ST* %s, i32 1     
  %t2 = getelementptr %struct.ST, %struct.ST* %t1, i32 0, i32 2  
  %t3 = getelementptr %struct.RT, %struct.RT* %t2, i32 0, i32 1     
  %t4 = getelementptr [10 x [20 x i32]], [10 x [20 x i32]]* %t3, i32 0, i32 5 
  %t5 = getelementptr [20 x i32], [20 x i32]* %t4, i32 0, i32 13        
  ret i32* %t5


4. 修改OC的消息發(fā)送為直接調(diào)用

我們知道在OC中方法調(diào)用都是通過(guò)runtime進(jìn)行msgSend調(diào)用的,那么能否對(duì)一些編譯期間已經(jīng)確定了的調(diào)用規(guī)則改為直接調(diào)用的方式來(lái)避免被hook呢?

//OC代碼如下:
- (void)runTestOne {
    [self runTestTwo];
    [self runTestThree:10];
    int a = [self runTestFour];
    NSLog(@"%d", a);
}

- (void)runTestTwo {
    NSLog(@"call TestTwo");
}

- (void)runTestThree:(int)value {
    NSLog(@"call TestThree %d", value);
}

- (int)runTestFour {
    return 1;
}

//------ clang -S -fobjc-arc -emit-llvm TestIR.m -o TESTIR.ll 導(dǎo)出IR ------

; Function Attrs: nonlazybind  //禁止函數(shù)的延遲符號(hào)綁定。這可能會(huì)更快地調(diào)用函數(shù),但如果在程序啟動(dòng)期間沒有調(diào)用函數(shù),則會(huì)付出額外的程序啟動(dòng)時(shí)間。
  //聲明外部符號(hào),#4對(duì)應(yīng)上面第3點(diǎn)的屬性組
declare i8* @objc_msgSend(i8*, i8*, ...) #4 

  //noinline:不內(nèi)聯(lián)調(diào)用 optnone:跳過(guò)optimization pass ssp: 開啟堆棧保護(hù)
; Function Attrs: noinline optnone ssp
  //名字重整,以轉(zhuǎn)義字符\01為前綴
  //%0則表示上2中的不透明結(jié)構(gòu)類型,也是就msg_send的第一個(gè)參數(shù),id self
  //#1為類型組
  //!dbg !80 將元數(shù)據(jù)!80使用!dbg附加到方法中,!80則是上面提到的DISubprogram對(duì)象
  define internal void @"\01-[TestIR runTestOne]"(%0* %0, i8* %1) #1 {
  %3 = alloca %0*, align 8
  %4 = alloca i8*, align 8
  %5 = alloca i32, align 4
  store %0* %0, %0** %3, align 8
  store i8* %1, i8** %4, align 8
  %6 = load %0*, %0** %3, align 8
  // OBJC_SELECTOR_REFERENCES_.2 即為sel,sel是通過(guò)OBJC_METH_VAR_NAME_獲取到方法的字符串
  %7 = load i8*, i8** @OBJC_SELECTOR_REFERENCES_, align 8, !invariant.load !9
  //將%0*類型的%6 轉(zhuǎn)換為i8*
  %8 = bitcast %0* %6 to i8*
  // i8* (i8*, i8*, ...) 的objc_msgSend方法, 轉(zhuǎn)成void (i8*, i8*) 再進(jìn)行傳參調(diào)用
  call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* %8, i8* %7)
  %9 = load %0*, %0** %3, align 8
  %10 = load i8*, i8** @OBJC_SELECTOR_REFERENCES_.2, align 8, !invariant.load !9
  %11 = bitcast %0* %9 to i8*
  // 轉(zhuǎn)成 void (i8*, i8*, i32) 即增加一個(gè)入?yún)?  call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i32)*)(i8* %11, i8* %10, i32 10)
  %12 = load %0*, %0** %3, align 8
  %13 = load i8*, i8** @OBJC_SELECTOR_REFERENCES_.4, align 8, !invariant.load !9
  %14 = bitcast %0* %12 to i8*
  // 轉(zhuǎn)成 i32  (i8*, i8*) 返回值為i32
  %15 = call i32 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 (i8*, i8*)*)(i8* %14, i8* %13)
  store i32 %15, i32* %5, align 4
  %16 = load i32, i32* %5, align 4
  notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*), i32 %16)
  ret void
}

可以看到,調(diào)用OC方法,即內(nèi)部都是通過(guò)objc_msgSend或其他幾個(gè)衍生方法來(lái)實(shí)現(xiàn)的,i8* @objc_msgSend(i8*, i8*, ...)這是一個(gè)帶變參的C函數(shù),第一個(gè)參數(shù)表示指向類實(shí)例的指針,第二個(gè)參數(shù)表示方法的選擇子,其余則為可變參數(shù)列表。換言之,該函數(shù)通過(guò)向Objective-C運(yùn)行時(shí)傳遞消息來(lái)間接調(diào)用,然后通過(guò)提供的入?yún)?lái)找到正確調(diào)用的真正函數(shù)。

嘗試直接在IR中修改為直接call真正的調(diào)用方法:

//第1處的msgSend調(diào)用
call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* %8, i8* %7)
//替換為:
call void @"\01-[TestIR runTestTwo]"(%0* %6, i8* %7)
  
//第2處的msgSend調(diào)用
  call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i32)*)(i8* %11, i8* %10, i32 10)
//替換為:
call void @"\01-[TestIR runTestThree:]"(%0* %9, i8* %10, i32 10)

//第3處的msgSend調(diào)用
%15 = call i32 bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i32 (i8*, i8*)*)(i8* %14, i8* %13)
//替換為:
%15 = call i32 @"\01-[TestIR runTestFour]"(%0* %12, i8* %13)

修改完成執(zhí)行 llc -filetype=obj TESTIR.ll 生成目標(biāo)文件,然后通過(guò)gcc生成可執(zhí)行文件,最終執(zhí)行如下:

./a.out 
a.out[4491:1939294] call TestTwo
a.out[4491:1939294] call TestThree param: 10
a.out[4491:1939294] 1

可以看到,此種直接調(diào)用的方案對(duì)于明確指定的方法調(diào)用是可行的。

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