[轉]緩沖區(qū)溢出攻擊

原文鏈接:https://www.cnblogs.com/fanzhidongyzby/p/3250405.html

緩沖區(qū)溢出(Buffer Overflow)是計算機安全領域內既經典而又古老的話題。隨著計算機系統(tǒng)安全性的加強逊桦,傳統(tǒng)的緩沖區(qū)溢出攻擊方式可能變得不再奏效晴氨,相應的介紹緩沖區(qū)溢出原理的資料也變得“大眾化”起來个粱。其中看雪的《0day安全:軟件漏洞分析技術》一書將緩沖區(qū)溢出攻擊的原理闡述得簡潔明了池颈。本文參考該書對緩沖區(qū)溢出原理的講解,并結合實際的代碼實例進行驗證赤炒。不過即便如此蟹略,完成一個簡單的溢出代碼也需要解決很多書中無法涉及的問題,尤其是面對較新的具有安全特性的編譯器——比如MS的Visual Studio2010哈打。接下來塔逃,我們結合具體代碼,按照對緩沖區(qū)溢出原理的循序漸進地理解方式去挖掘緩沖區(qū)溢出背后的底層機制料仗。

# 代碼 <=> 數(shù)據

顧名思義湾盗,緩沖區(qū)溢出的含義是為緩沖區(qū)提供了多于其存儲容量的數(shù)據,就像往杯子里倒入了過量的水一樣立轧。通常情況下格粪,緩沖區(qū)溢出的數(shù)據只會破壞程序數(shù)據,造成意外終止氛改。但是如果有人精心構造溢出數(shù)據的內容帐萎,那么就有可能獲得系統(tǒng)的控制權!如果說用戶(也可能是黑客)提供了水——緩沖區(qū)溢出攻擊的數(shù)據胜卤,那么系統(tǒng)提供了溢出的容器——緩沖區(qū)疆导。

緩沖區(qū)在系統(tǒng)中的表現(xiàn)形式是多樣的,高級語言定義的變量葛躏、數(shù)組是鬼、結構體等在運行時可以說都是保存在緩沖區(qū)內的肤舞,因此所謂緩沖區(qū)可以更抽象地理解為一段可讀寫的內存區(qū)域,緩沖區(qū)攻擊的最終目的就是希望系統(tǒng)能執(zhí)行這塊可讀寫內存中已經被蓄意設定好的惡意代碼均蜜。按照馮·諾依曼存儲程序原理李剖,程序代碼是作為二進制數(shù)據存儲在內存的,同樣程序的數(shù)據也在內存中囤耳,因此直接從內存的二進制形式上是無法區(qū)分哪些是數(shù)據哪些是代碼的篙顺,這也為緩沖區(qū)溢出攻擊提供了可能。
圖1 進程地址空間分布

圖1是進程地址空間分布的簡單表示充择。代碼存儲了用戶程序的所有可執(zhí)行代碼德玫,在程序正常執(zhí)行的情況下,程序計數(shù)器(PC指針)只會在代碼段和操作系統(tǒng)地址空間(內核態(tài))內尋址椎麦。數(shù)據段內存儲了用戶程序的全局變量宰僧,文字池等。椆劭妫空間存儲了用戶程序的函數(shù)棧幀(包括參數(shù)琴儿、局部數(shù)據等),實現(xiàn)函數(shù)調用機制嘁捷,它的數(shù)據增長方向是低地址方向造成。堆空間存儲了程序運行時動態(tài)申請的內存數(shù)據等,數(shù)據增長方向是高地址方向雄嚣。除了代碼段和受操作系統(tǒng)保護的數(shù)據區(qū)域晒屎,其他的內存區(qū)域都可能作為緩沖區(qū),因此緩沖區(qū)溢出的位置可能在數(shù)據段缓升,也可能在堆鼓鲁、棧段。如果程序的代碼有軟件漏洞港谊,惡意程序會“教唆”程序計數(shù)器從上述緩沖區(qū)內取指坐桩,執(zhí)行惡意程序提供的數(shù)據代碼!本文分析并實現(xiàn)棧溢出攻擊方式封锉。

# 函數(shù)棧幀

棧的主要功能是實現(xiàn)函數(shù)的調用。因此在介紹棧溢出原理之前膘螟,需要弄清函數(shù)調用時棾筛#空間發(fā)生了怎樣的變化。每次函數(shù)調用時荆残,系統(tǒng)會把函數(shù)的返回地址(函數(shù)調用指令后緊跟指令的地址)奴艾,一些關鍵的寄存器值保存在棧內,函數(shù)的實際參數(shù)和局部變量(包括數(shù)據内斯、結構體蕴潦、對象等)也會保存在棧內像啼。這些數(shù)據統(tǒng)稱為函數(shù)調用的棧幀,而且是每次函數(shù)調用都會有個獨立的棧幀潭苞,這也為遞歸函數(shù)的實現(xiàn)提供了可能忽冻。

圖2 函數(shù)棧幀

如圖所示,我們定義了一個簡單的函數(shù)function此疹,它接受一個整形參數(shù)僧诚,做一次乘法操作并返回。當調用function(0)時蝗碎,arg參數(shù)記錄了值0入棧湖笨,并將call function指令下一條指令的地址0x00bd16f0保存到棧內,然后跳轉到function函數(shù)內部執(zhí)行蹦骑。每個函數(shù)定義都會有函數(shù)頭和函數(shù)尾代碼慈省,如圖綠框表示。因為函數(shù)內需要用ebp保存函數(shù)棧幀基址眠菇,因此先保存ebp原來的值到棧內边败,然后將棧指針esp內容保存到ebp。函數(shù)返回前需要做相反的操作——將esp指針恢復琼锋,并彈出ebp放闺。這樣,函數(shù)內正常情況下無論怎樣使用棧缕坎,都不會使棧失去平衡怖侦。

sub esp,44h指令為局部變量開辟了棧空間谜叹,比如ret變量的位置匾寝。理論上,function只需要再開辟4字節(jié)空間保存ret即可荷腊,但是編譯器開辟了更多的空間(這個問題很詭異艳悔,你覺得呢?)女仰。函數(shù)調用結束返回后猜年,函數(shù)棧幀恢復到保存參數(shù)0時的狀態(tài),為了保持棧幀平衡疾忍,需要恢復esp的內容乔外,使用add esp,4將壓入的參數(shù)彈出。

之所以會有緩沖區(qū)溢出的可能一罩,主要是因為椦钣祝空間內保存了函數(shù)的返回地址。該地址保存了函數(shù)調用結束后后續(xù)執(zhí)行的指令的位置,對于計算機安全來說差购,該信息是很敏感的四瘫。如果有人惡意修改了這個返回地址,并使該返回地址指向了一個新的代碼位置欲逃,程序便能從其它位置繼續(xù)執(zhí)行找蜜。

# 三、棧溢出基本原理

上邊給出的代碼是無法進行溢出操作的暖夭,因為用戶沒有“插足”的機會锹杈。但是實際上很多程序都會接受用戶的外界輸入,尤其是當函數(shù)內的一個數(shù)組緩沖區(qū)接受用戶輸入的時候迈着,一旦程序代碼未對輸入的長度進行合法性檢查的話竭望,緩沖區(qū)溢出便有可能觸發(fā)!比如下邊的一個簡單的函數(shù)裕菠。

 void fun(unsigned char *data)
{
    unsigned char buffer[BUF_LEN];
    strcpy((char*)buffer,(char*)data);//溢出點  
}

這個函數(shù)沒有做什么有“意義”的事情(這里主要是為了簡化問題)咬清,但是它是一個典型的棧溢出代碼。在使用不安全的strcpy庫函數(shù)時奴潘,系統(tǒng)會盲目地將data的全部數(shù)據拷貝到buffer指向的內存區(qū)域旧烧。buffer的長度是有限的,一旦data的數(shù)據長度超過BUF_LEN画髓,便會產生緩沖區(qū)溢出掘剪。

圖3 緩沖區(qū)溢出

由于棧是低地址方向增長的,因此局部數(shù)組buffer的指針在緩沖區(qū)的下方奈虾。當把data的數(shù)據拷貝到buffer內時夺谁,超過緩沖區(qū)區(qū)域的高地址部分數(shù)據會“淹沒”原本的其他棧幀數(shù)據,根據淹沒數(shù)據的內容不同肉微,可能會有產生以下情況:

  1. 淹沒了其他的局部變量匾鸥。如果被淹沒的局部變量是條件變量,那么可能會改變函數(shù)原本的執(zhí)行流程碉纳。這種方式可以用于破解簡單的軟件驗證勿负。
  2. 淹沒了ebp的值。修改了函數(shù)執(zhí)行結束后要恢復的棧指針劳曹,將會導致棧幀失去平衡奴愉。
  3. 淹沒了返回地址。這是棧溢出原理的核心所在铁孵,通過淹沒的方式修改函數(shù)的返回地址锭硼,使程序代碼執(zhí)行“意外”的流程!
  4. 淹沒參數(shù)變量库菲。修改函數(shù)的參數(shù)變量也可能改變當前函數(shù)的執(zhí)行結果和流程。
  5. 淹沒上級函數(shù)的棧幀志膀,情況與上述4點類似熙宇,只不過影響的是上級函數(shù)的執(zhí)行鳖擒。當然這里的前提是保證函數(shù)能正常返回,即函數(shù)地址不能被隨意修改(這可能很麻煩L讨埂)蒋荚。

如果在data本身的數(shù)據內就保存了一系列的指令的二進制代碼,一旦棧溢出修改了函數(shù)的返回地址馆蠕,并將該地址指向這段二進制代碼的其實位置期升,那么就完成了基本的溢出攻擊行為。
圖4 基本棧溢出攻擊

通過計算返回地址內存區(qū)域相對于buffer的偏移互躬,并在對應位置構造新的地址指向buffer內部二進制代碼的其實位置播赁,便能執(zhí)行用戶的自定義代碼!這段既是代碼又是數(shù)據的二進制數(shù)據被稱為shellcode吼渡,因為攻擊者希望通過這段代碼打開系統(tǒng)的shell容为,以執(zhí)行任意的操作系統(tǒng)命令——比如下載病毒,安裝木馬寺酪,開放端口坎背,格式化磁盤等惡意操作。

# 棧溢出攻擊

上述過程雖然理論上能完成棧溢出攻擊行為寄雀,但是實際上很難實現(xiàn)得滤。操作系統(tǒng)每次加載可執(zhí)行文件到進程空間的位置都是無法預測的,因此棧的位置實際是不固定的盒犹,通過硬編碼覆蓋新返回地址的方式并不可靠懂更。為了能準確定位shellcode的地址,需要借助一些額外的操作阿趁,其中最經典的是借助跳板的棧溢出方式膜蛔。

根據前邊所述,函數(shù)執(zhí)行后脖阵,棧指針esp會恢復到壓入參數(shù)時的狀態(tài)皂股,在圖4中即data參數(shù)的地址。如果我們在函數(shù)的返回地址填入一個地址命黔,該地址指向的內存保存了一條特殊的指令jmp esp——跳板呜呐。那么函數(shù)返回后,會執(zhí)行該指令并跳轉到esp所在的位置——即data的位置悍募。我們可以將緩沖區(qū)再多溢出一部分蘑辑,淹沒data這樣的函數(shù)參數(shù),并在這里放上我們想要執(zhí)行的代碼坠宴!這樣洋魂,不管程序被加載到哪個位置,最終都會回來執(zhí)行棧內的代碼。

圖5 借助跳板的棧溢出攻擊

借助于跳板的確可以很好的解決棧幀移位(棧加載地址不固定)的問題副砍,但是跳板指令從哪找呢衔肢?“幸運”的是,在Windows操作系統(tǒng)加載的大量dll中豁翎,包含了許多這樣的指令角骤,比如kernel32.dll,ntdll.dll心剥,這兩個動態(tài)鏈接庫是Windows程序默認加載的邦尊。如果是圖形化界面的Windows程序還會加載user32.dll,它也包含了大量的跳板指令优烧!而且更“神奇”的是Windows操作系統(tǒng)加載dll時候一般都是固定地址蝉揍,因此這些dll內的跳板指令的地址一般都是固定的。我們可以離線搜索出跳板執(zhí)行在dll內的偏移匙隔,并加上dll的加載地址疑苫,便得到一個適用的跳板指令地址!

//查詢dll內第一個jmp esp指令的位置  int findJmp(char*dll_name)
{
    char* handle=(char*)LoadLibraryA(dll_name);//獲取dll加載地址  
    for(int pos=0;;pos++)//遍歷dll代碼空間      
    {
        if(handle[pos]==(char)0xff&&handle[pos+1]==(char)0xe4)//尋找0xffe4 = jmp  esp          
        {
            return (int)(handle+pos);
        }
    }
}

這里簡化了搜索算法纷责,輸出第一個跳板指令的地址捍掺,讀者可以選取其他更合適位置。LoadLibraryA庫函數(shù)返回值就是dll的加載地址再膳,然后加上搜索到的跳板指令偏移pos便是最終地址挺勿。jmp esp指令的二進制表示為0xffe4,因此搜索算法就是搜索dll內這樣的字節(jié)數(shù)據即可喂柒。

雖然如此不瓶,上述的攻擊方式還不夠好。因為在esp后繼續(xù)追加shellcode代碼會將上級函數(shù)的棧幀淹沒灾杰,這樣做并沒有什么好處蚊丐,甚至可能會帶來運行時問題。既然被溢出的函數(shù)棧幀內提供了緩沖區(qū)艳吠,我們還是把核心的shellcode放在緩沖區(qū)內麦备,而在esp之后放上跳轉指令轉移到原本的緩沖區(qū)位置。由于這樣做使代碼的位置在esp指針之前昭娩,如果shellcode中使用了push指令便會讓esp指令與shellcode代碼越來越近凛篙,甚至淹沒自身的代碼。這顯然不是我們想要的結果栏渺,因此我們可以強制抬高esp指針呛梆,使它在shellcode之前(低地址位置),這樣就能在shellcode內正常使用push指令了磕诊。

圖6 調整shellcode與棧指針

調整代碼的內容很簡單:

add esp,-X
jmp esp
  • 第一條指令抬高了棧指針到shellcode之前填物。X代表shellcode起始地址與esp的偏移纹腌。如果shellcode從緩沖區(qū)起始位置開始,那么就是buffer的地址偏移滞磺。這里不使用sub esp,X指令主要是避免X的高位字節(jié)為0的問題壶笼,很多情況下緩沖區(qū)溢出是針對字符串緩沖區(qū)的,如果出現(xiàn)字節(jié)0會導致緩沖區(qū)截斷雁刷,從而導致溢出失敗。
  • 第二條指令就是跳轉到shellcode的起始位置繼續(xù)執(zhí)行保礼。(又是jmp espE胬)

通過上述方式便能獲得一個較為穩(wěn)定的棧溢出攻擊。

# shellcode構造

shellcode實質是指溢出后執(zhí)行的能開啟系統(tǒng)shell的代碼炮障。但是在緩沖區(qū)溢出攻擊時目派,也可以將整個觸發(fā)緩沖區(qū)溢出攻擊過程的代碼統(tǒng)稱為shellcode,按照這種定義可以把shellcode分為四部分:

  1. 核心shellcode代碼胁赢,包含了攻擊者要執(zhí)行的所有代碼企蹭。
  2. 溢出地址,是觸發(fā)shellcode的關鍵所在智末。
  3. 填充物谅摄,填充未使用的緩沖區(qū),用于控制溢出地址的位置系馆,一般使用nop指令填充——0x90表示送漠。
  4. 結束符號0,對于符號串shellcode需要用0結尾由蘑,避免溢出時字符串異常闽寡。

前邊一直在圍繞溢出地址討論,并解決了shellcode組織的問題尼酿,而最核心的代碼如何構造并未提及——即攻擊成功后做的事情爷狈。其實一旦緩沖區(qū)溢出攻擊成功后,如果被攻擊的程序有系統(tǒng)的root權限——比如系統(tǒng)服務程序裳擎,那么攻擊者基本上可以為所欲為了涎永!但是我們需要清楚的是,核心shellcode必須是二進制代碼形式句惯。而且shellcode執(zhí)行時是在遠程的計算機上土辩,因此shellcode是否能通用是一個很復雜的問題。我們可以用一段簡單的代碼實例來說明這個問題抢野。

緩沖區(qū)溢出成功后拷淘,一般大家都會希望開啟一個遠程的shell控制被攻擊的計算機。開啟shell最直接的方式便是調用C語言的庫函數(shù)system指孤,該函數(shù)可以執(zhí)行操作系統(tǒng)的命令启涯,就像我們在命令行下執(zhí)行命令那樣贬堵。假如我們執(zhí)行cmd命令——在遠程計算機上啟動一個命令提示終端(我們可能還不能和它交互,但是可以在這之前建立一個遠程管道等)结洼,這里僅作為實例測試黎做。

為了使system函數(shù)調用成功,我們需要將“cmd”字符串內容壓入椝扇蹋空間蒸殿,并將其地址壓入作為system函數(shù)的參數(shù),然后使用call指令調用system函數(shù)的地址鸣峭,完成函數(shù)的執(zhí)行宏所。但是這樣做還不夠,如果被溢出的程序沒有加載C語言庫的話摊溶,我們還需要調用Windows的API Loadlibrary加載C語言的庫msvcrt.dll爬骤,類似的我們也需要為字符串“msvcrt.dll”開辟棧空間莫换。

xor ebx,ebx ;//ebx=0  
push 0x3f3f6c6c ;//ll??  
push 0x642e7472 ;//rt.d  
push 0x6376736d ;//msvc  
mov [esp+10],ebx ;//'?'->'0'  
mov [esp+11],ebx ;//'?'->'0'  
mov eax,esp ;//"msvcrt.dll"地址  
push eax ;//"msvcrt.dll"  
mov eax,0x77b62864 ;//kernel32.dll:LoadLibraryA  call eax ;//LoadLibraryA("msvcrt.dll")  
add esp,16

push 0x3f646d63 ;//"cmd?"  
mov [esp+3],ebx ;//'?'->'\0'  
mov eax,esp;//"cmd"地址  
push eax ;//"cmd"  
mov eax,0x774ab16f ;//msvcrt.dll:system  
call eax ;//system("cmd")  
add esp,8

上述匯編代碼實質上是如下兩個函數(shù)調用語句:

Loadlibrary(“msvcrt.dll”);
system(“cmd”);

不過在構造這段匯編代碼時需要注意不能出現(xiàn)字節(jié)0霞玄,為了填充字符串的結束字符,我們使用已經初始化為0的ebx寄存器代替拉岁。另外坷剧,在對庫函數(shù)調用的時候需要提前計算出函數(shù)的地址,如Loadlibrary函數(shù)的0x77b62864喊暖。計算方式如下:

int findFunc(char*dll_name,char*func_name)
{
    HINSTANCE handle=LoadLibraryA(dll_name);//獲取dll加載地址  
    return (int)GetProcAddress(handle,func_name);
}

這個函數(shù)地址是在本地計算的听隐,如果被攻擊計算機的操作系統(tǒng)版本差別較大的話,這個地址可能是錯誤的哄啄。不過在《0day安全:軟件漏洞分析技術》中雅任,作者提供了一個更好的方式,感興趣的讀者可以參考該書提供的代碼咨跌。因此構造一個通用的shellcode并非十分容易沪么,如果想讓攻擊變得有效的話。

# 匯編語言自動轉換

寫出shellcode后(無論是簡單的還是通用的)锌半,我們還需要將這段匯編代碼轉換為機器代碼禽车。如果讀者對x86匯編十分熟悉的話,選擇手工敲出二進制代碼的話也未嘗不可刊殉。不過我們都希望能讓計算機幫助做完這些事殉摔,既然開發(fā)環(huán)境提供了編譯器,用它們幫忙何樂而不為呢记焊?既不用OllyDbg工具逸月,也不適用其他的第三方工具,我們寫一個簡單的函數(shù)來完成這個工作遍膜。

//將內嵌匯編的二進制指令dump到文件,style指定輸出數(shù)組格式還是二進制形式碗硬,返回代碼長度  int dumpCode(unsigned char*buffer)
{
    goto END ;//略過匯編代碼  BEGIN:
    __asm
    {
        //在這里定義任意的合法匯編代碼          
    }
END:
    //確定代碼范圍      
    UINT begin,end;
    __asm
    {
        mov eax,BEGIN ;
        mov begin,eax ;
        mov eax,END ;
        mov end,eax ;
    }
    //輸出      
    int len=end-begin;
    memcpy(buffer,(void*)begin,len);
        //四字節(jié)對齊      
    int fill=(len-len%4)%4;
    while(fill--)buffer[len+fill]=0x90;
    //返回長度      
    return len+fill;
}

因為C++是支持嵌入式匯編代碼的瓤湘,因此在函數(shù)內的匯編代碼都會被整成編譯為二進制代碼。實現(xiàn)二進制轉換的基本思想是讀取編譯器最終生成的二進制代碼段數(shù)據恩尾,將數(shù)據導出到指定的緩沖區(qū)內弛说。為了鎖定嵌入式匯編代碼的位置和長度,我們定義了兩個標簽BEGIN和END翰意。這兩個標簽在匯編語言級別會被解析為實際的線性地址木人,但是在高級語言級是無法直接使用這兩個標簽值的,只能使用goto語句跳轉使用它們冀偶。但是我們可以順水推舟虎囚,使用兩個局部變量在匯編級記錄這兩個標簽的值!

//確定代碼范圍  
UINT begin,end;
__asm
{
    mov eax,BEGIN ;
    mov begin,eax ;
    mov eax,END ;
    mov end,eax ;
}

這樣就可以得到嵌入式匯編的代碼范圍了蔫磨,使用memcpy操作將代碼數(shù)據拷貝到目標緩沖區(qū)即可(后邊還用nop指令將代碼按照四字節(jié)對齊)。不過我們還需要注意一個問題圃伶,嵌入式匯編在函數(shù)執(zhí)行時也會執(zhí)行堤如,這顯然不可以,我們只是把它當作數(shù)據而已(是數(shù)據窒朋?還是代碼搀罢?),因此在函數(shù)開始的地方我們使用goto語句直接跳轉到嵌入式會變語句的結尾——END標簽侥猩!

# 七榔至、攻擊測試

按照上述內容,相信不難構造出一個簡單的shellcode并攻擊之前提供的漏洞函數(shù)欺劳。但是如果使用VS2010測試的話可能會碰到很多問題唧取。經過大量的調試和資料查詢,我們需要設置三處VS的項目屬性划提。

  1. 配置->配置屬性->C/C++->基本運行時檢查=默認值枫弟,避免被檢測棧幀失衡。
  2. 配置->配置屬性->C/C++->緩沖區(qū)安全檢查=否鹏往,避免識別緩沖區(qū)溢出漏洞淡诗。
  3. 配置->配置屬性->鏈接器->高級->數(shù)據執(zhí)行保護(DEP)=否,避免堆棧段不可執(zhí)行伊履。

從這三處設置看來韩容,目前的編譯器已經針對緩沖區(qū)溢出攻擊做了大量的保護工作(顯然這會降低程序的執(zhí)行性能,因此允許用戶配置)唐瀑,使得傳統(tǒng)的緩沖區(qū)溢出攻擊變得沒那么“猖狂”了群凶,但是在計算機安全領域,“道高一尺哄辣,魔高一丈”座掘,總有人會找到更隱蔽的攻擊方式讓編譯器開發(fā)者措手不及递惋。本文除了分析緩沖區(qū)溢出攻擊的原理之外,更希望讀者能從中感受到代碼安全的重要性溢陪,并結合編譯器提供的安全功能讓自己的代碼更加安全高效萍虽。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市形真,隨后出現(xiàn)的幾起案子杉编,更是在濱河造成了極大的恐慌,老刑警劉巖咆霜,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邓馒,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛾坯,警方通過查閱死者的電腦和手機光酣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來脉课,“玉大人救军,你說我怎么就攤上這事√攘悖” “怎么了唱遭?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長呈驶。 經常有香客問我拷泽,道長,這世上最難降的妖魔是什么袖瞻? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任司致,我火速辦了婚禮,結果婚禮上聋迎,老公的妹妹穿的比我還像新娘蚌吸。我一直安慰自己,他們只是感情好砌庄,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布羹唠。 她就那樣靜靜地躺著,像睡著了一般娄昆。 火紅的嫁衣襯著肌膚如雪佩微。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天萌焰,我揣著相機與錄音哺眯,去河邊找鬼。 笑死扒俯,一個胖子當著我的面吹牛奶卓,可吹牛的內容都是我干的一疯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼夺姑,長吁一口氣:“原來是場噩夢啊……” “哼墩邀!你這毒婦竟也來了?” 一聲冷哼從身側響起盏浙,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤眉睹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后废膘,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竹海,經...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年丐黄,在試婚紗的時候發(fā)現(xiàn)自己被綠了斋配。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡灌闺,死狀恐怖艰争,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情菩鲜,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布惦积,位于F島的核電站接校,受9級特大地震影響,放射性物質發(fā)生泄漏狮崩。R本人自食惡果不足惜蛛勉,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睦柴。 院中可真熱鬧诽凌,春花似錦、人聲如沸坦敌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狱窘。三九已至杜顺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蘸炸,已是汗流浹背躬络。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搭儒,地道東北人穷当。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓提茁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親馁菜。 傳聞我的和親對象是個殘疾皇子茴扁,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內容