安全編程:防止緩沖區(qū)溢出_第1頁
安全編程:防止緩沖區(qū)溢出_第2頁
安全編程:防止緩沖區(qū)溢出_第3頁
安全編程:防止緩沖區(qū)溢出_第4頁
安全編程:防止緩沖區(qū)溢出_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、安全編程:防止緩沖區(qū)溢出防止如今最常見的程序缺陷本文討論Linux/UNIX系統(tǒng)中最常見的缺陷:緩沖區(qū)溢出。本文首先解釋什么是 緩沖區(qū)溢出,以及它們?yōu)楹稳绱顺R姾腿绱宋kU(xiǎn)。然后討論廣泛用于解決緩沖區(qū) 溢出的新Linux和UNIX方法一一以及為什么這些方法還不足夠。隨后將展 示C/C+程序中防止緩沖區(qū)溢出的各種方法,同時(shí)包括靜態(tài)調(diào)整大小的方法(比 如標(biāo)準(zhǔn)的C庫和OpenBSD/strlcpy解決方案)和動(dòng)態(tài)調(diào)整大小的解決方案,以 及一些將為您提供幫助的工具。最后,本文以一些關(guān)于緩沖區(qū)溢出缺陷的未來發(fā) 展形勢(shì)的預(yù)測(cè)來結(jié)束全文的討論。標(biāo)記本文!發(fā)布日期:2004年3月01日級(jí)別:初級(jí)訪問情況3782

2、次瀏覽建議:0 (添加評(píng)論)平均分(共4個(gè)評(píng)分)新1988年11月,許多組織不得不因?yàn)椤癕orris蠕蟲”而切斷Internet連 接,“Morris蠕蟲”是23歲的程序員Robert Tappan Morris編寫的用于攻 擊VAX和Sun機(jī)器的程序。據(jù)有關(guān)方面估計(jì),這個(gè)程序大約使得整個(gè) Internet的10%崩潰。2001年7月,另一個(gè)名為“Code Red”的蠕蟲病毒 最終導(dǎo)致了全球運(yùn)行微軟的IIS Web Server的300,000多臺(tái)計(jì)算機(jī)受到攻擊。 2003 年 1 月,“Slammer”(也稱為 “Sapphire”)蠕蟲利用 Microsoft SQL Server 200

3、0中的一個(gè)缺陷,使得南韓和日本的部分Internet崩潰,中斷了芬蘭的電 話服務(wù),并且使得美國航空訂票系統(tǒng)、信用卡網(wǎng)絡(luò)和自動(dòng)出納機(jī)運(yùn)行緩慢。所有 這些攻擊以及其他許多攻擊,都利用了一個(gè)稱做為緩沖區(qū)溢出的程序缺陷。1999年Bugtraq(一個(gè)討論安全缺陷的郵件列表)進(jìn)行的一次非正式調(diào)查發(fā)現(xiàn), 三分之二的參與者認(rèn)為第一號(hào)的缺陷就是緩沖區(qū)溢出(要了解相關(guān)背景,請(qǐng)參閱 本文后面 參考資料部分列出的“Buffer Overflows: Attacks and Defenses for the Vulnerability of the Decade”一文)。從 1997 年到 2002 年 3 月,CE

4、RT/CC 發(fā)出的半數(shù)安全警報(bào)都基于緩沖區(qū)缺陷。如果希望自己的程序是安全的,您需要知道什么是緩沖區(qū)溢出,如何防止它們, 可以采用哪些最新的自動(dòng)化工具來防止它們(以及為什么這些工具還不足夠), 還有如何在您自己的程序中防止它們。什么是緩沖區(qū)溢出? 緩沖區(qū)以前可能被定義為“包含相同數(shù)據(jù)類型的實(shí)例的一個(gè)連續(xù)計(jì)算機(jī)內(nèi)存塊”。 在C和C+中,緩沖區(qū)通常是使用數(shù)組和諸如malloc()和new這樣的內(nèi)存 分配例程來實(shí)現(xiàn)的。極其常見的緩沖區(qū)種類是簡(jiǎn)單的字符數(shù)組。溢出是指數(shù)據(jù) 被添加到分配給該緩沖區(qū)的內(nèi)存塊之外。如果攻擊者能夠?qū)е戮彌_區(qū)溢出,那么它就能控制程序中的其他值。雖然存在許 多利用緩沖區(qū)溢出的方法,不

5、過最常見的方法還是“stack-smashing”攻擊。Elias Levy (又名為 Aleph One)的一篇經(jīng)典文章“Smashing the Stack for Fun and Profit”解釋了 stack-smashing 攻擊,Elias Levy 是 Bugtraq 郵件列表(請(qǐng) 參閱參考資料以獲得相關(guān)鏈接)的前任主持人。為了理解stack-smashing攻擊(或其他任何緩沖區(qū)攻擊)是如何進(jìn)行的,您需 要了解一些關(guān)于計(jì)算機(jī)在機(jī)器語言級(jí)實(shí)際如何工作的知識(shí)。在類UNIX系統(tǒng)上, 每個(gè)進(jìn)程都可以劃分為三個(gè)主要區(qū)域:文本、數(shù)據(jù)和堆棧。文本區(qū)域包括代碼 和只讀數(shù)據(jù),通常不能對(duì)它執(zhí)行寫

6、入操作。數(shù)據(jù)區(qū)域同時(shí)包括靜態(tài)分配的內(nèi)存(比如全局和靜態(tài)數(shù)據(jù))和動(dòng)態(tài)分配的內(nèi)存(通常稱為堆)。堆棧區(qū)域用于允 許函數(shù)/方法調(diào)用;它用于記錄函數(shù)完成之后的返回位置,存儲(chǔ)函數(shù)中使用的本 地變量,向函數(shù)傳遞參數(shù),以及從函數(shù)返回值。每當(dāng)調(diào)用一個(gè)函數(shù),就會(huì)使用一 個(gè)新的堆棧幀來支持該調(diào)用。了解這些之后,讓我們來考察一個(gè)簡(jiǎn)單的程序。清單1. 一個(gè)簡(jiǎn)單的程序void function1(int a, int b, int c) (char buffer15;gets(buffer1); /* DONT DO THIS */void main() (function(1,2,3);假設(shè)使用gcc來編譯清單1中的

7、簡(jiǎn)單程序,在X86上的Linux中運(yùn)行,并且 緊跟在對(duì)gets()的調(diào)用之后中止。此時(shí)的內(nèi)存內(nèi)容看起來像什么樣子呢?答案 是它看起來類似圖1,其中展示了從左邊的低位地址到右邊的高位地址排序的內(nèi) 存布局。圖1.堆棧視圖內(nèi)存的底部內(nèi)存的頂部-增長-.堆棧的頂部堆棧的底部許多計(jì)算機(jī)處理器,包括所有x86處理器,都支持從高位地址向低位地址“倒” 增長堆棧。因此,每當(dāng)一個(gè)函數(shù)調(diào)用另一個(gè)函數(shù),更多的數(shù)據(jù)將被添加到左邊(低 位地址),直至系統(tǒng)的堆??臻g耗盡。在這個(gè)例子中,當(dāng)main()調(diào)用function1() 時(shí),它將c的值壓入堆棧,然后壓入b的值,最后壓入a的值。之后它壓入 return (ret)值,

8、這個(gè)值在function1()完成時(shí)告訴function1()返回到 main()中的何處。它還把所謂的已保存的幀指針(saved frame pointer,sfp)” 記錄到堆棧上;這并不是必須保存的內(nèi)容,此處我們不需要理解它。在任何情況 下,function1()在啟動(dòng)以后,它會(huì)為buffer1()預(yù)留空間,這在圖1中顯 示為具有一個(gè)低地址位置。現(xiàn)在假設(shè)攻擊者發(fā)送了超過buffer1()所能處理的數(shù)據(jù)。接下來會(huì)發(fā)生什么情 況呢?當(dāng)然,C和C+程序員不會(huì)自動(dòng)檢查這個(gè)問題,因此除非程序員明確地 阻止它,否則下一個(gè)值將進(jìn)入內(nèi)存中的“下一個(gè)”位置。那意味著攻擊者能夠改 寫sfp (即已保存的幀指

9、針),然后改寫ret (返回地址)。之后,當(dāng)function1() 完成時(shí),它將“返回”一一不過不是返回到main(),而是返回到攻擊者想要 運(yùn)行的任何代碼。通常攻擊者會(huì)使用它想要運(yùn)行的惡意代碼來使緩沖區(qū)溢出,然后攻擊者會(huì)更改返 回值以指向它們已發(fā)送的惡意代碼。這意味著攻擊者本質(zhì)上能夠在一個(gè)操作中完 成整個(gè)攻擊! Aleph On的文章(請(qǐng)參閱參考資料)詳細(xì)介紹了這樣的攻擊代碼 是如何創(chuàng)建的。例如,將一個(gè)ASCII 0字符壓入緩沖區(qū)通常是很困難的,而該 文介紹了攻擊者一般如何能夠解決這個(gè)問題。除了 smashing-stack和更改返回地址外,還存在利用緩沖區(qū)溢出缺陷的其他途 徑。與改寫返回地

10、址不同,攻擊者可以smashing-stack (使堆棧上的緩沖區(qū)溢 出),然后改寫局部變量以利用緩沖區(qū)溢出缺陷。緩沖區(qū)根本就不必在堆棧 上一一它可以是堆中動(dòng)態(tài)分配的內(nèi)存(也稱為“malloc”或“new”區(qū)域), 或者在某些靜態(tài)分配的內(nèi)存中(比如“global”或“static”內(nèi)存)?;旧希?如果攻擊者能夠溢出緩沖區(qū)的邊界,麻煩或許就會(huì)找上你了。然而,最危險(xiǎn)的 緩沖區(qū)溢出攻擊就是stack-smashing攻擊,因?yàn)槿绻绦驅(qū)粽吆艽嗳酰?擊者獲得整個(gè)機(jī)器的控制權(quán)就特別容易。為什么緩沖區(qū)溢出如此常見?在幾乎所有計(jì)算機(jī)語言中,不管是新的語言還是舊的語言,使緩沖區(qū)溢出的任何 嘗試通常都會(huì)

11、被該語言本身自動(dòng)檢測(cè)并阻止(比如通過引發(fā)一個(gè)異?;蚋鶕?jù)需要 給緩沖區(qū)添加更多空間)。但是有兩種語言不是這樣:C和C+語言。C和C+ 語言通常只是讓額外的數(shù)據(jù)亂寫到其余內(nèi)存的任何位置,而這種情況可能被利用 從而導(dǎo)致恐怖的結(jié)果。更糟糕的是,用C和C+編寫正確的代碼來始終如一地 處理緩沖區(qū)溢出則更為困難;很容易就會(huì)意外地導(dǎo)致緩沖區(qū)溢出。除了 C和C+ 使用得非常廣泛外,上述這些可能都是不相關(guān)的事實(shí);例如,Red Hat Linux 7.1 中86%的代碼行都是用C或C+編寫的。因此,大量的代碼對(duì)這個(gè)問題都是 脆弱的,因?yàn)閷?shí)現(xiàn)語言無法保護(hù)代碼避免這個(gè)問題。在C和C+語言本身中,這個(gè)問題是不容易解決的。

12、該問題基于C語言的根 本設(shè)計(jì)決定(特別是C語言中指針和數(shù)組的處理方式)。由于C+是最兼容的 C語言超集,它也具有相同的問題。存在一些能防止這個(gè)問題的C/C+兼容版 本,但是它們存在極其嚴(yán)重的性能問題。而且一旦改變C語言來防止這個(gè)問題, 它就不再是C語言了。許多語言(比如Java和C#)在語法上類似C,但它們 實(shí)際上是不同的語言,將現(xiàn)有C或C+程序改為使用那些語言是一項(xiàng)艱巨的任 務(wù)。然而,其他語言的用戶也不應(yīng)該沾沾自喜。有些語言存在允許緩沖區(qū)溢出發(fā)生的 “轉(zhuǎn)義”子句。Ada 一般會(huì)檢測(cè)和防止緩沖區(qū)溢出(即針對(duì)這樣的嘗 試引發(fā)一個(gè)異常),但是不同的程序可能會(huì)禁用這個(gè)特性。C# 一般會(huì)檢測(cè)和防 止緩

13、沖區(qū)溢出,但是它允許程序員將某些例程定義為“不安全的”,而這樣的代 碼 可能會(huì)導(dǎo)致緩沖區(qū)溢出。因此如果您使用那些轉(zhuǎn)義機(jī)制,就需要使用C/C+ 程序所必須使用的相同種類的保護(hù)機(jī)制。許多語言都是用C語言來實(shí)現(xiàn)的(全 少部分是用C語言來實(shí)現(xiàn)的),并且用任何語言編寫的所有程序本質(zhì)上都依賴 用C或C+編寫的庫。因此,所有程序都會(huì)繼承那些問題,所以了解這些問題 是很重要的?;仨撌讓?dǎo)致緩沖區(qū)溢出的常見C和C+錯(cuò)誤從根本上講,在程序?qū)?shù)據(jù)讀入或復(fù)制到緩沖區(qū)中的任何時(shí)候,它需要在復(fù)制 之 前檢查是否有足夠的空間。能夠容易看出來的異常就不可能會(huì)發(fā)生一一但是程 序通常會(huì)隨時(shí)間而變更,從而使得不可能成為可能。遺憾的是

14、,C和C+附帶的大量危險(xiǎn)函數(shù)(或普遍使用的庫)甚至連這點(diǎn)(指 檢查空間)也無法做到。程序?qū)@些函數(shù)的任何使用都是一個(gè)警告信號(hào),因?yàn)槌?非慎重地使用它們,否則它們就會(huì)成為程序缺陷。您不需要記住這些函數(shù)的列表; 我的真正目的是說明這個(gè)問題是多么普遍。這些函數(shù)包括strcpy(3)、 strcat(3)、sprintf(3)(及其同類 vsprintf(3)和 gets(3)。scanf()函 數(shù)集(scanf(3)、fscanf(3)、sscanf(3)、vscanf(3)、vsscanf(3) 和 vfscanf(3)可能會(huì)導(dǎo)致問題,因?yàn)槭褂靡粋€(gè)沒有定義最大長度的格式是很容 易的(當(dāng)讀取不受信任

15、的輸入時(shí),使用格式“s”總是一個(gè)錯(cuò)誤)。其他危險(xiǎn)的函數(shù)包括 realpath(3)、getopt(3)、getpass(3)、streadd(3)、 strecpy(3)和strtrns(3)。從理論上講,snprintf()應(yīng)該是相對(duì)安全 的一一在現(xiàn)代GNU/Linux系統(tǒng)中的確是這樣。但是非常老的UNIX和Linux 系統(tǒng)沒有實(shí)現(xiàn)snprintf()所應(yīng)該實(shí)現(xiàn)的保護(hù)機(jī)制。Microsoft的庫中還有在相應(yīng)平臺(tái)上導(dǎo)致同類問題的其他函數(shù)(這些函數(shù)包括 wcscpy()、_tcscpy()、_mbscpy()、wcscat()、_tcscat()、_mbscat() 和 CopyMemory()

16、。注意,如果使用 Microsoft 的 MultiByteToWideChar()函 數(shù),還存在一個(gè)常見的危險(xiǎn)錯(cuò)誤一一該函數(shù)需要一個(gè)最大尺寸作為字符數(shù)目, 但是程序員經(jīng)常將該尺寸以字節(jié)計(jì)(更普遍的需要),結(jié)果導(dǎo)致緩沖區(qū)溢出缺陷。另一個(gè)問題是C和C+對(duì)整數(shù)具有非常弱的類型檢查,一般不會(huì)檢測(cè)操作這些 整數(shù)的問題。由于它們要求程序員手工做所有的問題檢測(cè)工作,因此以某種可被 利用的方式不正確地操作那些整數(shù)是很容易的。特別是,當(dāng)您需要跟蹤緩沖區(qū)長 度或讀取某個(gè)內(nèi)容的長度時(shí),通常就是這種情況。但是如果使用一個(gè)有符號(hào)的值 來存儲(chǔ)這個(gè)長度值會(huì)發(fā)生什么情況呢一一攻擊者會(huì)使它“成為負(fù)值”,然后把 該數(shù)據(jù)解釋為一

17、個(gè)實(shí)際上很大的正值嗎?當(dāng)數(shù)字值在不同的尺寸之間轉(zhuǎn)換時(shí),攻 擊者會(huì)利用這個(gè)操作嗎?數(shù)值溢出可被利用嗎?有時(shí)處理整數(shù)的方式會(huì)導(dǎo)致程 序缺陷。回頁首防止緩沖區(qū)溢出的新技術(shù)當(dāng)然,要讓程序員不犯常見錯(cuò)誤是很難的,而讓程序(以及程序員)改為使用 另一種語言通常更為困難。那么為何不讓底層系統(tǒng)自動(dòng)保護(hù)程序避免這些問題 呢?最起碼,避免stack-smashing攻擊是一件好事,因?yàn)閟tack-smashing攻 擊是特別容易做到的。一般來說,更改底層系統(tǒng)以避免常見的安全問題是一個(gè)極好的想法,我們?cè)诒疚?后面也會(huì)遇到這個(gè)主題。事實(shí)證明存在許多可用的防御措施,而一些最受歡迎的 措施可分組為以下類別:基于探測(cè)方法(

18、canary)的防御。這包括StackGuard(由Immunix所使用)、ProPolice(由OpenBSD所使用)和Microsoft的/GS選項(xiàng)。非執(zhí)行的堆棧防御。這包括Solar Designer的non-exec補(bǔ)?。ㄓ蒓penWall所使用) 和 exec shield (由 Red Hat/Fedora 所使用)。其他方法。這包括libsafe (由Mandrake所使用)和堆棧分割方法。遺憾的是,迄今所見的所有方法都具有弱點(diǎn),因此它們不是萬能藥,但是它們會(huì) 提供一些幫助。基于探測(cè)方法的防御研究人員Crispen Cowan創(chuàng)建了一個(gè)稱為StackGuard的有趣方法。Stac

19、kguard修改C編譯器(gcc),以便將一個(gè)“探測(cè)”值插入到返回地址的 前面?!疤綔y(cè)儀”就像煤礦中的探測(cè)儀:它在某個(gè)地方出故障時(shí)發(fā)出警告。在任 何函數(shù)返回之前,它執(zhí)行檢查以確保探測(cè)值沒有改變。如果攻擊者改寫返回地址 (作為stack-smashing攻擊的一部分),探測(cè)儀的值或許就會(huì)改變,系統(tǒng)內(nèi)就 會(huì)相應(yīng)地中止。這是一種有用的方法,不過要注意這種方法無法防止緩沖區(qū)溢出 改寫其他值(攻擊者仍然能夠利用這些值來攻擊系統(tǒng))。人們也曾擴(kuò)展這種方法 來保護(hù)其他值(比如堆上的值)。Stackguard (以及其他防御措施)由Immunix 所使用。IBM 的 stack-smashing 保護(hù)程序(ss

20、p,起初名為 ProPolice)是 StackGuard 的方法的一種變化形式。像StackGuard 一樣,ssp使用一個(gè)修改過的編譯器在 函數(shù)調(diào)用中插入一個(gè)探測(cè)儀以檢測(cè)堆棧溢出。然而,它給這種基本的思路添加了 一些有趣的變化。它對(duì)存儲(chǔ)局部變量的位置進(jìn)行重新排序,并復(fù)制函數(shù)參數(shù)中 的指針,以便它們也在任何數(shù)組之前。這樣增強(qiáng)了 ssp的保護(hù)能力;它意味著 緩沖區(qū)溢出不會(huì)修改指針值(否則能夠控制指針的攻擊者就能使用指針來控制程 序保存數(shù)據(jù)的位置)。默認(rèn)情況下,它不會(huì)檢測(cè)所有函數(shù),而只是檢測(cè)確實(shí)需要 保護(hù)的函數(shù)(主要是使用字符數(shù)組的函數(shù))。從理論上講,這樣會(huì)稍微削弱保護(hù) 能力,但是這種默認(rèn)行為改

21、進(jìn)了性能,同時(shí)仍然能夠防止大多數(shù)問題??紤]到實(shí) 用的因素,它們以獨(dú)立于體系結(jié)構(gòu)的方式使用gcc來實(shí)現(xiàn)它們的方法,從而使 其更易于運(yùn)用。從2003年5月的發(fā)布版本開始,廣受贊譽(yù)的OpenBSD(它重 點(diǎn)關(guān)注安全性)在他們的整個(gè)發(fā)行套件中使用了 ssp (也稱為ProPolice)。Microsoft基于StackGuard的成果,添加了一個(gè)編譯器標(biāo)記(/GS)來實(shí)現(xiàn)其C 編譯器中的探測(cè)儀。非執(zhí)行的堆棧防御另一種方法首先使得在堆棧上執(zhí)行代碼變得不可能。遺憾的是,x86處理器(最 常見的處理器)的內(nèi)存保護(hù)機(jī)制無法容易地支持這點(diǎn);通常,如果一個(gè)內(nèi)存頁是 可讀的,它就是可執(zhí)行的。一個(gè)名叫Solar De

22、signer的開發(fā)人員想出了一種內(nèi) 核和處理器機(jī)制的聰明組合,為Linux內(nèi)核創(chuàng)建了一個(gè)“非執(zhí)行的堆棧補(bǔ)丁”; 有了這個(gè)補(bǔ)丁,堆棧上的程序就不再能夠像通常的那樣在x86上運(yùn)行。事實(shí)證 明在有些情況下,可執(zhí)行程序需要在堆棧上;這包括信號(hào)處理和跳板代碼(trampoline)處理。trampoline是有時(shí)由編譯器(比如GNAT Ada編譯器) 生成的奇妙結(jié)構(gòu),用以支持像嵌套子例程之類的結(jié)構(gòu)。Solar Designer還解決 了如何在防止攻擊的同時(shí)使這些特殊情況不受影響的問題。Linux中實(shí)現(xiàn)這個(gè)目的的最初補(bǔ)丁在1998年被Linus Torvalds拒絕,這是因 為一個(gè)有趣的原因。即使不能將代

23、碼放到堆棧上,攻擊者也可以利用緩沖區(qū)溢出 來使程序“返回”某個(gè)現(xiàn)有的子例程(比如C庫中的某個(gè)子例程),從而進(jìn)行 攻擊。簡(jiǎn)而言之,僅只是擁有非可執(zhí)行的堆棧是不足夠的。一段時(shí)間之后,人們又想出了一種防止該問題的新思路:將所有可執(zhí)行代碼轉(zhuǎn)移 到一個(gè)稱為“ASCII保護(hù)(ASCII armor)”區(qū)域的內(nèi)存區(qū)。要理解這是如何工 作的,就必須知道攻擊者通常不能使用一般的緩沖區(qū)溢出攻擊來插入ASCII NUL 字符(0)這個(gè)事實(shí)。這意味著攻擊者會(huì)發(fā)現(xiàn),要使一個(gè)程序返回包含0的地址 是很困難的。由于這個(gè)事實(shí),將所有可執(zhí)行代碼轉(zhuǎn)移到包含0的地址就會(huì)使得 攻擊該程序困難多了。具有這個(gè)屬性的最大連續(xù)內(nèi)存范圍是從0

24、到0 x01010100的一組內(nèi)存地址,因 此它們就被命名為ASCII保護(hù)區(qū)域(還有具有此屬性的其他地址,但它們是分 散的)。與非可執(zhí)行的堆棧相結(jié)合,這種方法就相當(dāng)有價(jià)值了:非可執(zhí)行的堆棧 阻止攻擊者發(fā)送可執(zhí)行代碼,而ASCII保護(hù)內(nèi)存使得攻擊者難于通過利用現(xiàn)有 代碼來繞過非可執(zhí)行堆棧。這樣將保護(hù)程序代碼避免堆棧、緩沖區(qū)和函數(shù)指針溢 出,而且全都不需重新編譯。然而,ASCI I保護(hù)內(nèi)存并不適用于所有程序;大程序也許無法裝入ASCI I保護(hù) 內(nèi)存區(qū)域(因此這種保護(hù)是不完美的),而且有時(shí)攻擊者 能夠?qū)?插入目的地 址。此外,有些實(shí)現(xiàn)不支持跳板代碼,因此可能必須對(duì)需要這種保護(hù)的程序禁 用該特性。Re

25、d Hat的Ingo Molnar在他的“exec-shield”補(bǔ)丁中實(shí)現(xiàn)了這種 思想,該補(bǔ)丁由Fedora核心(可從Red Hat獲得它的免費(fèi)版本)所使用。最 新版本的OpenWall GNU/Linux (OWL)使用了 Solar Designer提供的這種方法 的實(shí)現(xiàn)(請(qǐng)參閱參考資料 以獲得指向這些版本的鏈接)。其他方法還有其他許多方法。一種方法就是使標(biāo)準(zhǔn)庫對(duì)攻擊更具抵抗力。Lucent Technologies開發(fā)了 Libsafe,這是多個(gè)標(biāo)準(zhǔn)C庫函數(shù)的包裝,也就是像 strcpy()這樣已知的對(duì)stack-smashing攻擊很脆弱的函數(shù)。Libsafe是在 LGPL下授予許可

26、證的開放源代碼軟件。那些函數(shù)的libsafe版本執(zhí)行相關(guān)的檢 查,確保數(shù)組改寫不會(huì)超出堆棧楨。然而,這種方法僅保護(hù)那些特定的函數(shù),而 不是從總體上防止堆棧溢出缺陷,并且它僅保護(hù)堆棧,而不保護(hù)堆棧中的局部變 量。它們的最初實(shí)現(xiàn)使用了 LD_PRELOAD,而這可能與其他程序產(chǎn)生沖突。Linux 的Mandrake發(fā)行套件(從7.1版開始)包括了 libsafe。另一種方法稱為“分割控制和數(shù)據(jù)堆?!币灰换镜乃悸肥菍⒍褩7指顬閮蓚€(gè) 堆棧,一個(gè)用于存儲(chǔ)控制信息(比如“返回”地址),另一個(gè)用于控制其他所有 數(shù)據(jù)。Xu et al.在gcc中實(shí)現(xiàn)了這種方法,StackShield在匯編程序中實(shí)現(xiàn) 了這種

27、方法。這樣使得操縱返回地址困難多了,但它不會(huì)阻止改變調(diào)用函數(shù)的數(shù) 據(jù)的緩沖區(qū)溢出攻擊。事實(shí)上還有其他方法,包括隨機(jī)化可執(zhí)行程序的位置;Crispen的“PointGuard” 將這種探測(cè)儀思想引申到了堆中,等等。如何保護(hù)當(dāng)今的計(jì)算機(jī)現(xiàn)在已成了一項(xiàng) 活躍的研究任務(wù)?;仨撌滓话惚Wo(hù)是不足夠的如此多不同的方法意味著什么呢?對(duì)用戶來說,好的一面在于大量創(chuàng)新的方法正 在試驗(yàn)之中;長期看來,這種“競(jìng)爭(zhēng)”會(huì)更容易看出哪種方法最好。而且,這種 多樣性還使得攻擊者躲避所有這些方法更加困難。然而,這種多樣性也意味著開 發(fā)人員需要 避免編寫會(huì)干擾其中任何一種方法的代碼。這在實(shí)踐上是很容易的; 只要不編寫對(duì)堆棧楨執(zhí)行

28、低級(jí)操作或?qū)Χ褩5牟季肿骷僭O(shè)的代碼就行了。即使不 存在這些方法,這也是一個(gè)很好的建議。操作系統(tǒng)供應(yīng)商需要參與進(jìn)來就相當(dāng)明顯了:至少挑選一種方法,并使用它。緩 沖區(qū)溢出是第一號(hào)的問題,這些方法中最好的方法通常能夠減輕發(fā)行套件中幾乎 半數(shù)已知缺陷的影響??梢宰C明,不管是基于探測(cè)儀的方法更好,還是基于非可 執(zhí)行堆棧的方法更好,它們都具有各自的優(yōu)點(diǎn)。可以將它們結(jié)合起來使用,但是 少數(shù)方法不支持這樣使用,因?yàn)楦郊拥男阅軗p失使得這樣做不值得。我并沒有其 他意思,至少就這些方法本身而言是這樣;libsafe和分割控制及數(shù)據(jù)堆棧的方 法在它們所提供的保護(hù)方面都具有局限性。當(dāng)然,最糟糕的解決辦法就是根本不 對(duì)這

29、個(gè)第一號(hào)的缺陷提供保護(hù)。還沒有實(shí)現(xiàn)一種方法的軟件供應(yīng)商需要立即計(jì)劃 這樣做。從2004年開始,用戶應(yīng)該開始避免使用這樣的操作系統(tǒng),即它們至少 沒有對(duì)緩沖區(qū)溢出提供某種自動(dòng)保護(hù)機(jī)制。然而,沒有哪種方法允許開發(fā)人員忽略緩沖區(qū)溢出。所有這些方法都能夠被攻擊 者破壞。攻擊者也許能夠通過改變函數(shù)中其他數(shù)據(jù)的值來利用緩沖區(qū)溢出;沒 有哪種方法能夠防止這點(diǎn)。如果能夠插入某些難于創(chuàng)建的值(比如NUL字符), 那么這其中的許多方法都能被攻擊者繞開;隨著多媒體和壓縮數(shù)據(jù)變得更加普 遍,攻擊者繞開這些方法就更容易了。從根本上講,所有這些方法都能減輕從程 序接管攻擊到拒絕服務(wù)攻擊的緩沖區(qū)溢出攻擊所帶來的破壞。遺憾的是

30、,隨著計(jì) 算機(jī)系統(tǒng)在更多關(guān)鍵場(chǎng)合的使用,即使拒絕服務(wù)通常也是不可接受的。因而,盡 管發(fā)行套件應(yīng)該至少包括一種適當(dāng)?shù)姆烙椒ǎ⑶议_發(fā)人員應(yīng)該使用(而不是 反對(duì))那些方法,但是開發(fā)人員仍然需要最初就編寫無缺陷的軟件?;仨撌證/C+解決方案針對(duì)緩沖區(qū)溢出的一種簡(jiǎn)單解決辦法就是轉(zhuǎn)為使用能夠防止緩沖區(qū)溢出的語言。 畢竟,除了 C和C+夕卜,幾乎每種高級(jí)語言都具有有效防止緩沖區(qū)溢出的內(nèi)置 機(jī)制。但是許多開發(fā)人員因?yàn)榉N種原因還是選擇使用C和C+。那么您能做什 么呢?事實(shí)證明存在許多防止緩沖區(qū)溢出的不同技術(shù),但它們都可劃分為以下兩種方 法:靜態(tài)分配的緩沖區(qū)和動(dòng)態(tài)分配的緩沖區(qū)。首先,我們將講述這兩種方法分別

31、是什么。然后,我們將討論靜態(tài)方法的兩個(gè)例子(標(biāo)準(zhǔn)C strncpy/strncat和 OpenBSD的strlcpy/strlcat ),接著討論動(dòng)態(tài)方法的兩個(gè)例子(SafeStr和 C+ 的 std:string )?;仨撌字匾x擇:靜態(tài)和動(dòng)態(tài)分配的緩沖區(qū) 緩沖區(qū)具有有限的空間。因此實(shí)際上存在處理緩沖區(qū)空間不足的兩種可能方式?!办o態(tài)分配的緩沖區(qū)”方法:也就是當(dāng)緩沖區(qū)用完時(shí),您抱怨并拒絕為緩沖區(qū)增加 任何空間?!皠?dòng)態(tài)分配的緩沖區(qū)”方法:也就是當(dāng)緩沖區(qū)用完時(shí),動(dòng)態(tài)地將緩沖區(qū)大小調(diào)整到 更大的尺寸,直至用完所有內(nèi)存。靜態(tài)方法具有一些缺點(diǎn)。事實(shí)上,靜態(tài)方法有時(shí)可能會(huì)帶來不同的缺陷。靜態(tài)方 法基本上就

32、是丟棄“過多的”數(shù)據(jù)。如果程序無論如何還是使用了結(jié) 果數(shù)據(jù),那么攻擊者會(huì)嘗試填滿緩沖區(qū),以便在數(shù)據(jù)被截?cái)鄷r(shí)使用他希望的任何 內(nèi)容來填充緩沖區(qū)。如果使用靜態(tài)方法,應(yīng)該確保攻擊者能夠做的最糟糕的事情 不會(huì)使得預(yù)先的假設(shè)無效,而且檢查最終結(jié)果也是一個(gè)好主意。動(dòng)態(tài)方法具有許多優(yōu)點(diǎn):它們能夠向上適用于更大的問題(而不是帶來任意的限 制),而且它們沒有導(dǎo)致安全問題的字符數(shù)組截?cái)鄦栴}。但它們也具有自身的問 題:在接受任意大小的數(shù)據(jù)時(shí),可能會(huì)遇到內(nèi)存不足的情況一一而這在輸入時(shí) 也許不會(huì)發(fā)生。任何內(nèi)存分配都可能會(huì)失敗,而編寫真正很好地處理該問題的C 或C+程序是很困難的。甚至在內(nèi)存真正用完之前,也可能導(dǎo)致計(jì)算機(jī)

33、變得太 忙而不可用。簡(jiǎn)而言之,動(dòng)態(tài)方法通常使得攻擊者發(fā)起拒絕服務(wù)攻擊變得更加容 易。因此仍然需要限制輸入。此外,必須小心設(shè)計(jì)程序來處理任意位置的內(nèi)存耗 盡問題,而這不是一件容易的事情。標(biāo)準(zhǔn)C庫方法最簡(jiǎn)單的方法之一是簡(jiǎn)單地使用那些設(shè)計(jì)用于防止緩沖區(qū)溢出的標(biāo)準(zhǔn)C庫函數(shù) (即使在使用C +,這也是可行的),特別是strncpy(3)和strncat(3)。這些標(biāo)準(zhǔn)C庫函數(shù)一般支持靜態(tài)分配方法,也就是在數(shù)據(jù)無法裝入緩沖區(qū)時(shí)丟 棄它。這種方法的最大優(yōu)點(diǎn)在于,您可以肯定這些函數(shù)在任何機(jī)器上都可用,并 且任何C/C+開發(fā)人員都會(huì)了解它們。許許多多的程序都是以這種方式編寫的, 并且確實(shí)可行。遺憾的是,要正確地

34、做到這點(diǎn)卻是令人吃驚的困難。下面是其中的一些問題:strncpy(3)和strncat(3)都要求您給出剩余的空間,而不是給出緩沖區(qū)的總 大小。這之所以會(huì)成為問題是因?yàn)?,雖然緩沖區(qū)的大小一經(jīng)分配就不會(huì)變化,但是 緩沖區(qū)中剩余的空間量會(huì)在每次添加或刪除數(shù)據(jù)時(shí)發(fā)生變化。這意味著程序員必須 始終跟蹤或重新計(jì)算剩余的空間。這種跟蹤或重新計(jì)算很容易出錯(cuò),而任何錯(cuò)誤都 可能給緩沖區(qū)攻擊打開方便之門。在發(fā)生了溢出(和數(shù)據(jù)丟失)時(shí),兩個(gè)函數(shù)都不會(huì)給出簡(jiǎn)單的報(bào)告,因此如果要檢 測(cè)緩沖區(qū)溢出,程序員就必須做更多的工作。如果源字符串至少和目標(biāo)一樣長,那么函數(shù)strncpy(3)還不會(huì)使用NUL來結(jié)束 字符串;這可能

35、會(huì)在以后導(dǎo)致嚴(yán)重破壞。因而,在運(yùn)行strncpy(3)之后,您通 常需要重新結(jié)束目標(biāo)字符串。函數(shù)strncpy(3)還可以用來僅把源字符串的一部分復(fù)制到目標(biāo)中。在執(zhí)行這 個(gè)操作時(shí),要復(fù)制的字符的數(shù)目通常是基于源字符串的相關(guān)信息來計(jì)算的。這樣的 危險(xiǎn)之處在于,如果忘了考慮可用的緩沖區(qū)空間,那么即使在使用strncpy(3) 時(shí)也可能會(huì)留下緩沖區(qū)攻擊隱患。這個(gè)函數(shù)也不會(huì)復(fù)制NUL字符,這可能也是一 個(gè)問題??梢酝ㄟ^一種防止緩沖區(qū)溢出的方式使用sprintf(),但是意外地留下緩沖區(qū)溢 出攻擊隱患是非常容易的。sprintf()函數(shù)使用一個(gè)控制字符串來指定輸出格 式,該控制字符串通常包括“ s ”(

36、字符串輸出)。如果指定字符串輸出的精確指 定符(比如%.10s),那么您就能夠通過指定輸出的最大長度來防止緩沖區(qū)溢出。 甚至可以使用“ * ”作為精確指定符(比如“ .*s ”),這樣您就可以傳入一個(gè) 最大長度值,而不是在控制字符串中嵌入最大長度值。這樣的問題在于,很容易就 會(huì)不正確地使用sprintf()。一個(gè)“字段寬度”(比如“ 10s ”)僅指定了最小 長度一一而不是最大長度。“字段寬度”指定符會(huì)留下緩沖區(qū)溢出隱患,而字段寬 度和精確寬度指定符看起來幾乎完全相同一一唯一的區(qū)別在于安全的版本具有一 個(gè)點(diǎn)號(hào)。另一個(gè)問題在于,精確字段僅指定一個(gè)參數(shù)的最大長度,但是緩沖區(qū)需要 針對(duì)組合起來的數(shù)據(jù)

37、的最大尺寸調(diào)整大小。scanf()系列函數(shù)具有一個(gè)最大寬度值,至少IEEE Standard 1003-2001清楚地規(guī) 定這些函數(shù)一定不能讀取超過最大寬度的數(shù)據(jù)。遺憾的是,并非所有規(guī)范都清楚地 規(guī)定了這一點(diǎn),我們不清楚是否所有實(shí)現(xiàn)都正確地實(shí)現(xiàn)了這些限制(這在如今的 GNU/Linux系統(tǒng)上就不能正確地工作)。如果您依賴它,那么在安裝或初始化期間 運(yùn)行小測(cè)試來確保它能正確工作,這樣做將是明智的。strncpy(3)還存在一個(gè)惱人的性能問題。從理論上講,strncpy(3)是 strcpy(3)的安全替代者,但是strncpy(3)還會(huì)在源字符串結(jié)束時(shí)使用NUL 來填充整個(gè)目標(biāo)空間。這是很奇怪的

38、,因?yàn)閷?shí)際上并不存在這樣做的很好理由, 但是它從一開始就是這樣,并且有些程序還依賴這個(gè)特性。這意味著從strcpy(3) 切換到strncpy(3)會(huì)降低性能一一這在如今的計(jì)算機(jī)上通常不是一個(gè)嚴(yán)重 的問題,但它仍然是有害的。那么可以使用標(biāo)準(zhǔn)C庫的例程來防止緩沖區(qū)溢出嗎?是的,不過并不容易。如 果計(jì)劃沿著這條路線走,您需要理解上述的所有要點(diǎn)。或者,您可以使用下面幾 節(jié)將要講述的一種替代方法。OpenBSD 的 strlcpy/strlcatOpenBSD開發(fā)人員開發(fā)了一種不同的靜態(tài)方法,這種方法基于他們開發(fā)的新函數(shù) strlcpy(3)和strlcat(3)。這些函數(shù)執(zhí)行字符串復(fù)制和拼接,不過更

39、不容易 出錯(cuò)。這些函數(shù)的原型如下: size_t strlcpy (char *dst, const char *src, size_t size);size_t strlcat (char *dst, const char *src, size_t size); strlcpy()函數(shù)把以NUL結(jié)尾的字符串從“ src ”復(fù)制到“ dst ”(最多size-1 個(gè)字符)。strlcat()函數(shù)把以NUL結(jié)尾的字符串src附加到dst的結(jié)尾(但 是目標(biāo)中的字符數(shù)目將不超過size-1)。初看起來,它們的原型和標(biāo)準(zhǔn)C庫函數(shù)并沒有多大區(qū)別。但是事實(shí)上,它們之 間存在一些顯著區(qū)別。這些函數(shù)都接受目標(biāo)

40、的總大小(而不是剩余空間)作為參 數(shù)。這意味著您不必連續(xù)地重新計(jì)算空間大小,而這是一項(xiàng)易于出錯(cuò)的任務(wù)。此 外,只要目標(biāo)的大小至少為1,兩個(gè)函數(shù)都保證目標(biāo)將以NUL結(jié)尾(您不能將 任何內(nèi)容放入零長度的緩沖區(qū))。如果沒有發(fā)生緩沖區(qū)溢出,返回值始終是組合 字符串的長度;這使得檢測(cè)緩沖區(qū)溢出真正變得容易了。遺憾的是,strlcpy(3)和strlcat(3)并不是在類UNIX系統(tǒng)的標(biāo)準(zhǔn)庫中普遍 可用。OpenBSD和Solaris將它們內(nèi)置在string.h中,但是GNU/Linux系 統(tǒng)卻不是這樣。這并不是一件那么困難的事情;因?yàn)楫?dāng)?shù)讓酉到y(tǒng)沒有提供它們時(shí), 您甚至可以將一些小函數(shù)直接包括在自己的程序

41、源代碼中。SafeStrMessier和Viega開發(fā)了 “SafeStr”庫,這是一種用于C的動(dòng)態(tài)方法,它自 動(dòng)根據(jù)需要調(diào)整字符串的大小。使用malloc()實(shí)現(xiàn)所使用的相同技巧,Safestr 字符串很容易轉(zhuǎn)換為常規(guī)的C“ char* ”字符串:safestr在傳遞指針“之前” 的地址處存儲(chǔ)重要信息。這種技術(shù)的優(yōu)點(diǎn)在于,在現(xiàn)有程序中使用SafeStr將 會(huì)很容易。SafeStr還支持“只讀”和“受信任”的字符串,這也可能是有用的。 這種方法的一個(gè)問題在于它需要XXL (這是一個(gè)給C添加異常處理和資源管理 支持的庫),因此您實(shí)際上要僅為了處理字符串而引入一個(gè)重要的庫Safestr是 在開放源

42、代碼的BSD風(fēng)格的許可證下發(fā)布的。C+ std:string針對(duì)C+用戶的另一種解決方案是標(biāo)準(zhǔn)的std:string類,這是一種動(dòng)態(tài)的方 法(緩沖區(qū)根據(jù)需要而增長)。它幾乎是不需要傷腦筋的,因?yàn)镃+語言直接 支持該類,因此不需要做特殊的工作就可使用它,并且其他庫也可能會(huì)使用它。 就其本身而言,std:string通常會(huì)防止緩沖區(qū)溢出,但是如果通過它提取一 個(gè)普通C字符串(比如使用data()或c_str(),那么上面討論的所有問題 都會(huì)重新出現(xiàn)。還要記住data()并不總是返回以NUL結(jié)尾的字符串。由于種種歷史原因,許多C+庫和預(yù)先存在的程序都創(chuàng)建了它們自己的字符串 類。這可能使得std:st

43、ring更難于使用,并且在使用那些庫或修改那些程序 時(shí)效率很低,因?yàn)椴煌淖址愋蛯⒉坏貌贿B續(xù)地來回轉(zhuǎn)換。并非其他所有那 些字符串類都會(huì)防止緩沖區(qū)溢出,并且如果它們對(duì)C不受保護(hù)的char*類型執(zhí) 行自動(dòng)轉(zhuǎn)換,那么緩沖區(qū)溢出缺陷很容易引入那些類中。回頁首工具有許多工具可以在緩沖區(qū)溢出缺陷導(dǎo)致問題之前幫助檢測(cè)它們。例如,像我的 Flawfinder和Viega的RATS這樣的工具能夠搜索源代碼,識(shí)別出可能被不正 確地使用的函數(shù)(基于它們的參數(shù)來歸類)。這些工具的一個(gè)缺點(diǎn)在于,它們不 是完美的一一它們會(huì)遺漏一些緩沖區(qū)溢出缺陷,并且它們會(huì)識(shí)別出一些實(shí)際上 不是問題的“問題”。但是使用它們?nèi)匀皇侵档玫?/p>

44、,因?yàn)榕c手工查找相比,它們 將幫助您在短得多的時(shí)間內(nèi)識(shí)別出代碼中的潛在問題?;仨撌捉Y(jié)束語借助知識(shí)、謹(jǐn)慎和工具,C和C+中的緩沖區(qū)溢出缺陷是可以防止的。不過做 起來并沒有那么容易,特別是在C中。如果使用C和C+來編寫安全的程序, 您需要真正理解緩沖區(qū)溢出和如何防止它們。一種替代方法是使用另一種編程語言,因?yàn)槿缃竦膸缀跗渌姓Z言都能防止緩 沖區(qū)溢出。但是使用另一種語言并不會(huì)消除所有問題。許多語言依賴C庫,并 且許多語言還具有關(guān)閉該保護(hù)特性的機(jī)制(為速度而犧牲安全性)。但是即便如 此,不管您使用哪種語言,開發(fā)人員都可能會(huì)犯其他許多錯(cuò)誤,從而帶來引入缺 陷。不管您做什么,開發(fā)沒有錯(cuò)誤的程序都是極其困

45、難的,即使最仔細(xì)的復(fù)查通常也 會(huì)遺漏其中一些錯(cuò)誤。開發(fā)安全程序的最重要方法之一是最小化特權(quán)。那意味 著程序的各個(gè)部分應(yīng)該具有它們需要的唯一特權(quán),一點(diǎn)也不能多。這樣,即使程 序具有缺陷(誰能無過?),也可能會(huì)避免將該缺陷轉(zhuǎn)化為安全事故。但是在實(shí) 踐中如何做到這點(diǎn)呢?下一篇文章將研究如何實(shí)際地最小化Linux/UNIX系統(tǒng) 中的特權(quán),以便您能防止自己不可避免的錯(cuò)誤所帶來安全隱患。參考資料您可以參閱本文在developerWorks全球站點(diǎn)上的英文原文.閱讀developerWorks上David的安全編程專欄系列中的所有文章連載。 David 的書 Secure Programming for L

46、inux and Unix HOWTO 詳細(xì)介紹了如何開發(fā)安 全的軟件。 The What, Why, and How of the 1988 Internet Worm更詳細(xì)地介紹了 1988 年的 Morris蠕蟲事件。C. Ian Kyer、Warren J. Sheffer 和 Bruce Salvatore、Fasken Martineau DuMoulin LLP 所 著的 New IT Concerns in the Age of Anti-Terrorism: How the Canadian Government has Reacted and How Business Sh

47、ould React 指出,Morris 蠕蟲使得當(dāng)時(shí)大約有 88,000 臺(tái)計(jì)算機(jī)的Internet中的10%的計(jì)算機(jī)崩潰。Steve Burnett and Stephen Paine 所著的 RSA Securitys Official Guide to Cryptography(McGraw-Hill, 2001 年)在第 11 章(“Doing it Wrong: The break-ins)中指出, Morris蠕蟲使得大約10%的Internet崩潰,該書還對(duì)安全故障提出了其他有趣的 評(píng)論。CERT(R) Advisory CA-2001-19 Code Red Worm Exp

48、loiting Buffer Overflow In IIS Indexing Service DLL更詳細(xì)地介紹了 Code Red 病毒?!?Frontline: Cyber War!: The Warnings?總結(jié)了各種攻擊及其已知的影響,包括 Code Red 和 Slammer。Aleph One (Elias Levy)撰寫的 Smashing The Stack For Fun And Profit一文(Phrack Magazine,1996 年 11 月 8 日,第 49 期第 14 篇文章)闡述了 stack-smashing 攻 擊是如何進(jìn)行的。在該文刊出之前許多年就已

49、經(jīng)在發(fā)生stack-smashing攻擊,但是 該文很好地描述了這些攻擊。David 的文章 More than a Gigabuck: Estimating GNU/Linuxs Size研究了 Red HatLinux 7.1的源代碼。結(jié)果發(fā)現(xiàn)這個(gè)發(fā)行套件包括3千多萬個(gè)實(shí)際源代碼行(source lines of code,SLOC),其中86%都是用C或者C+編寫的。該文還發(fā)現(xiàn),如果 采用美國的傳統(tǒng)專有手段,開發(fā)這個(gè)Linux發(fā)行套件將需要10億美元和8,000個(gè) 人年的成本(以2000年的美元幣值計(jì))。Crispin Cowan、Perry Wagle、Calton Pu、Steve Beattie 和 Jonathan Walpole 撰寫的 Buffe

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論