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

下載本文檔

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

文檔簡介

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

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

溫馨提示

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

評論

0/150

提交評論