第二章 超越基本的靜態分析:X86反匯編

來源:Malware Data Science Attack Detection and Attribution

要徹底理解惡意程序,我們通常需要超越對其節、字符串、導入和圖像的基本靜態分析。這涉及到對程序的匯編代碼進行逆向工程。事實上,反匯編和逆向工程是惡意軟件樣本深度靜態分析的核心。

由于逆向工程是一門藝術、技術工藝和科學,所以徹底的探索超出了本章的范圍。我在這里的目標是向您介紹逆向工程,以便您可以將其應用于惡意軟件數據科學。理解這種方法對于成功地將機器學習和數據分析應用于惡意軟件是至關重要的。

在本章中,我將從理解x86反匯編所需的概念開始。在本章的后面,我將展示惡意軟件作者如何試圖繞過反匯編,并討論如何減輕這些反分析和反檢測操作。但首先,讓我們回顧一些常見的反匯編方法以及x86匯編語言的基礎知識。

一、反匯編方法

反匯編是將惡意軟件的二進制代碼轉換成有效的x86匯編語言的過程。惡意軟件作者通常用高級語言(如C或c++)編寫惡意軟件程序,然后使用編譯器將源代碼編譯成x86二進制代碼。匯編語言是這種二進制代碼的可讀表示形式。因此,將惡意軟件程序分解成匯編語言是了解其核心行為的必要條件。

不幸的是,反匯編不是一件容易的事情,因為惡意軟件作者經常使用一些技巧來阻止潛在的逆向工程師。事實上,在故意混淆的情況下,完全反匯編是計算機科學中一個尚未解決的問題。目前,僅存在近似的、容易出錯的方法來反匯編這些程序。

例如,考慮自修改代碼的情況,或者在執行時修改自身的二進制代碼。正確分解這段代碼的唯一方法是理解代碼修改自身的程序邏輯,但這可能非常復雜。

由于完全反匯編目前是不可能的,我們必須用不完善的方法來完成這項任務。我們將使用的方法是線性反匯編,這涉及到在可移植可執行(PE)文件中識別與x86程序代碼相對應的連續字節序列,然后解碼這些字節。這種方法的主要限制是,它忽略了CPU在程序執行過程中如何解碼指令的細微差別。此外,它也沒有解釋惡意軟件作者有時使用的各種混淆,使他們的程序更難分析。

逆向工程的其他方法,我們在這里不討論,是工業級反匯編器(如IDA Pro)使用的更復雜的反匯編方法。這些更高級的方法實際上模擬或推理程序執行,以發現程序可能通過一系列條件分支達到哪些匯編指令。

盡管這種類型的反匯編比線性反匯編更精確,但它比線性反匯編方法占用的CPU資源要多得多,這使得它不太適合數據科學的目的,因為數據科學的重點是反匯編數千甚至數百萬個程序。

然而,在開始使用線性反匯編進行分析之前,您需要回顧匯編語言的基本組件。

二、x86匯編語言基礎

匯編語言是針對給定體系結構的人類可讀的最低級編程語言,它緊密地映射到特定CPU體系結構的二進制指令格式。匯編語言的一行幾乎總是等同于一條CPU指令。因為程序集的級別很低,所以您常常可以通過使用正確的工具輕松地從惡意軟件二進制文件中檢索它。

獲得基本的熟練閱讀反匯編的惡意軟件x86代碼比你想象的要容易。這是因為大多數惡意軟件匯編代碼花費了大部分時間通過Windows操作系統的動態鏈接庫(dll)調用操作系統,dll在運行時加載到程序內存中。惡意軟件程序使用dll來完成大部分實際工作,比如修改系統注冊表、移動和復制文件、建立網絡連接以及通過網絡協議進行通信等等。因此,跟蹤惡意程序匯編代碼通常需要了解函數調用從匯編中生成的方式,以及了解各種DLL調用的功能。當然,事情可能會變得復雜得多,但了解這么多可以揭示很多關于惡意軟件的信息。

在接下來的幾節中,我將介紹一些重要的匯編語言概念。我還解釋了一些抽象的概念,如控制流和控制流圖。最后,我們對ircbot.exe程序進行了分解,并探討了它的組裝和控制流程如何使我們了解它的用途。

x86匯編有兩種主要的語法:Intel和AT&T。在本書中,我使用的是Intel語法,它可以從所有主要的反匯編器中獲得,并且是x86 CPU的官方Intel文檔中使用的語法。

讓我們從查看CPU寄存器開始。

2.1CPU寄存器

寄存器是x86 cpu執行計算的小型數據存儲單元。由于寄存器位于CPU本身,寄存器訪問比內存訪問快幾個數量級。這就是為什么核心計算操作,如算術和條件測試指令,都是目標寄存器。這也是為什么CPU使用寄存器來存儲有關運行程序狀態的信息。雖然許多寄存器對于經驗豐富的x86匯編程序員是可用的,但是我們在這里只關注幾個重要的寄存器。

1、通用寄存器

通用寄存器對于程序集程序員來說就像一個空白空間。在32位系統上,每個寄存器包含32位、16位或8位空間,我們可以對這些空間執行算術操作、位操作、字節順序交換操作等等。

在常見的計算工作流中,程序將數據從內存或外部硬件設備轉移到寄存器中,對這些數據執行一些操作,然后將數據移回內存中進行存儲。例如,要對長列表進行排序,程序通常從內存中的數組中提取列表項,在寄存器中比較它們,然后將比較結果寫回內存。

要了解Intel 32位體系結構中通用寄存器模型的一些細微差別,請看圖2-1。

縱軸顯示通用寄存器的布局,橫軸顯示EAX、EBX、ECX和EDX是如何細分的。EAX、EBX、ECX和EDX是32位寄存器,其中包含更小的16位寄存器:AX、BX、CX和DX。如圖所示,這些16位寄存器可以細分為上下8位寄存器:AH、AL、BH、BL、CH、CL、DH和DL。雖然有時處理EAX、EBX、ECX和EDX中的子分類很有用,但是您將主要看到對EAX、EBX、ECX和EDX的直接引用。

2、堆棧和控制流寄存器

堆棧管理寄存器存儲有關程序堆棧的關鍵信息,程序堆棧負責存儲函數的局部變量、傳遞給函數的參數以及與程序控制流相關的控制信息。我們來復習一下這些寄存器。

簡單地說,ESP寄存器指向當前執行函數堆棧的頂部,而EBP寄存器指向當前執行函數堆棧的底部。這對于現代程序來說是至關重要的信息,因為這意味著通過引用相對于堆棧的數據而不是使用它的絕對地址,過程性和面向對象的代碼可以更優雅、更有效地訪問本地變量。

盡管您不會在x86匯編代碼中看到對EIP寄存器的直接引用,但它在安全分析中非常重要,特別是在漏洞研究和緩沖區溢出利用開發的上下文中。這是因為EIP包含當前執行指令的內存地址。攻擊者可以使用緩沖區溢出漏洞間接破壞EIP寄存器的值,并控制程序的執行。

除了它在開發中的作用,EIP在分析惡意軟件部署的惡意代碼方面也很重要。使用調試器,我們可以隨時檢查EIP的值,這有助于我們了解在任何特定時間執行的代碼惡意軟件。

EFLAGS是一個包含CPU標志的狀態寄存器,CPU標志是存儲當前執行程序狀態信息的位。EFLAGS寄存器是在x86程序中創建條件分支的過程的核心,或者是由于if/then風格的程序邏輯的結果而導致的執行流的更改。具體來說,當一個x86匯編程序檢查一個值是否大于或小于零,然后跳到一個函數基于這個測試的結果,EFLAGS寄存器起到了支持作用,更詳細地描述“基本塊和控制流圖”在19頁。

3、算術指令

指令在通用寄存器上操作。您可以使用算術指令使用通用寄存器執行簡單的計算。例如,add、sub、inc、dec和mul是在惡意軟件逆向工程中經常遇到的算術指令。表2-1列出了一些基本指令及其語法示例。


add指令添加兩個整數,并將結果存儲在指定的第一個操作數中,無論這個操作數是內存位置還是寄存器(根據以下語法)。記住,只有一個參數可以是內存位置。子指令類似于加法,只是它減去整數。inc指令遞增寄存器或內存位置的整數值,而dec遞減寄存器或內存位置的整數值。

4、數據轉移指令

x86處理器提供了一組健壯的指令,用于在寄存器和內存之間移動數據。這些指令提供了允許我們操作數據的底層機制。主體內存運動指令是mov指令。表2-2顯示了如何使用mov指令移動數據。


與mov指令相關,lea指令將指定的絕對內存地址加載到寄存器中,用于獲得指向內存位置的指針。例如,lea edx [ESP -4]從ESP中的值中減去4并將結果值加載到edx中。

5、棧指令

x86匯編中的堆棧是一種數據結構,允許您將值推入和彈出到堆棧中。這類似于在一堆板的頂部或頂部添加和刪除板。

因為控制流往往是通過c風格的表達在x86匯編函數調用,因為這些函數調用使用堆棧來傳遞參數,分配本地變量,并且記住什么該計劃的一部分返回一個函數執行完畢后,棧需要理解和控制流在一起。

當程序員希望將寄存器值保存到堆棧中時,push指令將值推送到程序堆棧中,pop指令將從堆棧中刪除值并將它們放入指定的寄存器中。

push指令使用以下語法執行操作:

push 1

在本例中,程序將堆棧指針(寄存器ESP)指向一個新的內存地址,從而為值(1)騰出空間,該值現在存儲在堆棧的頂部位置。然后它將值從參數復制到CPU剛剛為堆棧頂部騰出空間的內存位置。

讓我們將其與pop進行對比:

pop eax

該程序使用pop將堆棧頂部的值彈出并將其移動到指定的寄存器中。在本例中,pop eax從堆棧中彈出頂部值并將其移動到eax中。

關于x86程序堆棧,需要理解的一個不直觀但重要的細節是,它在內存中向下增長,因此堆棧上的最大值實際上存儲在堆棧內存中的最低地址。當您分析引用存儲在堆棧上的數據的匯編代碼時,記住這一點非常重要,因為除非您知道堆棧的內存布局,否則很快就會混淆。

因為x86堆棧增長下行在內存中,當推指令分配空間程序堆棧的一個新值,它將ESP的價值,讓它指向一個較低的位置在內存中,然后從目標寄存器值拷貝到內存位置,堆棧的頂部開始地址和成長。相反,pop指令實際上復制堆棧的頂部值,然后增加ESP的值,使其指向更高的內存位置。

6、控制流指令

x86程序的控制流定義了程序可能執行的指令執行序列的網絡,具體取決于程序可能接收到的數據、設備交互和其他輸入。控制流指令定義程序的控制流。它們比堆棧指令更復雜,但仍然非常直觀。由于控制流在x86匯編中通常是通過c風格的函數調用來表示的,所以堆棧和控制流是緊密相關的。它們也是相關的,因為這些函數調用使用堆棧傳遞參數、分配局部變量,并記住函數執行完后返回程序的哪個部分。

調用和ret控制流指令對于程序在x86匯編中如何調用函數以及程序在執行這些函數之后如何從函數返回是最重要的。

調用指令調用一個函數。可以把它看作是一個函數,您可以用C之類的高級語言編寫它,以便在調用調用指令并完成函數的執行之后,程序返回到該指令。您可以使用以下語法調用調用指令,其中address表示調用指令調用函數的內存。可以把它看作是一個函數,您可以用C之類的高級語言編寫它,以便在調用調用指令并完成函數的執行之后,程序返回到該指令。您可以使用以下語法調用調用指令,其中address表示內存:

call?address

調用指令做兩件事。首先,它將函數調用返回后將執行的指令的地址推到堆棧的頂部,以便程序知道被調用的函數執行完后返回什么地址。其次,call用地址操作數指定的值替換EIP的當前值。然后,CPU在EIP指向的新內存位置開始執行。

就像調用開始一個函數調用一樣,ret指令完成它。你可以單獨使用ret指令,不需要任何參數,如下圖所示:

ret

當被調用時,ret將從堆棧中彈出頂部值,我們希望該值是調用指令被調用時被推入堆棧的保存程序計數器值(EIP)。然后它將彈出的程序計數器值放回EIP并繼續執行。

jmp指令是另一個重要的控制流結構,它的操作比調用簡單。jmp不需要擔心保存EIP,只需告訴CPU移動到作為其參數指定的內存地址,并在那里開始執行。例如,jmp 0x12345678告訴CPU在下一條指令上開始執行存儲在內存位置0x12345678的程序代碼。

您可能想知道如何使jmp和調用指令以一種有條件的方式執行,比如“如果程序收到了一個網絡包,執行以下函數”。答案是x86程序集沒有高級構造,比如if、then、else、else if等等。相反,在程序代碼中分支到一個地址通常需要兩條指令:一條cmp指令,它根據某個測試值檢查某個寄存器中的值,并將該測試的結果存儲在EFLAGS寄存器中;另一條是條件分支指令。

大多數條件分支指令都以j開頭,它允許程序跳轉到內存地址,并使用表示測試條件的字母進行后置。例如,jge告訴程序在大于或等于時跳轉。這意味著要測試的寄存器中的值必須大于或等于測試值。

cmp指令使用以下語法:

cmp?register, memory location, or literal, register, memory location, or

literal

如前所述,cmp將指定的通用寄存器中的值與值進行比較,然后將比較的結果存儲在EFLAGS寄存器中。

然后調用各種條件jmp指令如下:

j* address

正如您所看到的,我們可以將j前綴為任意數量的條件測試指令。例如,只有當被測試的值大于或等于寄存器中的值時,才要跳轉,請使用以下指令:

jge address

注意,與調用和ret指令的情況不同,jmp指令家族從不涉及程序堆棧。實際上,在jmp指令家族中,x86程序負責跟蹤自己的執行流,并可能保存或刪除關于它訪問過哪些地址的信息,以及在執行了特定的指令序列之后應該返回到哪里的信息。

7、基本塊和控制流程圖

雖然當我們在文本編輯器中滾動x86程序的代碼時,它們看起來是順序的,但實際上它們有循環、條件分支和無條件分支(控制流)。所有這些都為每個x86程序提供了一個網絡結構。讓我們使用清單2-1中的簡單玩具組裝程序來看看這是如何工作的。

正如您可以看到的,這個程序初始化計數器的值10,存儲在寄存器EAX?。接下來,它的循環價值EAX由每個迭代1?遞減。最后,一旦EAX已經到了一個值0?,程序循環的爆發。

在控制流程圖分析語言中,我們可以把這些指令看作是由三個基本塊組成的。基本塊是一系列指令,我們知道這些指令總是連續執行的。換句話說,基本塊總是以分支指令或分支目標指令結束,并且總是以程序的第一條指令(稱為程序的入口點)或分支目標開始。

在清單2-1中,您可以看到我們的簡單程序的基本塊從哪里開始和結束。第一個基本塊由指令mov eax, 10在setup:下組成。第二個基本塊由以下幾行組成:從子eax開始,1到在loopstart:下面的jne $loopstart,第三個基本塊從mov eax開始,1在loopend:下面。我們可以使用圖2-2中的圖可視化基本塊之間的關系。(我們使用術語圖與術語網絡同義;在計算機科學中,這些術語是可以互換的。


如果一個基本塊可以流到另一個基本塊中,我們將它連接起來,如圖2-2所示。如圖所示,setup基本塊指向loopstart基本塊,該基本塊在轉換到loopend基本塊之前重復10次。現實世界中的程序有這樣的控制流程圖,但它們要復雜得多,有數千個基本塊和數千個互連。

8、使用pefile和capstone反匯編ircbot.exe

現在您已經很好地理解了匯編語言的基礎知識,讓我們來分解ircbot的前100個字節。使用線性反匯編的exe匯編代碼。為此,我們將使用開放源碼Python庫pefile(在第1章中介紹)和capstone,這是一個可以反匯編32位x86二進制代碼的開放源碼反匯編庫。您可以使用以下命令使用pip安裝這兩個庫:

pip install pefile

pip install capstone

安裝了這兩個庫之后,我們可以使用清單2-2中的代碼利用它們來反編譯ircbot.exe。

#!/usr/bin/python

import pefile

from capstone import *

# load the target PE file

pe = pefile.PE("ircbot.exe")

# get the address of the program entry point from the program header

entrypoint = pe.OPTIONAL_HEADER.AddressOfEntryPoint

# compute memory address where the entry code will be loaded into memory

entrypoint_address = entrypoint+pe.OPTIONAL_HEADER.ImageBase

# get the binary code from the PE file object

binary_code = pe.get_memory_mapped_image()[entrypoint:entrypoint+100]

# initialize disassembler to disassemble 32 bit x86 binary code

disassembler = Cs(CS_ARCH_X86, CS_MODE_32)

# disassemble the code

for instruction in disassembler.disasm(binary_code, entrypoint_address):

print "%s\t%s" %(instruction.mnemonic, instruction.op_str)

這將產生以下輸出:

?push????ebp

mov?????ebp, esp

push????-1

push????0x437588

push????0x41982c

?mov?????eax, dword ptr fs:[0]

push????eax

mov?????dword ptr fs:[0], esp

?add?????esp, -0x5c

push????ebx

push????esi

push????edi

mov?????dword ptr [ebp - 0x18], esp

?call????dword ptr [0x496308]

--snip--

不要擔心理解反匯編輸出中的所有指令:這將涉及到超出本書范圍的對匯編的理解。

但是,您應該對輸出中的許多指令感到滿意,并對它們的功能有一定的了解。例如,惡意軟件推動EBP寄存器中的值壓入堆棧?,保存它的價值。然后將ESP中的值移動到EBP中,并將一些數值推入堆棧。程序會將一些數據在內存中移動到EAX寄存器?,和它添加值0 x5c?ESP寄存器中的值。最后,程序使用的調用指令調用一個函數存儲在內存地址0 x496308?。

因為這不是一本關于逆向工程的書,所以我在這里不再深入討論代碼的含義。我所介紹的是理解匯編語言如何工作的一個開端。有關匯編語言的更多信息,我推薦使用Intel程序員手冊,網址是http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html。

9、限制靜態分析的因素

在本章和第1章中,您了解了各種使用靜態分析技術來闡明新發現的惡意二進制文件的目的和方法的方法。不幸的是,靜態分析有一些限制,使得它在某些情況下用處不大。例如,惡意軟件作者可以使用某些攻擊策略,這些策略的實現要比防御容易得多。讓我們來看看這些進攻性的戰術,看看如何防御它們。

(1)包裝

惡意軟件打包是惡意軟件作者壓縮、加密或以其他方式破壞大部分惡意程序的過程,從而使惡意軟件分析人員無法理解這些程序。當惡意軟件運行時,它會解包,然后開始執行。繞過惡意軟件打包最明顯的方法是在安全的環境中實際運行惡意軟件,這是我將在第3章介紹的動態分析技術。

注意:出于合法的原因,軟件安裝程序也會使用軟件打包。良性軟件作者使用打包來交付他們的代碼,因為它允許他們壓縮程序資源來減少軟件安裝程序下載的大小。它還幫助它們阻止業務競爭對手的逆向工程嘗試,并提供了一種方便的方法來將許多程序資源捆綁到一個安裝程序文件中。

(2)資源混淆

惡意軟件作者使用的另一種反檢測、反分析技術是資源混淆。它們混淆了程序資源(如字符串和圖形圖像)存儲在磁盤上的方式,然后在運行時對它們進行反混淆,以便惡意程序可以使用它們。例如,一個簡單的混淆操作是在PE resources部分中存儲的圖像和字符串中的所有字節中添加一個值1,然后在運行時從所有這些數據中減去1。當然,這里可能存在許多混淆,所有這些都使惡意軟件分析人員難以使用靜態分析來理解惡意軟件二進制文件。

與打包一樣,繞過資源混淆的一種方法是在安全的環境中運行惡意軟件。當這不是一個選項時,唯一能減輕資源混淆的方法就是找出惡意軟件混淆其資源的方式,并手動消除它們的混淆,這是專業惡意軟件分析師經常做的事情。

(3)Anti-disassembly技術

惡意軟件作者使用的第三組反檢測、反分析技術是反匯編技術。這些技術旨在利用最先進的反匯編技術的固有限制,向惡意軟件分析人員隱藏代碼,或使惡意軟件分析人員認為存儲在磁盤上的代碼塊包含與實際不同的指令。

反匯編技術的一個例子是將分支轉移到一個內存位置,惡意軟件作者的反匯編程序將把這個內存位置解釋為另一條指令,本質上是向逆向工程師隱藏惡意軟件的真實指令。反匯編技術有巨大的潛力,沒有完美的方法來抵御它們。在實踐中,針對這些技術的兩種主要防御方法是在動態環境中運行惡意軟件樣本,并手動找出惡意軟件樣本中反匯編策略的表現,以及如何繞過它們。

(4)動態下載數據

惡意軟件作者使用的最后一類反分析技術包括從外部獲取數據和代碼。例如,惡意軟件示例可能在惡意軟件啟動時從外部服務器動態加載代碼。如果是這種情況,靜態分析對于這樣的代碼將是無用的。類似地,惡意軟件可能在啟動時從外部服務器獲取解密密鑰,然后使用這些密鑰解密將在惡意軟件執行過程中使用的數據或代碼。

顯然,如果惡意軟件使用工業級加密算法,靜態分析將不足以恢復加密的數據和代碼。這種反分析和反檢測技術非常強大,解決它們的唯一方法是通過某種方式獲取外部服務器上的代碼、數據或私鑰,然后使用它們來分析所涉及的惡意軟件。

總結

本章介紹了x86匯編代碼分析,并演示了如何使用開放源碼Python工具在ircbot.exe上執行基于解體的靜態分析。雖然這并不是x86程序集的完整入門,但是您現在應該已經有了一個了解給定惡意程序集轉儲中發生的事情的起點。最后,您了解了惡意軟件作者如何防范反匯編和其他靜態分析技術,以及如何減輕這些反分析和反檢測策略。在第3章中,您將學習如何進行動態惡意軟件分析,這彌補了靜態惡意軟件分析的許多弱點。

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

推薦閱讀更多精彩內容