COM組件棧緩沖區(qū)溢出漏洞檢測技術研究_第1頁
COM組件棧緩沖區(qū)溢出漏洞檢測技術研究_第2頁
COM組件棧緩沖區(qū)溢出漏洞檢測技術研究_第3頁
COM組件棧緩沖區(qū)溢出漏洞檢測技術研究_第4頁
COM組件棧緩沖區(qū)溢出漏洞檢測技術研究_第5頁
已閱讀5頁,還剩88頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

摘要為了解決軟件復用,縮短軟件開發(fā)時間,降低維護成本和實現(xiàn)程序動態(tài)升級,軟件設計領域產(chǎn)生了組件化程序設計結構,并且日益成為發(fā)展趨勢。微軟的COM組件對象模型是當今比較成熟的軟件組件模型之一,被廣泛應用于Windows操作系統(tǒng)和應用程序中。隨著COM組件技術的大量使用,COM組件暴露出越來越多的安全問題,其中,緩沖區(qū)溢出安全問題占了很大比例。 緩沖區(qū)溢出漏洞一直是安全漏洞最常見的一種形式。緩沖區(qū)溢出問題主要出現(xiàn)在C/C++這類非類型安全語言中,而在新一代的編程語言,例如Java、C#中不存在。一個重要的原因就是C/C++允許通過指針進行間接內(nèi)存訪問但沒有緩沖區(qū)邊界檢查和提供了大量對緩沖區(qū)可能存在不安全操作的庫函數(shù),在Windows操作系統(tǒng)中也存在類似的函數(shù)。因此,如果能采用有效的手段對COM組件的緩沖區(qū)溢出漏洞進行檢測,將能極大地提高組件軟件的安全性。 根據(jù)COM組件多數(shù)情況下源代碼未可知的測試特點,基于COM組件的二進制代碼實現(xiàn)對其可能存在的棧緩沖區(qū)溢出漏洞的檢測。檢測方法是將檢測緩沖區(qū)溢出問題轉化為整數(shù)范圍分析問題。建立適用COM組件的危險函數(shù)庫,在匯編代碼中識別危險函數(shù)的調(diào)用位置,然后根據(jù)危險函數(shù)參數(shù)的類型通過掃描識別不同的緩沖區(qū),將聲明的緩沖區(qū)大小和使用的緩沖區(qū)大小等價為整數(shù)范圍,依據(jù)制定的緩沖區(qū)溢出標準檢測溢出漏洞。根據(jù)COM組件使用虛表定位函數(shù)的結構特點,實現(xiàn)了COM組件中用戶函數(shù)的精確定位;同時利用IDC腳本語言提取了COM組件中的函數(shù)依賴關系圖?;谝陨瞎ぷ?,實現(xiàn)了一個COM組件棧緩沖區(qū)溢出漏洞檢測原型系統(tǒng)。關鍵詞:緩沖區(qū)溢出,靜態(tài)分析,二進制代碼,COM組件,危險函數(shù)AbstractInordertosolvetheproblemofsoftwarereuse,shortensoftwaredevelopingperiod,reducemaintenancecostandrealizesoftwareautomaticupdating,componentsoftwaredesignisputforwardandhasbecomeaninevitabledevelopmentaltrend.Microsoft'sComponentObjectModel(COM)isarelativelymatureoneofsoftwarecomponentmodels,whichiswidelyusedinWindowsoperatingsystemandapplicationprograms.AlongwiththeprevalenceofCOM,moreandmoresecurityproblemsareexposed,ofwhichbufferoverflowconstitutesahighproportion.Bufferoverflowhasbeenoneofthecommonestformsofsecurityholes.Itmainlyexistsinthenontype-safelanguages,suchasCandC++.However,newgenerationlanguages,suchasJavaandC#,donothavethisproblem.OneofthemostimportantreasonsisthatCallowindirectmemoryaccessbypointerwithoutboundarycheckandprovidemanyunsafefunctionswhichmaycausebufferoverflow.Meanwhile,therearesuchfunctionsinWindowsoperatingsystem.Therefore,ifeffectivemeanscanbeadoptedtodetectbufferoverflow,securityofcomponentsoftwarewillbegreatlyenhanced.BufferoverflowdetectionbasedonbinarycodeisthoroughlystudiedinthisthesisbecauseinmostcasesthesourcecodeofCOMisunknown.Adetectionalgorithmisproposedbymodelingthebufferoverflowproblemandtransformittorangescomparisonofintegers.First,establishanunsafefunctionlibraryofCOManduseittodistinguishunsafefunctioncallsinassemblycode.Second,accordingtotypesofargumentspassedtounsafefunctions,differentbuffersaredistinguished.Third,obtainrangesofintegersbasedonthesizeofdeclaredbuffersandallocatedones.Last,usebufferoverflowdetectioncriterionmadebythisthesistodetectbufferoverflow.COMusesvirtualtabletolocatefunctions.Accordingtothisstructuralcharacteristics,thisthesisrealiseaccuratepositioningofuserfunctionsandextracttheirdependencerelationsbymeansofIDCscriptlanguage.Basedontheworkmentionedabove,aprototypesystemthatcandetchstackbufferoverflowofCOMisrealised.Keywords:bufferoverflow,staticanalysis,binarycode,COMcomponent,unsafefunction目錄TOC\o"1-2"\h\z\u摘要 IAbstract II1緒論1.1課題背景 (1)1.2國內(nèi)外概況 (2)1.3論文主要研究工作 (4)1.4論文結構 (5)2COM組件及棧緩沖區(qū)溢出漏洞檢測方法基礎2.1COM組件 (6)2.2緩沖區(qū)溢出原理 (8)2.3棧緩沖區(qū)溢出檢測技術 (11)2.4小結 (19)3COM組件棧緩沖區(qū)溢出漏洞檢測技術3.1函數(shù)的識別 (21)3.2參數(shù)的識別 (25)3.3函數(shù)的返回值 (25)3.4變量的識別 (27)3.5COM組件危險函數(shù)庫建立 (30)3.6小結 (34)4COM組件棧緩沖區(qū)溢出漏洞檢測系統(tǒng)設計與實現(xiàn)4.1模塊結構和功能 (35)4.2主要數(shù)據(jù)結構 (36)4.3函數(shù)定位模塊實現(xiàn) (37)4.4棧溢出靜態(tài)分析模塊實現(xiàn) (38)4.5結果輸出模塊實現(xiàn) (47)4.6與CSTS的接口 (49)4.7小結 (50)5實驗與測試5.1實驗目的 (51)5.2實驗環(huán)境 (51)5.3測試用例 (52)5.4測試結果 (53)6總結與展望6.1工作總結 (59)6.2工作展望 (60)致謝 (61)參考文獻 (62)附錄攻讀學位期間參與的科研項目 (67)1緒論1.1課題背景繼面向對象的軟件設計方法之后,基于組件的軟件設計方法正在逐漸成為新的趨勢[1]。按照組件化程序設計的思想,復雜的應用程序被設計成一些小的、功能單一的組件模塊,這些模塊可以運行在同一機器上,也可以運行在不同的機器上,每臺機器的運行環(huán)境可以不同,甚至可以是不同的操作系統(tǒng)。為了實現(xiàn)這樣的應用軟件,組件需要一些細致的規(guī)范,只有組件程序遵守這些共同的規(guī)范,組件軟件才能正常運行。目前流行的組件規(guī)范有三種,它們是OMG(ObjectManagementGroup)提出的CORBA(CommonObjectRequestBreakerArchitecture);微軟提出的COM(ComponentObjectModel);還有SUN公司提出的EJB(EnterpriseJavaBean)。Cai[2]給出了三種規(guī)范的詳細比較。微軟的COM組件對象模型是當今比較成熟的軟件組件規(guī)范之一,被廣泛應用于Windows操作系統(tǒng)和應用程序中。在分布式計算、Internet網(wǎng)絡、三層體系結構開發(fā)以及音視頻處理等前沿領域,COM組件技術正在被大量使用。COM組件在受到廣泛應用的同時,自身的安全問題也日益暴露。其中,緩沖區(qū)溢出安全問題占了很大的比例。在安全日益被人們所關注的今天,緩沖區(qū)溢出毫無疑問是最大的安全威脅之一。1988年,Internet上的第一例蠕蟲(Morris)攻擊,就是利用Vax和Sun機器fingerd中的緩沖區(qū)溢出漏洞。SANS評選出的2005年威脅最大的20個漏洞中,有8個跟緩沖區(qū)溢出有關。國內(nèi)綠盟科技根據(jù)安全漏洞的嚴重程度、影響范圍等因素綜合評出2006年度的十大安全漏洞,緩沖區(qū)溢出漏洞占4個。綠盟科技在2006年發(fā)布CVE漏洞8個,而緩沖區(qū)溢出漏洞就占了5個。根據(jù)CERT的統(tǒng)計數(shù)據(jù),近幾年與緩沖區(qū)溢出有關的安全事件在50%以上。經(jīng)過十幾年的發(fā)展,緩沖區(qū)溢出已經(jīng)成為一種成熟和最有效的黑客攻擊手段。并且在接下來的許多年里,情況仍然會是這樣。隨著COM組件的廣泛應用,COM組件中暴露的緩沖區(qū)溢出的安全問題的數(shù)量也是逐年上升。因此檢測COM組件的緩沖區(qū)溢出漏洞具有十分重要的意義。COM組件多數(shù)情況下源代碼不可知,目前常用的測試方法是黑盒測試。按照經(jīng)驗和一般規(guī)律設計大量測試用例,以期望觸發(fā)組件的漏洞和異常。這種方法在某些時候會起到較好的效果,但效率太低,而且對測試人員的經(jīng)驗和技術水平要求很高。為了改進已有的COM組件測試方法,提高對COM組件安全漏洞的檢測,本論文研究并實現(xiàn)了COM組件棧緩沖區(qū)溢出漏洞檢測原型系統(tǒng)。 同時,對COM組件進行靜態(tài)分析獲取其結構信息和安全漏洞信息是實驗室正在研發(fā)的針對COM組件的安全漏洞自動檢測工具的需要。所以本論文研發(fā)的原型系統(tǒng)可以很好與實驗室研發(fā)的自動檢測工具結合,以完善該工具的效率和功能。1.2國內(nèi)外概況軟構件技術提供了一種較面向對象方法更為有效的軟件設計模式,構件軟件被廣泛應用并成為一種主流軟件形態(tài)[3](本段及后兩段中的“構件”與論文中的“組件”概念一樣,只是翻譯的不同)。然而,構件的內(nèi)部信息屏蔽、演變速度快以及構件間的異質(zhì)、松耦合等特點給構件及構件軟件的測試工作帶來一系列的問題[4,5]:對于構件提供者,問題在于需要使用相當充分的覆蓋準則進行測試以提高構件的可復用性;對用戶使用構件的上下文環(huán)境并不完全了解;并且不能很好的獲得構件的錯誤報告。對于構件使用者而言,問題在于源代碼不可見性給測試設計和用例生成帶來了極大的障礙[6];系統(tǒng)中構件的異質(zhì)性不利于測試標準的統(tǒng)一及自動化的實現(xiàn);構件版本更新快以及不確定性迫使要對構件系統(tǒng)進行較為頻繁的回歸測試等。構件軟件測試通常分為構件測試、子系統(tǒng)測試、系統(tǒng)測試三個階段[7],分別與傳統(tǒng)軟件測試過程的單元測試、集成測試和系統(tǒng)測試相對應。Harrold[8]認為應該從構件開發(fā)者和構件使用者兩個不同的角度來看待構件軟件的測試問題。一般來講,構件測試主要由構件的開發(fā)者完成,而后兩個階段的測試則由使用者實施。總之,根據(jù)構件自身的特點,尋求高效的構件軟件測試技術和開發(fā)實用的測試工具是當今軟件業(yè)界一個亟待解決的問題。相對于構件測試的研究,針對緩沖區(qū)溢出攻擊的研究比較成熟。緩沖區(qū)溢出攻擊可追溯到1988年臭名昭著的Morris蠕蟲,在隨后的1989年里,Spafford提交了一份關于運行在VAX機上的BSD版UNIX的fingerd的緩沖區(qū)溢出程序的技術細節(jié)的分析報告,這引起了一部分安全人士對這個研究領域的重視。但真正讓眾人認識緩沖區(qū)溢出攻擊的論文是1996年11月AlephOne在Phrack雜志第49期發(fā)表的論文“Smashingthestackforfunandprofit”[9]。該論文闡述了Linux系統(tǒng)中棧的結構和如何利用棧緩沖區(qū)溢出,并首次提出shellcode的概念。1999年,IIS4.0遠程攻擊代碼的作者darkspyritAKABarnabyJack[10]在PhrackMagzine(第55期)上提出了使用系統(tǒng)核心DLL中的“jmpesp”的指令來完成到shellcode跳轉的想法,從此開創(chuàng)了Win32平臺下緩沖區(qū)溢出的新思想,大量Windows平臺下的緩沖區(qū)溢出漏洞也被利用。在緩沖區(qū)溢出漏洞挖掘和檢測方面,國外已經(jīng)有一些較為深入的研究工作,并且這些技術都能應用到在Win32平臺下開發(fā)的軟件中,而國內(nèi)的研究還處于初級階段。通過對目前國內(nèi)外相關研究文獻的搜集整理,按照各種技術的研究對象的不同,可將緩沖區(qū)溢出漏洞檢測的研究工作進行如下分類[11,12,13]:1、基于源代碼的靜態(tài)檢測技術:將源代碼作為輸入,通過建立漏洞庫,掃描源代碼,匹配漏洞模式來檢測緩沖區(qū)溢出漏洞。目前關于此方面的研究較多,典型的工具有ITS4[14]、BOON[15]、Splint[16,17]、ARCHER[18]等。2、基于源代碼的動態(tài)檢測技術:可通過擴展編譯器功能和直接修改源代碼來完成溢出漏洞的檢測,如StackGuard[19,20]和STOBO[21]等工具。3、基于二進制代碼的動態(tài)檢測技術:通過靜態(tài)或動態(tài)修改二進制代碼和采用“黑盒測試”的方法實現(xiàn)棧溢出漏洞檢測,如RAD[22]、libverify[23]和Fuzz[24]等工具。4、基于二進制代碼的靜態(tài)檢測技術:該技術的研究目前比較少見,目前典型的研究是通過一些反匯編工具對目標代碼進行處理,然后再依賴一些源代碼靜態(tài)檢測技術進行處理[25,26,27],如TerryEruceCillette[28]、bugscam[29,30]等工具。漏洞檢測方法各有利弊,需要根據(jù)具體情況選擇相應的方法來確保最大程度上挖掘出緩沖區(qū)溢出漏洞。在上述的研究工作中,基于二進制代碼的靜態(tài)檢測技術對于針對COM組件的緩沖區(qū)溢出檢測具有指導意義。論文中的檢測技術就是歸屬于基于二進制代碼的靜態(tài)檢測技術的。1.3論文主要研究工作論文的主要工作為:在研究已有緩沖區(qū)溢出檢測技術,分析現(xiàn)存成熟的緩沖區(qū)溢出檢測工具,研究C/C++程序的逆向技術以及參加實驗室的針對COM組件的安全漏洞自動化檢測工具的開發(fā)的基礎上,設計了一種針對COM組件二進制代碼的棧緩沖區(qū)溢出漏洞檢測算法SBOD-BC-CC(stackbufferoverflowdetectionbasedonbinarycodeofCOMcomponent),并最終實現(xiàn)一個COM組件棧緩沖區(qū)溢出檢測原型系統(tǒng)。具體工作包括:1、現(xiàn)有的棧緩沖區(qū)溢出檢測技術的總結對現(xiàn)有的棧緩沖區(qū)溢出檢測技術進行了深入的研究,將溢出檢測技術分為四大類:基于源碼的靜態(tài)檢測、基于源碼的動態(tài)檢測、基于二進制代碼的靜態(tài)檢測和基于二進制代碼的動態(tài)檢測。詳細分析了這四類技術并介紹了各自的代表自動化工具。在分析現(xiàn)有檢測技術的基礎上,提出論文研究的檢測技術。2、C/C++程序的逆向技術研究熟悉反匯編工具IDA、OllyICE和Win32dsm,熟練掌握IDC腳本語言。研究在匯編代碼中識別程序流程,識別函數(shù)(庫函數(shù)和用戶編寫的函數(shù)),識別函數(shù)的參數(shù),識別函數(shù)的變量等技術,在研究識別庫函數(shù)的過程中提出了一個切實可行的算法。3、函數(shù)定位模塊的實現(xiàn)為了減少掃描匯編代碼得到的無用信息,提高檢測效率。根據(jù)COM組件結構特點,設計了一種自動識別接口函數(shù)的算法,并實現(xiàn)了一個函數(shù)定位模塊,可以將COM組件中用戶函數(shù)名和該函數(shù)在匯編代碼中的線性地址實現(xiàn)精確定位。4、COM組件危險函數(shù)庫的建立棧緩沖區(qū)溢出通常發(fā)生在字符串操作函數(shù)中,對于一般的C/C++程序,使用的是C字符串,所以危險函數(shù)庫主要是由常規(guī)字符串操作函數(shù)(如strcpy、strcat等)的子集構成。但COM組件中使用的是混合字符串。常規(guī)的字符串操作函數(shù)無法處理該類型字符串。因此必須建立適用COM組件的危險函數(shù)庫。5、棧溢出檢測算法的實現(xiàn)在詳細分析bugscam的基礎上,對其兩大核心函數(shù)SHeapBuffSize和函數(shù)StckBuffSize做了改進。利用IDA的腳本語言IDC實現(xiàn)了一個完整的棧溢出檢測算法SBOD-BC-CC。該算法可以根據(jù)危險函數(shù)的參數(shù)類型,識別不同的賦值操作指令,從而較準確的得到參數(shù)代表的緩沖區(qū)的分配長度和使用長度。依據(jù)緩沖區(qū)溢出的標準,判斷棧溢出漏洞。同時通過識別函數(shù)調(diào)用的操作指令,并結合函數(shù)定位模塊,可以提取用戶函數(shù)的依賴關系,以XML格式輸出的函數(shù)依賴關系為COM組件的后續(xù)分析提供良好的輸入信息。1.4論文結構第一章為緒論,主要介紹論文研究內(nèi)容的一些背景情況,國內(nèi)外研究現(xiàn)狀和論文的主要內(nèi)容。第二章首先介紹了COM組件,然后對緩沖區(qū)溢出的機理做了分析。重點介紹了棧緩沖區(qū)溢出的原因和分類,同時比較分析了現(xiàn)有的棧緩沖區(qū)溢出檢測技術和工具。第三章重點介紹靜態(tài)分析的理論基礎,研究如何從二進制代碼中識別高級語言的關鍵結構。介紹了在匯編代碼中識別函數(shù),識別函數(shù)參數(shù),識別函數(shù)返回值以及識別函數(shù)變量的技術。同時討論了COM組件危險函數(shù)庫的建立。第四章詳細介紹COM組件的棧緩沖區(qū)溢出漏洞檢測系統(tǒng)的設計與實現(xiàn)。第五章介紹對實現(xiàn)的原型系統(tǒng)進行的測試與分析。第六章是對本文工作的總結和展望。2COM組件及棧緩沖區(qū)溢出漏洞檢測方法基礎COM組件在受到廣泛應用的同時,自身的安全問題也日益暴露。其中,緩沖區(qū)溢出安全問題占了很大的比例。為了提高組件軟件的健壯性,針對COM組件緩沖區(qū)溢出漏洞檢測方法的研究具有十分重要的意義。本章首先簡單介紹了COM組件,然后詳細介紹了緩沖區(qū)溢出原理和目前成熟的檢測方法及工具。2.1COM組件COM組件是符合COM規(guī)范編寫的組件。COM是由Microsoft提出的組件標準,不僅提供了組件之間的接口標準,還引入了面向對象的思想。在COM標準中,對象是某個類的實例,稱為COM對象。接口是一組方法的集合,其方法也稱為接口成員函數(shù)。COM組件為COM對象提供活動空間,COM對象以COM接口方式提供服務。COM組件、COM對象和COM接口三者之間的關系如圖2.1所示[31]。圖2.1COM組件、COM對象和COM接口關系COM組件有兩種,一是進程內(nèi)組件,是一個DLL(動態(tài)鏈接庫)文件;二是進程外組件,是一個EXE(可執(zhí)行程序)文件。當組件的客戶程序調(diào)用組件的功能時,首先創(chuàng)建一個COM對象,然后通過該對象實現(xiàn)的COM接口調(diào)用所提供的服務。當所有的服務結束后,如果客戶程序不再使用該COM對象,那么應該釋放掉COM對象所占有的資源,包括對象本身。組件的內(nèi)部實現(xiàn)對客戶程序是完全隱藏的。2.1.1COM對象COM提供的是面向對象的組件模型,COM組件提供給客戶的是以對象形式封裝的實體??蛻舫绦蚺cCOM組件通過COM對象交互。COM規(guī)范采用了128位全局唯一標識符GUID來標識COM對象。與C++對象相比兩點不同:1、COM對象的數(shù)據(jù)成員的封裝以組件模塊為最終邊界,對于對象用戶是完全透明的;而C++對象的封裝特性相比之較差,可能對于用戶是可見的。2、COM對象的可重用性通過COM對象的包容和聚合來實現(xiàn);而C++對象的可重用性是通過繼承機制實現(xiàn)的。2.1.2COM接口1、接口的定義接口是一組邏輯上相關的函數(shù)集合。COM對象通過接口成員函數(shù)對外提供服務。接口的傳統(tǒng)命名前綴為I。COM模型中,客戶程序通過接口獲得對象的服務。每個接口由一個128位的全局唯一標識符IID來標識,客戶通過IID獲得接口的指針,再通過接口指針調(diào)用相應的接口成員函數(shù)。COM規(guī)范使用IDL(接口描述語言)來定義COM接口。MicrosoftVisualC++提供了MIDL工具,可以把IDL接口描述文件編譯成C/C++兼容的接口描述頭文件(.h),該文件可以被組件程序和客戶程序所使用。2、接口的內(nèi)存結構客戶程序用一個指向接口的指針來調(diào)用接口方法,接口指針又指向另一個指針(pVtable),pVtable指向接口函數(shù)表(vtable),接口函數(shù)表的每一項為4個字節(jié)的函數(shù)指針,每個函數(shù)指針與對象的函數(shù)實現(xiàn)連接起來(如圖2.2所示)。接口指向的虛函數(shù)表是確定的,即接口的成員函數(shù)個數(shù)和先后順序是不變的;對于每個成員函數(shù)來說,其參數(shù)和返回值也是確定的。不管什么語言,只要能支持這樣的接口內(nèi)存結構描述,就可以定義接口。圖2.2COM組件接口結構3、IUnknown接口COM定義的每一個接口都必須從IUnknown接口繼承。IUnknown接口提供了接口查詢和對象生存期管理的功能。如果客戶要對對象進行操作,則必須保證對象存在于內(nèi)存中;如果客戶對對象的操作完成,則必須把對象從內(nèi)存釋放掉。IUnknown接口采用引用計數(shù)方法,可以有效的控制對象的生存期。IUnknown接口包含3個方法:QueryInterface、AddRef、Release。QueryInterface用于完成接口之間的跳轉,查詢COM對象的其它接口;AddRef和Release用于對引用計數(shù)進行操作:當調(diào)用AddRef時,對象的引用計數(shù)加1;當調(diào)用Release時,對象的引用計數(shù)減1。當對象的引用計數(shù)為0時,對象可以自己釋放自己。2.2緩沖區(qū)溢出原理除了人為因素外,緩沖區(qū)溢出主要源于現(xiàn)有系統(tǒng)中的進程內(nèi)存分配和結構布局,非類型安全語言的使用以及危險函數(shù)的使用等問題。2.2.1緩沖區(qū)溢出機理緩沖區(qū)溢出是向一個緩沖區(qū)填充超過它處理能力的數(shù)據(jù)所造成的結果[9]。危險函數(shù)是指可能導致緩沖區(qū)溢出的C標準庫函數(shù)和系統(tǒng)API。廣義的“緩沖區(qū)”是指應用程序中定義的變量在進程中對應的內(nèi)存位置。在C程序中定義的全局變量或用關鍵字static定義的靜態(tài)變量,已被初始化的分配在數(shù)據(jù)區(qū),而未被初始化的則位于BSS區(qū);用內(nèi)存分配函數(shù)分配的變量位于堆區(qū);函數(shù)中的局部變量被分配在堆棧區(qū)。這些內(nèi)存區(qū)域的分配位置如圖2.3所示。圖2.3Windows內(nèi)存分布一方面,程序中為變量分配的緩沖區(qū)在進程對應的內(nèi)存結構中具有固定的大小,另一方面由于C語言不進行邊界檢查,在給變量直接或間接賦值時,如果不加以注意,變量有可能被分配到一個超過其對應緩沖區(qū)大小的數(shù)據(jù),導致緩沖區(qū)溢出。當程序試圖訪問已分配的緩沖區(qū)以外的內(nèi)存時,將出現(xiàn)下列三種情況之一:一是內(nèi)存不存在,系統(tǒng)報錯;二是系統(tǒng)提供了內(nèi)存保護的功能,報段錯或保護錯;三是允許訪問,但將覆蓋緩沖區(qū)一側的內(nèi)容,輕則留下安全隱患,成為日后被入侵者攻擊的弱點,嚴重的會導致系統(tǒng)崩潰。被溢出的緩沖區(qū)若位于堆棧段,則稱為堆棧溢出,若位于data、bss、heap段,則稱作堆溢出。本文設計的漏洞檢測系統(tǒng)主要針對的是棧溢出漏洞。1、堆棧定義堆棧是一塊保存數(shù)據(jù)的連續(xù)內(nèi)存。一個名為堆棧指針(ESP)的寄存器指向堆棧的頂部。堆棧的底部在一個固定的地址。堆棧有兩個重要的操作PUSH和POP,分別實現(xiàn)向堆棧中添加元素和從中移去元素。2、堆棧幀堆棧由邏輯堆棧幀組成。當調(diào)用函數(shù)時邏輯堆棧幀被壓入棧中,當函數(shù)返回時邏輯堆棧幀被從堆棧中彈出。堆棧幀包括函數(shù)的參數(shù),函數(shù)的局部變量,以及恢復前一個堆棧幀所需要的數(shù)據(jù),其中包括在函數(shù)調(diào)用時指令指針(EIP)的值。3、堆棧增長方式堆棧既可以向下增長(低端地址)也可以向上增長,這依賴于具體的實現(xiàn)。在很多計算機的實現(xiàn)方式中,包括Intel,Motorola等處理器,堆棧是向下增長的。堆棧指針(ESP)也是依賴于具體實現(xiàn)的。它可以指向堆棧的最后地址,或者指向堆棧之后的下一個空閑可用地址。本文中,堆棧是向下增長,ESP指向堆棧的最后地址。4、棧溢出的原理函數(shù)調(diào)用時的堆棧分配過程中,非靜態(tài)局部變量緩沖區(qū)的分配和填充不是同時進行的,并且依據(jù)不同的標準:局部變量緩沖區(qū)的分配是依據(jù)局部變量的聲明,而填充則是依據(jù)其實際被賦予的值。因此這個過程中就出現(xiàn)了安全漏洞。當對局部變量填充的值長度超出分配的值長度,而程序中又缺乏邊界檢查機制時,數(shù)據(jù)就會繼續(xù)向棧底寫入,相繼覆蓋其他變量的緩沖區(qū)、EBP和EIP等,如圖2.4所示。EBP是定位函數(shù)形參和局部變量的標桿,EIP決定函數(shù)調(diào)用結束后的返回地址,二者一旦出錯輕則使程序運行不正常,重則出錯終止,甚至被他人取得程序的運行權進而控制本機。攻擊者只要在程序運行時傳送給它一個足夠大的參數(shù),就可以在返回地址EIP中填入一個攻擊者希望程序轉向的任意內(nèi)存地址,從而控制了程序的運行權。這也就是基于堆棧的緩沖區(qū)溢出攻擊的原理。2.2.2棧緩沖區(qū)溢出分類1、根據(jù)溢出破壞的內(nèi)容分類JohnWilander和MariamKamkar[32]根據(jù)棧溢出所破壞的內(nèi)容將棧溢出分為六種類型:(1)破壞函數(shù)返回地址;(2)破壞原棧幀基指針;(3)破壞作為變量的函數(shù)指針;(4)破壞作為函數(shù)參數(shù)的函數(shù)指針;(5)破壞作為變量的longjmp緩沖區(qū);(6)破壞作為函數(shù)參數(shù)的longjmp緩沖區(qū);在C語言中包含了一個簡單的檢驗/恢復系統(tǒng),稱為setjmp/longjmp。頭文件<setjmp.h>中聲明了這些函數(shù),并且定義了jmp_buf數(shù)據(jù)類型。setjmp(jmp_bufj)必須首先被調(diào)用,它表示“使用變量j記錄現(xiàn)在的位置,函數(shù)返回0”。longjmp(jmp_bufj,inti)可以接著被調(diào)用,根據(jù)jmp_buf結構中保存的程序現(xiàn)場實現(xiàn)跳轉。jmp_buf結構存放了程序當前寄存器的值,以確保使用longjmp后可以跳回到該執(zhí)行點上繼續(xù)執(zhí)行。VC6.0編譯器對其在X86下的定義如圖2.5所示。圖2.4函數(shù)調(diào)用時的棧幀圖2.5jmp_buf數(shù)據(jù)結構然而,如果能在longjmp函數(shù)執(zhí)行以前覆蓋掉jmp_buf,就能重寫寄存器EIP的值,當longjmp函數(shù)恢復保存的堆棧棧幀后,程序就可能跳到指定的地方去執(zhí)行。類似函數(shù)指針,longjmp緩沖區(qū)能夠指向任何地方,所以攻擊者所要做的就是找到一個可供溢出的緩沖區(qū)。2、根據(jù)溢出的方向分類根據(jù)棧溢出的方向可以分為“棧上溢”和“棧下溢”,比如固定大小的緩沖區(qū)的正負越界操作就會發(fā)生棧上溢和棧下溢。3、根據(jù)溢出改寫數(shù)據(jù)的方式分類根據(jù)棧溢出時對數(shù)據(jù)的改寫方式是否連續(xù)[24],可以分為連續(xù)的棧溢出和不連續(xù)的棧溢出,目前的大多數(shù)檢測工具都只能檢測連續(xù)的棧溢出。 2.3棧緩沖區(qū)溢出檢測技術目前,在緩沖區(qū)溢出檢測技術研究方面,國外的研究比較深入,而國內(nèi)的研究尚處于起步階段[33]??傮w上,棧緩沖區(qū)溢出的檢測技術可分為兩類:靜態(tài)檢測技術和動態(tài)檢測技術。靜態(tài)檢測技術指的是不依賴于程序的運行檢測出棧溢出漏洞的技術。靜態(tài)檢測一般發(fā)生在編譯前和編譯后,分為基于源代碼的靜態(tài)檢測和基于二進制代碼的靜態(tài)檢測。基于源代碼的靜態(tài)檢測技術是目前研究相對較多的一個分支?;诙M制代碼的靜態(tài)檢測技術的研究目前比較少見,其本質(zhì)也是基于“源碼”的檢測技術。動態(tài)檢測技術指的是檢測過程必須依賴于程序的運行,一般需要在程序中插入或者修改一些代碼來檢測棧溢出漏洞。動態(tài)檢測可分為基于源代碼的動態(tài)檢測和基于二進制代碼的動態(tài)檢測。基于源代碼的動態(tài)檢測可通過擴展編譯器和直接修改源代碼來完成溢出漏洞的檢測?;诙M制代碼的動態(tài)檢測通過靜態(tài)或動態(tài)修改二進制代碼和采用“黑盒測試”的方法實現(xiàn)棧溢出漏洞檢測。當前棧溢出檢測技術的總結如圖2.6所示。圖2.6現(xiàn)有棧溢出檢測技術總結2.3.1基于源代碼的靜態(tài)檢測技術基于源代碼的靜態(tài)檢測技術是目前研究相對較多的一個分支,通過基于源代碼的詞法分析、模型化、標記驅動、語法分析等靜態(tài)分析技術完成棧溢出漏洞的檢測[34]。1、詞法分析該技術的代表檢測工具是ITS4。ITS4是最早的以命令行方式工作在Linux和Unix環(huán)境中的一種靜態(tài)掃描C/C++源代碼中安全漏洞的簡單工具。ITS4構造了一種具有安全威脅模型結構的數(shù)據(jù)庫,然后使用詞法分析技術對源碼進行對應模式的搜索匹配。它可以發(fā)現(xiàn)一些最普遍的安全漏洞。2001年發(fā)布的基于Python語言開發(fā)的用來安全審查C/C++語言程序的工具FlawFinder[35]和同年發(fā)布的RATS[35]與ITS4的工作原理相同,都是先建立漏洞數(shù)據(jù)庫,然后通過對源程序進行詞法分析進行模式匹配,找到潛在的漏洞。這類檢測技術的特點是實現(xiàn)簡單、算法效率高,但是由于沒有考慮到語法和語義層次的信息,容易出現(xiàn)漏報和錯報問題。2、模型化可以將緩沖區(qū)溢出檢測形式化為整數(shù)限制求解問題。代表檢測工具是BOON。BOON(BufferOverrunDetection)是加州伯克利大學的DavidWagner博士在其博士論文中實現(xiàn)的原型系統(tǒng),將檢測緩沖區(qū)溢出問題轉化為整數(shù)范圍分析問題。BOON不檢測格式化字符串漏洞的掃描,而且檢測結果存在大量的誤報和錯報,但作者明確表示不再有更新的版本,所以近年來在這方面并無新的研究進展發(fā)布。3、標記驅動該技術的代表檢測工具是Splint。LCLint是DavidEvans等人在Lint工具的基礎上開發(fā)的一種使用規(guī)范來檢查代碼安全性的工具。2002年一月初LCLint改名為Splint(SecureProgrammingLint),專門檢測程序的安全問題。Splint要求程序員在C源碼中按照固定的注釋格式/*@...@*/加入標記,這些標記被當作規(guī)格屬性,根據(jù)標記在每個函數(shù)中增加事先條件和事后條件(事先條件是指函數(shù)參數(shù)必須滿足指定的限制,事后條件保證函數(shù)結果滿足一些限制要求),然后利用詞法分析等技術驗證標記的限制條件與程序是否一致。Splint不能很好的處理指針的運算。使用標記驅動技術的另一成熟檢測工具是NuritDor[36]等開發(fā)了一種名為CSSV的緩沖區(qū)檢測器的原型系統(tǒng),其創(chuàng)新處在于引入契約的概念。4、語法分析YichenXie等開發(fā)了一種名為ARCHER的檢測工具。ARCHER的核心處理過程大致如下:首先對源碼進行語法分析,生成抽象語法樹AST,然后利用工具構造相應的程序流程控制圖,在其上進行路徑敏感分析。在對Sendmail、Linux等一些開放源碼的系統(tǒng)測試中,ARCHER取得了不錯的效果。除了上述幾種工具外,VinodCanapathy[37]等人提出一種基于線性規(guī)劃和靜態(tài)分析理論的輕量級別檢測算法,該算法首先使用工具軟件生成一些指針相關的信息和抽象語法樹,然后生成一些線性約束,并利用線性規(guī)劃的思想來完成區(qū)間分析,檢測可能出現(xiàn)的溢出漏洞。該算法主要圍繞著商業(yè)工具軟件Codesurfer來實現(xiàn)的。2.3.2基于源代碼的動態(tài)檢測技術基于源代碼的動態(tài)檢測技術一般需要在程序運行之前在程序中插入或者修改一些指令來檢測棧溢出。根據(jù)插入代碼方式的不同,可分為擴展編譯器和直接修改源碼的檢測技術。1、擴展編譯器擴展編譯器的技術是指對現(xiàn)有的編譯器進行擴展,通過編譯器收集棧的信息,增加緩沖區(qū)邊界信息并插入邊界檢查代碼,以實現(xiàn)對棧溢出的檢測。根據(jù)棧溢出檢測方式的不同,目前常用的兩種方法是基于“canary”的邊界檢查和數(shù)組邊界檢查。(1)基于“canary”的邊界檢查基于“canary”值的邊界檢測主要是由Cowan提出來的一種檢測策略。目前該策略的實現(xiàn)工具StackGuard受到了廣泛應用。StackGuard是一個GCC編譯器的補丁,援引StackGuard作者的話,StackGuard是一種“用輕微的性能損失來消除堆棧溢出問題的編譯器技術”。StackGuard主要是在內(nèi)存中的返回地址及緩沖區(qū)之間插入一個“canary”值(一個單字),這樣函數(shù)調(diào)用時的棧幀如圖2.7所示。圖2.7StackGuard作用下的函數(shù)棧幀布局如果試圖用溢出的方法修改返回地址,就會破壞“canary”值。當函數(shù)返回時,發(fā)現(xiàn)“canary”值被改變了,就證明可能有人試圖進行緩沖區(qū)溢出攻擊,程序會立刻響應,發(fā)送一個警告消息,然后停止工作。為防止攻擊者構造“canary”,StackGuard選用“終止符”和“隨機數(shù)”來作為“檢舉字”的值。但由于“檢舉字”所在的位置是固定的,所以可能通過指針跳轉被繞過。StackGuard的這個缺點被MariuszWoloszyn發(fā)現(xiàn)[38],并在Bulba和Kil3er的論文“BypassingStackGuardandStackShield”中公布。StackGuard對大多數(shù)返回地址溢出攻擊非常有效,但是對于堆攻擊,函數(shù)指針攻擊,長跳轉緩沖區(qū)攻擊,堆棧幀指針溢出攻擊以及格式化字符串攻擊無法做出防御。后來又有很多工具在StackGuard的基礎上進行了擴展,比如由Vendicator開發(fā)的基于Linux和IntelI386架構的GCC編譯器補丁程序StackShield[38]、由IBM日本研究院的HiroakiEtoh和KunikazuYoda實現(xiàn)的編譯器保護技術Propolice[39]和編譯器補丁PointGuard[38]等,但其基本原理都是一樣的,只不過在局部進行了優(yōu)化而已。(2)數(shù)組邊界檢查緩沖區(qū)溢出的原因是C語言并不檢查緩沖區(qū)的邊界,所以數(shù)組邊界檢查能防止所有的緩沖區(qū)溢出的產(chǎn)生和攻擊。為了實現(xiàn)數(shù)組邊界檢查,應當檢查對數(shù)組的所有讀寫操作以確保對數(shù)組的操作在正確的范圍內(nèi)。RichardJones和PaulKelly開發(fā)了一個GCC補丁[40],對C語言進行完全的數(shù)組上下界檢查。該技術采用了一種新的概念“指示目標”(referentobject),用它來代表指針所指向的對象的各種特征:大小、位置等。所有的“指示目標”存在一個全局表,而對堆空間的操作用另一個結構來記錄。利用這些信息,在使用指針或進行指針運算的時候即可進行邊界檢查。CRED(TheCRangeErrorDetector)[41]是對Jones和Kelly方法的擴展。CRED擴展了“指示目標”內(nèi)涵,允許指針指示的目標地址超出緩沖區(qū)的邊界。這種方法在具有大量指針和數(shù)組運算的程序中會帶來很大的性能損失,因為每次指針和數(shù)組的訪問都要進行檢查。2、修改源代碼實際應用中,直接修改源代碼不是一種合理的行為,因為涉及到大量的人力勞動。大多數(shù)棧溢出檢測工具都是從編譯器的角度進行靜態(tài)插裝,但并不是所有的思想都能通過修改編譯器實現(xiàn)的,美國哥倫比亞大學的Sidiroglou、Giovanidis和Keromytis提出的“以堆替?!钡募夹g[42],即DYBOC(DynamicBufferOverflowContainment)技術和EricHaugh、MattBishop提出的修改源代碼的STOBO工具(SystematicTestingofBufferOverflows)通過修改源代碼體現(xiàn)出了非常有意義的棧溢出的檢測思想。2.3.3基于二進制代碼的靜態(tài)檢測技術基于二進制代碼的靜態(tài)檢測技術的研究仍然比較少見,目前典型的研究是通過一些反匯編工具對二進制代碼進行處理,得到匯編代碼,然后再依賴一些源代碼靜態(tài)檢測技術在匯編代碼的基礎上進行處理。在2001年的BlackHat大會上,HalVarFlake做了名為“AuditingClosed-SourceSoftware—Usingreverseengineeringinasecuritycontext”的報告,介紹了利用IDA的腳本語言IDC識別漏洞的方法,并介紹了bugscam工具。bugscam是一個基于IDAProIDC腳本機制、輕量級的漏洞分析工具。bugscam并不能處理所有的壓棧指令,在很多情況下不能得到緩沖區(qū)的偏移地址,也就不能算出緩沖區(qū)的大小,更不能發(fā)現(xiàn)緩沖區(qū)溢出漏洞。反匯編工具IDAPro可以支持多種芯片和多種操作系統(tǒng)的可執(zhí)行文件格式,對沒有調(diào)試符號的文件,能正確地確定所引入的庫和引進調(diào)用的庫函數(shù),并能標注函數(shù)的參數(shù),bugscam就是利用這一點,利用IDA識別出來的函數(shù)參數(shù),間接得到參數(shù)的緩沖區(qū)大小。其核心思想是通過兩個相鄰參數(shù)的偏移量之差得到參數(shù)的緩沖區(qū)大小。下面以棧中緩沖區(qū)分配為例,如圖2.8所示。圖2.8棧中緩沖區(qū)分配簡略圖比如函數(shù)內(nèi)部有一個leaeax,[ebp-808h]形式的指令,那么可以判斷ebp-808就是一個棧分配區(qū)域。這樣也就確定了函數(shù)棧空間有一個var_808的變量,IDA可自動識別出棧中變量。如果要確定dst變量的大小,那么就是1008h-808h=800h。2.3.4基于二進制代碼的動態(tài)檢測技術1、靜態(tài)修改二進制代碼目前這類技術可以分為以下兩類:一類是通過靜態(tài)分析程序生成的二進制代碼,找出相關函數(shù)的入口點和退出點,然后修改二進制代碼。在相關函數(shù)的入口點和退出點插入一些語句來檢測棧的運行情況。該技術的典型代表工具是由Prasad和Chiueh提出來的RAD工具。其檢測方法是在函數(shù)調(diào)用產(chǎn)生新堆棧時,將函數(shù)返回地址副本保存到返回地址倉庫RAR中,在函數(shù)返回時檢查副本和返回地址的一致性,依此檢測棧溢出。另一類工具是通過對帶調(diào)式信息的程序提取出全局和局部緩沖區(qū)的大小信息,并把這些信息組織后,重新寫回到二進制代碼文件中,然后通過監(jiān)控內(nèi)存操作庫函數(shù)對這些全局和局部緩沖區(qū)的操作行為來檢測是否發(fā)生了棧溢出。該方法的典型是印度理工的KumarAvijit、PrateekGupta和DeepakGupaa提出來的兩個工具:TIED和Libsafe-Plus[43]。2、動態(tài)修改二進制代碼這類技術是在程序運行過程中對二進制代碼進行修改,在函數(shù)的出入口插入一些指令來獲取棧的運行情況。由于動態(tài)修改二進制代碼技術可以不依賴源碼的支持,因此對此技術的研究比較熱。目前比較有代表性的技術為ArashBaratloo、TimothyTsai和NavjotSingh開發(fā)的libverify以及印度理工的SuhasGupta、PranayPratap、HuzurSaran和S.Arun-Kumar提出來的函數(shù)出入口替換技術[44]。3、黑盒測試該技術是通過自動化工具生成測試數(shù)據(jù),通過仿真攻擊狀態(tài)下應用程序的執(zhí)行狀態(tài)來判斷緩沖區(qū)溢出漏洞位置。該技術的代表是由威斯康星大學的BartonR.Milier等開發(fā)的Fuzz工具。該工具通過隨機生成不同長度的字符串對目標代碼的執(zhí)行進行測試。Fuzz是可定制的,用戶可以通過設置不同的選項來對軟件進行不同類型的測試。即可用它來發(fā)現(xiàn)SQL注入、格式串、緩沖區(qū)溢出、目錄遍歷和其它漏洞。這種工具可以發(fā)現(xiàn)一些軟件中的緩沖區(qū)溢出漏洞,但是這種對潛在漏洞的搜索是強力性和無策略的,而執(zhí)行路徑一般不可能完全估計到,因此存在潛在漏洞的可能性。另外,F(xiàn)uzz執(zhí)行時間很難估計,而且該工具給出的結果僅僅是一種判斷,漏洞在源碼中的具體位置不能確定。2.3.5現(xiàn)有緩沖區(qū)溢出檢測技術總結1、靜態(tài)檢測技術(1)由于在靜態(tài)環(huán)境下很難獲得精確的棧的使用情況和遞歸調(diào)用的次數(shù),而且靜態(tài)工具也難以分析多線程環(huán)境,所以靜態(tài)工具只能檢測部分棧溢出漏洞;(2)靜態(tài)分析工具的分析結果通常會包含大量的錯誤信息,由此帶來很多額外的人工檢查工作;(3)相對動態(tài)檢測技術而言,靜態(tài)檢測技術不需要實際發(fā)生棧溢出,而動態(tài)檢測技術只有在棧溢出發(fā)生時才能檢測到,由于測試代碼不太可能對所有的程序進行完全的覆蓋測試,因此動態(tài)情況下有些棧溢出的代碼實際上不能被觸發(fā),而靜態(tài)分析工具有時能夠檢測出這樣的一些棧溢出情況。所以靜態(tài)檢測技術不能作為檢測棧緩沖區(qū)溢出的主要手段,但作為其他方法的補充還是很有價值的。2、動態(tài)檢測技術(1)基于源碼的動態(tài)檢測技術必須依賴源代碼[45]。目前主要有兩種實現(xiàn)方法:擴展編譯器的方法,實現(xiàn)難度比較高,需要重新編譯源代碼,但是能夠提供比較完整的棧操作的信息和函數(shù)的信息;直接修改源碼的方法,實現(xiàn)難度低,但可能會帶來一些誤操作,而且需要大量的人力。(2)基于二進制碼的動態(tài)檢測技術主要有三種實現(xiàn)方法:靜態(tài)修改二進制碼的方法,基于反匯編的修改方法容易出現(xiàn)反匯編的錯誤,導致檢測效果大打折扣,甚至可能造成代碼的運行不正常,基于調(diào)試信息的修改方法實際上還是要依賴于源代碼;動態(tài)修改二進制碼的方法,libverify不能檢測出靜態(tài)鏈接代碼的棧溢出錯誤,而函數(shù)出入口替換技術目前只能用在特殊編譯模式下的代碼。(3)動態(tài)檢測技術在檢測到溢出時,通常是終止程序的執(zhí)行,這雖然防范了攻擊,但同時帶來了拒絕服務攻擊;(4)動態(tài)檢測技術最主要的缺點是只能檢測出真正發(fā)生動態(tài)溢出的代碼,而實際中只有極少量的測試用例可以觸發(fā)棧溢出,因此動態(tài)檢測技術并不能檢測出所有可能的棧溢出。所以,動態(tài)檢測技術作為棧緩沖區(qū)溢出檢測的主流技術,已經(jīng)可以檢測出大量的棧溢出漏洞,而且自動化程度高,但仍不能全面的檢測出漏洞。因此軟件開發(fā)人員應同時運用靜態(tài)檢測技術和動態(tài)檢測技術,在軟件編碼和運行階段分別開展檢測,從而提高對棧溢出漏洞的檢測效果。2.4小結本章首先介紹了COM組件,然后對緩沖區(qū)溢出的機理做了分析。重點介紹了棧緩沖區(qū)溢出的原因和分類,同時比較分析了現(xiàn)有的棧緩沖區(qū)溢出檢測技術和工具。下一章將介紹COM組件棧緩沖區(qū)溢出漏洞檢測技術。3COM組件棧緩沖區(qū)溢出漏洞檢測技術造成緩沖區(qū)溢出的根本原因是非類型安全語言(如C++/C)沒有對內(nèi)存訪問進行邊界檢查。換言之,出現(xiàn)緩沖區(qū)溢出問題的程序大都是C++/C語言編寫的程序。因此論文中研究的COM組件棧緩沖區(qū)溢出漏洞檢測技術的研究對象默認是借助VisualC++6.0的ATL(ActiveTemplateLibrary)開發(fā)得來的。鑒于COM組件是符合特定規(guī)范的C++程序,對COM組件緩沖區(qū)溢出漏洞的檢測可以建立在對普通程序(C/C++程序)緩沖區(qū)溢出漏洞檢測的基礎上,再結合COM組件的特性,達到預期的研究目標。論文中提出的檢測技術和目前已有的基于二進制代碼的靜態(tài)檢測技術的不同點在于:1、對象不同基于二進制代碼的靜態(tài)檢測技術的研究對象是一般意義的C/C++程序,但論文中的檢測技術是針對源碼未知的COM組件。雖然COM組件在實際應用中大部分是通過C++開發(fā)的,但是因為其遵循COM規(guī)范,所以在結構和特性上與一般的C/C++程序差別很大,這也就造成了檢測技術的不同。2、危險函數(shù)庫不同一般的C/C++程序中使用的是C類型字符串,以‘\0’截斷符標識字符串的結束,這類型字符串的操作函數(shù)都是常規(guī)的字符串操作函數(shù),如strcpy,strcat等,它們中的一些可能導致緩沖區(qū)溢出問題的操作函數(shù)構成危險函數(shù)庫。但是COM組件使用的是混合類型字符串,這就導致了常規(guī)字符串操作函數(shù)的失效,需要根據(jù)COM組件的字符串操作函數(shù)建立特殊的危險函數(shù)庫。這是進行緩沖區(qū)溢出檢測的首要工作。3、核心算法不同論文中提出的系統(tǒng),是基于IDA的IDC腳本語言開發(fā)的。通過對組件的接口分析,獲取組件接口函數(shù)的線性地址,根據(jù)該線性地址準確定位至待測的組件函數(shù)。在檢測范圍內(nèi)定位危險函數(shù),識別危險函數(shù)的參數(shù)類型,根據(jù)不同類型獲取參數(shù)賦值操作從而計算參數(shù)代表的緩沖區(qū)的分配長度和使用長度,進而進行溢出漏洞判斷。同時提取函數(shù)依賴關系圖,將以上信息以XML的格式輸出,為COM組件的后續(xù)分析提供良好的輸入信息。識別匯編代碼中與棧溢出相關的語法成分和建立適用COM組件的危險函數(shù)庫是論文檢測的關鍵技術。本章將研究如何從匯編代碼中識別高級語言的關鍵結構和建立COM組件危險函數(shù)庫。3.1函數(shù)的識別3.1.1一般函數(shù)的識別函數(shù)是過程設計語言與面向對象設計語言的主要結構單元,因而,對匯編代碼的分析通常是從識別函數(shù)以及傳遞給它們的參數(shù)開始[46]。在絕大多數(shù)情況下,編譯器都使用CALL和RET專用機器指令來調(diào)用函數(shù)與返回到調(diào)用位置。CALL指令將緊接在它之后的指令的地址壓入堆棧的頂部,而RET指令則將該地址彈出來,并把控制傳遞給它。如果被調(diào)函數(shù)平衡堆棧,那么被調(diào)函數(shù)通過RETn指令結束調(diào)用,其中,n是在函數(shù)返回時從堆棧彈出的字節(jié)數(shù)。如果調(diào)用函數(shù)平衡堆棧,那么被調(diào)用函數(shù)通過RET指令結束調(diào)用。在下文中,除非特別聲明,否則都認為函數(shù)通過RET指令結束調(diào)用。絕大多數(shù)非優(yōu)化編譯器會在函數(shù)的開頭放入稱為起始標志的代碼,起始標志如圖3.1所示。圖3.1匯編代碼中函數(shù)的起始標志當打開被調(diào)用函數(shù)的棧幀時,首先保存前棧幀的EBP,然后棧頂指針寄存器(ESP)的當前值復制到EBP之中,即打開堆棧頁面,并且ESP的值會隨著為該局部變量分配的內(nèi)存塊大小而不斷減少,最后為函數(shù)局部變量分配內(nèi)存空間。(PUSHEBP)(MOVEBP,ESP)(SUBESP,XX)序列可用于找到分析文件中的所有函數(shù),包括那些沒有對它們進行直接引用的函數(shù)。但是對于優(yōu)化編譯器,不需要分配一個專用的寄存器(EBP)進行局部變量的尋址,通過使用ESP寄存器就可實現(xiàn)對局部變量進行尋址。在這種情況下,優(yōu)化函數(shù)的起始標志僅僅含有一條SUBESP,XXX指令,在這種情況下函數(shù)起始序列太短而不能當作辨認函數(shù)的標記。在“生命”的結束時期,函數(shù)通過向下(高端地址)移動堆棧指針而關閉堆棧頁面,然后恢復EBP的先前值(僅當編譯器通過EBP進行尋址時)。函數(shù)的結束標志如圖3.2所示。圖3.2匯編代碼中函數(shù)的結束標志(MOVESP,EBP)和(POPEBP)指令不需要緊接在一起,它們之間可以用其他指令進行分隔。因而,使用上下文搜索對于尋找結束標志是不合適的。但是不管函數(shù)遵循何種調(diào)用約定,函數(shù)均通過RET指令或RETn指令終止調(diào)用。所以RET對于表示函數(shù)的結束是足夠的,但并不是任何結束標志都是函數(shù)的結束,如果函數(shù)體內(nèi)有多個RET操作符(一般情況就是這樣的),編譯器通常為每個RET操作符生成一個結束標志,所以需要檢查在結束標志之后是否有新的結束標志,或者是否后面還有舊函數(shù)的代碼。識別函數(shù)就是把匯編代碼中出現(xiàn)的函數(shù)代碼范圍識別出來。在匯編代碼中,函數(shù)的起始地址可以通過起始標志確定或通過CALL指令的操作數(shù)直接或間接定位。而函數(shù)最后都是以“ret”的形式返回的,但并不是出現(xiàn)ret的地方就是函數(shù)結束的地方。所以根據(jù)程序本身的流程,可以得到函數(shù)代碼段識別算法3.1。算法3.1函數(shù)代碼段識別算法輸入:函數(shù)模塊的起始地址addr_start輸出:函數(shù)模塊的結束地址addr_end算法:addr_scan=addr_start;Begin:確定從addr_scan開始的第一條ret指令的地址addr_end;if(addr_start到addr_end之間不存在跳轉指令)returnaddr_end;取出addr_start到addr_end之間所有跳轉地址的最大值addr_jmp;if(addr_jmp<=addr_end)returnaddr_end;addr_scan=addr_jmp;gotoBegin;該算法的時間復雜度為O(n),其中n代表函數(shù)匯編代碼的規(guī)模。3.1.2庫函數(shù)的識別成熟的軟件開發(fā)中會大量使用標準庫函數(shù),以增強軟件的健壯性。但對于庫函數(shù)的識別核心是識別庫函數(shù)名以明確其功能。對于一些標準庫函數(shù),可通過一些成熟的反匯編工具實現(xiàn)快速自動識別。IDA的一項革命性的工作是庫文件快速識別與鑒定技術(FastLibraryIdentificationandRecognitionTechnology,簡稱FLIRT)。這項技術使IDA能在一系列編譯器的標準庫文件里自動找出調(diào)用的函數(shù),使反匯編清單清晰明了。但IDA一般情況下不能識別出所有的庫函數(shù)。而庫函數(shù)的識別對于逆向工作的質(zhì)量和效率都有很大的影響。這時,可以考慮通過用庫函數(shù)的機器碼匹配靜態(tài)庫文件的機器碼的方法來識別庫函數(shù)[47,48],如圖3.3所示。1、分析識別編譯器識別出執(zhí)行程序對應的源碼所用的編譯器,這是能否正確識別靜態(tài)庫的前提??梢越柚幾g器編譯所產(chǎn)生的其他特殊信息,可以很好的達到識別執(zhí)行程序所用編譯器的目的。圖3.3通過機器碼匹配識別庫函數(shù)的方法流程2、建立函數(shù)庫當根據(jù)PE文件識別出該PE文件所用的編譯器時,選取該編譯器常用的lib文件。由于這些庫文件都是COFF結構形式的,所以可以通過分析COFF文件格式得到庫文件中的所有函數(shù)的函數(shù)名、函數(shù)代碼等信息??梢圆捎肰isualC++6.0自帶的工具dumpbin獲取上述信息,如圖3.4所示,是從libc.lib中獲取的printf函數(shù)的匯編代碼。因為編譯器在編譯時會采取重定位操作,并且函數(shù)的參數(shù)會有變動,所以匯編指令的操作數(shù)會改變,但這不影響函數(shù)模塊的匹配。圖3.4從libc.lib中獲取的printf函數(shù)的匯編代碼3、匹配算法該匹配算法就是從靜態(tài)庫建立起來的函數(shù)庫中找出一個函數(shù)與識別出的函數(shù)代碼的操作碼序列相等,關于該算法,已有比較成熟的研究,詳看參考文獻[49]中所提到的算法。對于最后的匹配結果,可能有多個函數(shù)結果集滿足要求,通過實際的反編譯過程發(fā)現(xiàn),這種比例只占整個靜態(tài)庫中1%-3%左右,可以基本上滿足需求。3.2參數(shù)的識別區(qū)分函數(shù)的參數(shù)是對程序的反匯編結果進行分析的關鍵步驟。給函數(shù)傳遞參數(shù)的方式有三種:利用堆棧傳遞參數(shù)、利用寄存器傳遞參數(shù)以及通過全局變量進行隱含參數(shù)的傳遞。參數(shù)的正確識別取決于函數(shù)調(diào)用約定(CallingConvention)的判斷。調(diào)用約定決定以下內(nèi)容:函數(shù)參數(shù)的壓棧順序;由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧;產(chǎn)生函數(shù)修飾名的方法。函數(shù)調(diào)用有多種機制,每種機制都有自己的優(yōu)點和不足,并且它與使用的編程語言有關。本論文主要是針對C/C++程序的逆向分析,最常見的C++程序的調(diào)用約定有__cdecl、__stdcall、thiscall和__fastcall,如表3.1所示。論文中僅考慮參數(shù)通過堆棧傳遞,通過IDA的高效輔助,可以較準確的識別函數(shù)參數(shù)。但這同時也是本文實現(xiàn)的原型系統(tǒng)需要改進的地方。3.3函數(shù)的返回值函數(shù)的返回值通常是由return操作符返回的一個值。從匯編角度來看,主要有五種形式:1、使用return操作符從函數(shù)返回值(通過寄存器或者協(xié)處理器堆棧);2、通過參數(shù)按引用方式返回函數(shù)值;3、通過全局變量返回函數(shù)值;4、通過處理器標志返回函數(shù)值;5、通過堆從函數(shù)返回值。論文僅討論第一種情形。根據(jù)有關規(guī)范的規(guī)定,由return操作符返回的值放在EAX寄存器(在16位模式下是AX寄存器)之中。如果函數(shù)的處理結果超出了這個寄存器的位容量,那么該操作數(shù)的高32位會加載到EDX寄存器中(在16位模式下,高位字會加載到DX寄存器里面)。在大多數(shù)情況下,浮點型(float)結果是通過協(xié)處理器堆棧來返回的,這種值也可以通過EDX:EAX寄存器進行傳遞(在16位模式下是DX:AX寄存器)。如果返回一個含有幾百個字節(jié)的結構或者一個近似大小的對象,編譯器會在不告訴程序的情況下,給函數(shù)傳遞一個隱式參數(shù),這個指針指向保存的返回結果。具體的返回方式如表3.2所示。表3.2不同類型的函數(shù)返回值的返回方式類型(長度)返回方式1字節(jié)AL或者AX或者EAX寄存器2字節(jié)AX或者EAX寄存器4字節(jié)EAX寄存器8字節(jié)EDX:EAX寄存器浮點型協(xié)處理器堆?;蛘逧AX寄存器雙精度型協(xié)處理器堆棧或者EDX:EAX寄存器近指針EAX寄存器多于8字節(jié)引用方式的隱含參數(shù)表3.1C++程序常用調(diào)用約定比較特征\調(diào)用約定__cdecl__stdcall__fastcallthiscall進棧順序從右到左從右到左從右到左從右到左清理堆棧方式調(diào)用者被調(diào)用者被調(diào)用者被調(diào)用者函數(shù)名修飾約定C編譯方式僅在輸出函數(shù)名前加上一個下劃線前綴。函數(shù)名前加上一個下劃線前綴,后面加上一個"@"符號和其參數(shù)的字節(jié)數(shù)。在輸出函數(shù)名前加上一個"@"符號,后面也是一個"@"符號和其參數(shù)的字節(jié)數(shù)。不適用C++編譯方式以“?”標識函數(shù)名的開始,后跟函數(shù)名,函數(shù)名后面以“@@YA”標識參數(shù)表的開始。以“?”標識函數(shù)名的開始,后跟函數(shù)名,函數(shù)名后面以“@@YG”標識參數(shù)表的開始。以“?”標識函數(shù)名的開始,后跟函數(shù)名,函數(shù)名后面以“@@YI”標識參數(shù)表的開始。差異較大特點C和C++程序的缺省調(diào)用規(guī)范多數(shù)WindowsAPI調(diào)用方式寄存器調(diào)用方式,通常是前兩個DWORD類型的參數(shù)或較小的參數(shù)使用ECX和EDX寄存器傳遞,其余參數(shù)按照從右向左的順序入棧C++類成員函數(shù)調(diào)用方式。類實例的this指針通過ECX寄存器傳遞3.4變量的識別1、局部變量函數(shù)的局部變量或存在于棧中,或存儲在寄存器中,但由于一些寄存器會自動用于某些操作,如EDI用于MOVSD,ECX用于需計數(shù)的指令等,所以在函數(shù)中使用寄存器保存局部變量相當少見,在本文中,僅考慮局部變量在堆棧中存儲的情況。一旦獲取CPU控制權,函數(shù)就打開堆棧頁面(保存EBP寄存器以前的值,接著設置其值使它指向棧頂?shù)腅SP寄存器值相等)。局部變量位于EBP指向的地址的上方(也就是位于低端地址),而服務性數(shù)據(jù)(存儲的EBP寄存器值以及返回地址)同參數(shù)一樣位于它的下方(即位于高端地址)。局部變量與堆棧參數(shù)以相似的方式進行尋址。唯一不同的地方是,參數(shù)位于EBP的下方,而局部變量駐留在它的上方。參數(shù)相對于EBP地址具有正的偏移量,而局部變量的偏移量是負的。指向堆棧頁面的寄存器起到了一個柵欄的作用:函數(shù)參數(shù)位于它的一側,而局部變量位于另一側。如圖3.5所示。優(yōu)化編譯器能夠通過ESP寄存器直接對局部變量與參數(shù)進行尋址,從而將EBP寄存器騰出來進行更有用的工作。由此可見,[EBP-04]為第一個局部變量,[EBP+08]為第一個參數(shù),以此類推。圖3.5函數(shù)局部變量和參數(shù)的尋址方式初始化局部變量的方法有兩種:通過MOV指令為變量賦予必要的值,或者使用PUSH指令直接將值壓入堆棧。在大多數(shù)情況下,流行的編譯器使用MOV指令執(zhí)行初始化操作,而不合理的匯編器多使用PUSH指令,其目的往往在于誤導黑客起到保護的作用。2、全局變量在函數(shù)中判定全局變量很容易,全局變量通過直接對內(nèi)存進行尋址就可訪問。如:MOVEAX,[00401066]就表明函數(shù)在尋址全局變量,其中0x00401066是全局變量的地址。但判定變量的類型就像判定局部變量的類型,要知道函數(shù)是如何使用該變量。3、識別立即數(shù)的類型80x86微處理器系列支持三種類型的操作數(shù):立即數(shù)、寄存器與內(nèi)存。識別操作數(shù)的類型并不困難。如圖3.6所示。圖3.6識別操作數(shù)的實例說明80x86微處理器支持兩種內(nèi)存尋址模式:直接尋址方式與寄存器間接尋址方式。如果操作數(shù)是立即數(shù),則使用直接尋址方式。如果操作數(shù)是存儲在寄存器中的一個指針,那么尋址方式就是寄存器間接尋址方式,如圖3.7所示。圖3.780x86微處理器支持的內(nèi)存尋址模式為初始化寄存器指針,微處理器開發(fā)人員引入了一條專用指令LEAREG,[addr],它將計算地址表達式addr的值并把結果放入REG寄存器之中。LEA指令右邊的操作數(shù)總是表示一個近指針(LEA用于計算常量之和的情況除外),如圖3.8所示。近指針的內(nèi)部表示形式與取值相同的常數(shù)是一樣的。圖3.8LEA指令的說明程序中的立即數(shù),可以是常量,也可以是指針。對立即數(shù)的正確判斷對于程序的逆向分析作用很大。對立即數(shù)的錯誤判斷,很可能導致整個程序分析工作的失敗。要確定一個立即操作數(shù)的類型,關鍵在于分析立即數(shù)如何被使用。如果立即數(shù)是用于內(nèi)存操作數(shù)尋址的,那么它就是一個指針,否則,就是一個常量。存在兩種類型的指針:指向數(shù)據(jù)的指針與指向函數(shù)的指針。指向數(shù)據(jù)的指針用于從內(nèi)存單元中提取數(shù)值,這類指針出現(xiàn)在算術或者移動指令(如MOV、ADD、SUB等)之中。指向函數(shù)的指針用在間接調(diào)用指令(CALL)當中,并且偶爾用在間接跳轉調(diào)用指令當中。4、字符串在匯編代碼中識別字符串的一個難點是判定字符串的結束標志,據(jù)此,可以將字符串分為如下幾種類型:以‘\0’字符結尾的C字符串,以‘$’字符結尾的DOS字符串和以表示字符串長度的單字節(jié)、雙字節(jié)或者四字節(jié)開頭的Pascal字符串以及混合類型的字符串。具備C字符串和Pascal字符串特性的字符串稱為混合類型的字符串?;旌献址驗橛斜硎咀址L度的專用字段,所以使用該類型字符串的處理速度很快,并且能夠存放任何字符。同時,它為大量的C語言ASCIIZ字符串處理函數(shù)提供了兼容性。一般編程語言的庫函數(shù)在處理該字符串類型時,與處理Pascal字符串類型基本相同。在調(diào)用C庫函數(shù)時,編譯器傳遞指向字符串第一個字符的指針,而不是真正指向字符串結構開頭的指針。字符串的類型大致可以通過編譯器的類型來確定,而要精確的確定字符串的類型,就得分析處理算法。3.5COM組件危險函數(shù)庫建立1、COM使用的字符串類型COM規(guī)范的基本字符數(shù)據(jù)類型是OLECHAR,它是與平臺無關的字符表示法。在創(chuàng)建該字符集時,OLECHAR的基本數(shù)據(jù)類型隨操作系統(tǒng)的不同而不同。如今最流行的COM平臺是基于Win32API的,基于此,OLECHAR就是wchar_t的typedef。對于C++,Windows頭文件wtypes.h中定義如圖3.9所示:圖3.9wtypes.h中OLECHAR的定義 COM組件是運行在分布式環(huán)境中的,例如一個COM組件程序(DLL或EXE),組件使用者可能是在本機的某個進程內(nèi)加載組件(INPROC_SERVER),也可能是從另一個進程中調(diào)用組件的進程(LOCAL_SERVER);也可能是在不同的主機之間調(diào)用組件程序(REMOTE_SERVER)。這就要求COM字符串的長度被預先存儲好,以便當COM字符串在進程或者計算機之間被傳遞時,COM庫知道需要傳遞的數(shù)據(jù)?;谝陨蠈ψ址匦缘囊?,COM規(guī)范采用BSTR字符串數(shù)據(jù)類型。BSTR是混合類型的字符串,標準BSTR是一個有長度前綴和null結束符的OLECHAR數(shù)組。在C++中,BSTR實際上就是一個指向UNICODE字符串中首字符的32位指針,且BSTR向前的4個字節(jié)中,使用DWORD(雙字類型,4個字節(jié))保存字符串的字節(jié)長度(沒有含字符串的結束符‘\0’)。因此系統(tǒng)就能夠正確處理并傳送字符串到不同的地理位置。例如:輸入程序片段BSTRp=::SysAllocString(L"Hello,COM");斷點執(zhí)行,然后觀察p的內(nèi)存,如圖3.10所示。 圖3.10BSTR內(nèi)存結構2、BSTR的包裝類_bstr_t和CComBSTRCOM提供了兩個BSTR分配用的API:SysAllocString/SysReallocString。函數(shù)返回的指針指向BSTR的第一個字符。所有的BSTR都必須使SysFreeString()釋放,否則會造成內(nèi)存泄漏。但是存在很多BSTR封裝類可以實現(xiàn)內(nèi)存自動管理。_bstr_t是CRT提供的一個對BSTR的完整封裝類,實際上它隱藏了底層的BSTR。_bstr_t沒有訪問BSTR本身的操作符,所以一個_bstr_t類型的字符串不能被作為輸出參數(shù)傳給一個COM方法。CComBSTR是ATL中的BSTR封裝類,CComBSTR允許訪問底層的BSTR,可以傳遞一個CComBSTR對象給COM的方法。CComBSTR對象能夠自動的管理BSTR的內(nèi)存。3、導致棧緩沖區(qū)溢出的危險函數(shù)庫收集危險函數(shù)是檢測漏洞的關鍵工作。論文對一些可能導致棧緩沖區(qū)溢出的C標準庫函數(shù)和WindowsAPI進行分類總結,建立危險函數(shù)庫。(1)strcpy原型:externchar*strcpy(char*dest,char*src)原因:函數(shù)將源字符串復制到緩沖區(qū),如果src的長度大于dest,則導致溢出。類似函數(shù):wcscpy、lstrcpy、_tcspy和_mbscpy。(2)strcat原型:externchar*strcat(char*dest,char*src)原因:dest必須有足夠的空間來容納src的字符串,否則導致溢出。類似函數(shù):wcscat、lstrcat、_tcscat和_mbscat。(3)strncpy原型:externchar*strncpy(char*dest,char*src,intn)原因:不檢查目標緩沖區(qū)的長度,也不檢查null或其他無效的指針。如果源緩沖區(qū)不是以null結尾的,可能造成溢出。類似函數(shù):wcsncpy、lstrcpyn、_tcsncpy和_mbsnbcpy。(4)strncat原型:externchar*strncat(char*dest,char*src,intn)原因:dest必須有足夠的空間來容納src的字符串,否則導致溢出。類似函數(shù):wcsncat、_tcsncat和_mbsnbcat。(5)memcpy原型:externvoid*memcpy(void*dest,void*src,unsignedintcount)原因:不檢查目標緩沖區(qū)的長度,也不檢查null或其他無效的指針。如果源緩沖區(qū)不是以null結尾的,可能造成溢出。類似函數(shù):memccpy、memmove、movmem、bcopy。(6)memset原型:externvoid*memset(void*buffer,intc,intcount)原因:把buffer所指內(nèi)存區(qū)域的前count個字節(jié)設置成字符c,若count超過buffer的長度,則導致溢出。類似函數(shù):setmem。(7)strtrns原型:externchar*strtrns(constchar*str,constchar*old,constchar*new,char*result)原因:若str的長度大于result,則導致溢出。(8)streadd、strecpy原因:確保分配的目的地參數(shù)大小是源參數(shù)大小的四倍,否則導致溢出。(9)gets原型:externchar*gets(char*buffer)原因:它不檢查被拷貝的緩沖區(qū)大小。(10)fgets原型:char*fgets(char*string,intcount,FILE*stream)原因:將count個字符(自動包含null)拷貝至string中。不檢查目標緩沖區(qū)的長度,也不檢查null或其他無效的指針。如果源緩沖區(qū)不是以null結尾的,可能造成溢出。(11)scanf、sscanf、fscanf、vfscanf、vscanf、vsscanf、_tscanf、wscanf原因:類似gets,scanf、_tscanf、wscanf在使用%s格式化字符的時候,由于%s是沒有邊界的,因此都很難正確使用。相關函數(shù)的漏洞原因類似。(12)(v)sprintf、(v)snprintf

原型:intsprintf(char*string,constchar*format,...)intsnprintf(char*string,size_tcount,constchar*format,...)原因:當string所指緩沖區(qū)的長度過小不足以容納format所指緩沖區(qū)長度時,發(fā)生溢出。vsprintf()和vsnprintf()與前面所述的函數(shù)用法相同。它們的原型是:intvsprintf(char*string,constchar*format,va_listvarg)intvsnprintf(char*string,size_tcount,constchar*format,va_listvarg)(13)getc()、fgetc()、getchar()和read()原因:這些函數(shù)用于循環(huán)中,如果達到目標變量的最大值之后循環(huán)沒能適時地停止,函數(shù)就有可能讀入過多

溫馨提示

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

評論

0/150

提交評論