




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
..緩沖區溢出攻擊詳細講解緩沖區溢出〔BufferOverflow是計算機安全領域內既經典而又古老的話題。隨著計算機系統安全性的加強,傳統的緩沖區溢出攻擊方式可能變得不再奏效,相應的介紹緩沖區溢出原理的資料也變得"大眾化"起來。其中看雪的《0day安全:軟件漏洞分析技術》一書將緩沖區溢出攻擊的原理闡述得簡潔明了。本文參考該書對緩沖區溢出原理的講解,并結合實際的代碼實例進行驗證。不過即便如此,完成一個簡單的溢出代碼也需要解決很多書中無法涉及的問題,尤其是面對較新的具有安全特性的編譯器——比如MS的VisualStudio2010。接下來,我們結合具體代碼,按照對緩沖區溢出原理的循序漸進地理解方式去挖掘緩沖區溢出背后的底層機制。一、代碼<=>數據顧名思義,緩沖區溢出的含義是為緩沖區提供了多于其存儲容量的數據,就像往杯子里倒入了過量的水一樣。通常情況下,緩沖區溢出的數據只會破壞程序數據,造成意外終止。但是如果有人精心構造溢出數據的內容,那么就有可能獲得系統的控制權!如果說用戶〔也可能是黑客提供了水——緩沖區溢出攻擊的數據,那么系統提供了溢出的容器——緩沖區。緩沖區在系統中的表現形式是多樣的,高級語言定義的變量、數組、結構體等在運行時可以說都是保存在緩沖區內的,因此所謂緩沖區可以更抽象地理解為一段可讀寫的內存區域,緩沖區攻擊的最終目的就是希望系統能執行這塊可讀寫內存中已經被蓄意設定好的惡意代碼。按照馮·諾依曼存儲程序原理,程序代碼是作為二進制數據存儲在內存的,同樣程序的數據也在內存中,因此直接從內存的二進制形式上是無法區分哪些是數據哪些是代碼的,這也為緩沖區溢出攻擊提供了可能。圖1進程地址空間分布圖1是進程地址空間分布的簡單表示。代碼存儲了用戶程序的所有可執行代碼,在程序正常執行的情況下,程序計數器〔PC指針只會在代碼段和操作系統地址空間〔內核態內尋址。數據段內存儲了用戶程序的全局變量,文字池等。棧空間存儲了用戶程序的函數棧幀〔包括參數、局部數據等,實現函數調用機制,它的數據增長方向是低地址方向。堆空間存儲了程序運行時動態申請的內存數據等,數據增長方向是高地址方向。除了代碼段和受操作系統保護的數據區域,其他的內存區域都可能作為緩沖區,因此緩沖區溢出的位置可能在數據段,也可能在堆、棧段。如果程序的代碼有軟件漏洞,惡意程序會"教唆"程序計數器從上述緩沖區內取指,執行惡意程序提供的數據代碼!本文分析并實現棧溢出攻擊方式。二、函數棧幀棧的主要功能是實現函數的調用。因此在介紹棧溢出原理之前,需要弄清函數調用時棧空間發生了怎樣的變化。每次函數調用時,系統會把函數的返回地址〔函數調用指令后緊跟指令的地址,一些關鍵的寄存器值保存在棧內,函數的實際參數和局部變量〔包括數據、結構體、對象等也會保存在棧內。這些數據統稱為函數調用的棧幀,而且是每次函數調用都會有個獨立的棧幀,這也為遞歸函數的實現提供了可能。圖2函數棧幀如圖所示,我們定義了一個簡單的函數function,它接受一個整形參數,做一次乘法操作并返回。當調用function<0>時,arg參數記錄了值0入棧,并將callfunction指令下一條指令的地址0x00bd16f0保存到棧內,然后跳轉到function函數內部執行。每個函數定義都會有函數頭和函數尾代碼,如圖綠框表示。因為函數內需要用ebp保存函數棧幀基址,因此先保存ebp原來的值到棧內,然后將棧指針esp內容保存到ebp。函數返回前需要做相反的操作——將esp指針恢復,并彈出ebp。這樣,函數內正常情況下無論怎樣使用棧,都不會使棧失去平衡。subesp,44h指令為局部變量開辟了棧空間,比如ret變量的位置。理論上,function只需要再開辟4字節空間保存ret即可,但是編譯器開辟了更多的空間〔這個問題很詭異,你覺得呢?。函數調用結束返回后,函數棧幀恢復到保存參數0時的狀態,為了保持棧幀平衡,需要恢復esp的內容,使用addesp,4將壓入的參數彈出。之所以會有緩沖區溢出的可能,主要是因為棧空間內保存了函數的返回地址。該地址保存了函數調用結束后后續執行的指令的位置,對于計算機安全來說,該信息是很敏感的。如果有人惡意修改了這個返回地址,并使該返回地址指向了一個新的代碼位置,程序便能從其它位置繼續執行。三、棧溢出基本原理上邊給出的代碼是無法進行溢出操作的,因為用戶沒有"插足"的機會。但是實際上很多程序都會接受用戶的外界輸入,尤其是當函數內的一個數組緩沖區接受用戶輸入的時候,一旦程序代碼未對輸入的長度進行合法性檢查的話,緩沖區溢出便有可能觸發!比如下邊的一個簡單的函數。voidfun<unsignedchar*data>{unsignedcharbuffer[BUF_LEN];strcpy<<char*>buffer,<char*>data>;//溢出點}這個函數沒有做什么有"意義"的事情〔這里主要是為了簡化問題,但是它是一個典型的棧溢出代碼。在使用不安全的strcpy庫函數時,系統會盲目地將data的全部數據拷貝到buffer指向的內存區域。buffer的長度是有限的,一旦data的數據長度超過BUF_LEN,便會產生緩沖區溢出。圖3緩沖區溢出由于棧是低地址方向增長的,因此局部數組buffer的指針在緩沖區的下方。當把data的數據拷貝到buffer內時,超過緩沖區區域的高地址部分數據會"淹沒"原本的其他棧幀數據,根據淹沒數據的內容不同,可能會有產生以下情況:1、淹沒了其他的局部變量。如果被淹沒的局部變量是條件變量,那么可能會改變函數原本的執行流程。這種方式可以用于破解簡單的軟件驗證。2、淹沒了ebp的值。修改了函數執行結束后要恢復的棧指針,將會導致棧幀失去平衡。3、淹沒了返回地址。這是棧溢出原理的核心所在,通過淹沒的方式修改函數的返回地址,使程序代碼執行"意外"的流程!4、淹沒參數變量。修改函數的參數變量也可能改變當前函數的執行結果和流程。5、淹沒上級函數的棧幀,情況與上述4點類似,只不過影響的是上級函數的執行。當然這里的前提是保證函數能正常返回,即函數地址不能被隨意修改〔這可能很麻煩!。如果在data本身的數據內就保存了一系列的指令的二進制代碼,一旦棧溢出修改了函數的返回地址,并將該地址指向這段二進制代碼的其實位置,那么就完成了基本的溢出攻擊行為。圖4基本棧溢出攻擊通過計算返回地址內存區域相對于buffer的偏移,并在對應位置構造新的地址指向buffer內部二進制代碼的其實位置,便能執行用戶的自定義代碼!這段既是代碼又是數據的二進制數據被稱為shellcode,因為攻擊者希望通過這段代碼打開系統的shell,以執行任意的操作系統命令——比如下載病毒,安裝木馬,開放端口,格式化磁盤等惡意操作。四、棧溢出攻擊上述過程雖然理論上能完成棧溢出攻擊行為,但是實際上很難實現。操作系統每次加載可執行文件到進程空間的位置都是無法預測的,因此棧的位置實際是不固定的,通過硬編碼覆蓋新返回地址的方式并不可靠。為了能準確定位shellcode的地址,需要借助一些額外的操作,其中最經典的是借助跳板的棧溢出方式。根據前邊所述,函數執行后,棧指針esp會恢復到壓入參數時的狀態,在圖4中即data參數的地址。如果我們在函數的返回地址填入一個地址,該地址指向的內存保存了一條特殊的指令jmpesp——跳板。那么函數返回后,會執行該指令并跳轉到esp所在的位置——即data的位置。我們可以將緩沖區再多溢出一部分,淹沒data這樣的函數參數,并在這里放上我們想要執行的代碼!這樣,不管程序被加載到哪個位置,最終都會回來執行棧內的代碼。圖5借助跳板的棧溢出攻擊借助于跳板的確可以很好的解決棧幀移位〔棧加載地址不固定的問題,但是跳板指令從哪找呢?"幸運"的是,在Windows操作系統加載的大量dll中,包含了許多這樣的指令,比如kernel32.dll,ntdll.dll,這兩個動態鏈接庫是Windows程序默認加載的。如果是圖形化界面的Windows程序還會加載user32.dll,它也包含了大量的跳板指令!而且更"神奇"的是Windows操作系統加載dll時候一般都是固定地址,因此這些dll內的跳板指令的地址一般都是固定的。我們可以離線搜索出跳板執行在dll內的偏移,并加上dll的加載地址,便得到一個適用的跳板指令地址!//查詢dll內第一個jmpesp指令的位置
intfindJmp<char*dll_name>
{
char*handle=<char*>LoadLibraryA<dll_name>;//獲取dll加載地址
for<intpos=0;;pos++>//遍歷dll代碼空間
{
if<handle[pos]==<char>0xff&&handle[pos+1]==<char>0xe4>//尋找0xffe4=jmpesp
{
return<int><handle+pos>;
}
}
}這里簡化了搜索算法,輸出第一個跳板指令的地址,讀者可以選取其他更合適位置。LoadLibraryA庫函數返回值就是dll的加載地址,然后加上搜索到的跳板指令偏移pos便是最終地址。jmpesp指令的二進制表示為0xffe4,因此搜索算法就是搜索dll內這樣的字節數據即可。雖然如此,上述的攻擊方式還不夠好。因為在esp后繼續追加shellcode代碼會將上級函數的棧幀淹沒,這樣做并沒有什么好處,甚至可能會帶來運行時問題。既然被溢出的函數棧幀內提供了緩沖區,我們還是把核心的shellcode放在緩沖區內,而在esp之后放上跳轉指令轉移到原本的緩沖區位置。由于這樣做使代碼的位置在esp指針之前,如果shellcode中使用了push指令便會讓esp指令與shellcode代碼越來越近,甚至淹沒自身的代碼。這顯然不是我們想要的結果,因此我們可以強制抬高esp指針,使它在shellcode之前〔低地址位置,這樣就能在shellcode內正常使用push指令了。圖6調整shellcode與棧指針調整代碼的內容很簡單:addesp,-X
jmpesp第一條指令抬高了棧指針到shellcode之前。X代表shellcode起始地址與esp的偏移。如果shellcode從緩沖區起始位置開始,那么就是buffer的地址偏移。這里不使用subesp,X指令主要是避免X的高位字節為0的問題,很多情況下緩沖區溢出是針對字符串緩沖區的,如果出現字節0會導致緩沖區截斷,從而導致溢出失敗。第二條指令就是跳轉到shellcode的起始位置繼續執行。〔又是jmpesp!通過上述方式便能獲得一個較為穩定的棧溢出攻擊。五、shellcode構造shellcode實質是指溢出后執行的能開啟系統shell的代碼。但是在緩沖區溢出攻擊時,也可以將整個觸發緩沖區溢出攻擊過程的代碼統稱為shellcode,按照這種定義可以把shellcode分為四部分:1、核心shellcode代碼,包含了攻擊者要執行的所有代碼。2、溢出地址,是觸發shellcode的關鍵所在。3、填充物,填充未使用的緩沖區,用于控制溢出地址的位置,一般使用nop指令填充——0x90表示。4、結束符號0,對于符號串shellcode需要用0結尾,避免溢出時字符串異常。前邊一直在圍繞溢出地址討論,并解決了shellcode組織的問題,而最核心的代碼如何構造并未提及——即攻擊成功后做的事情。其實一旦緩沖區溢出攻擊成功后,如果被攻擊的程序有系統的root權限——比如系統服務程序,那么攻擊者基本上可以為所欲為了!但是我們需要清楚的是,核心shellcode必須是二進制代碼形式。而且shellcode執行時是在遠程的計算機上,因此shellcode是否能通用是一個很復雜的問題。我們可以用一段簡單的代碼實例來說明這個問題。緩沖區溢出成功后,一般大家都會希望開啟一個遠程的shell控制被攻擊的計算機。開啟shell最直接的方式便是調用C語言的庫函數system,該函數可以執行操作系統的命令,就像我們在命令行下執行命令那樣。假如我們執行cmd命令——在遠程計算機上啟動一個命令提示終端〔我們可能還不能和它交互,但是可以在這之前建立一個遠程管道等,這里僅作為實例測試。為了使system函數調用成功,我們需要將"cmd"字符串內容壓入棧空間,并將其地址壓入作為system函數的參數,然后使用call指令調用system函數的地址,完成函數的執行。但是這樣做還不夠,如果被溢出的程序沒有加載C語言庫的話,我們還需要調用Windows的APILoadlibrary加載C語言的庫msvcrt.dll,類似的我們也需要為字符串"msvcrt.dll"開辟棧空間。xorebx,ebx;//ebx=0push0x3f3f6c6c;//ll??push0x642e7472;//rt.dpush0x6376736d;//msvcmov[esp+10],ebx;//'?'->'0'mov[esp+11],ebx;//'?'->'0'moveax,esp;//"msvcrt.dll"地址pusheax;//"msvcrt.dll"moveax,0x77b62864;//kernel32.dll:LoadLibraryAcalleax;//LoadLibraryA<"msvcrt.dll">addesp,16push0x3f646d63;//"cmd?"mov[esp+3],ebx;//'?'->'\0'moveax,esp;//"cmd"地址pusheax;//"cmd"moveax,0x774ab16f;//msvcrt.dll:systemcalleax;//system<"cmd">addesp,8上述匯編代碼實質上是如下兩個函數調用語句:Loadlibrary<"msvcrt.dll">;system<"cmd">;不過在構造這段匯編代碼時需要注意不能出現字節0,為了填充字符串的結束字符,我們使用已經初始化為0的ebx寄存器代替。另外,在對庫函數調用的時候需要提前計算出函數的地址,如Loadlibrary函數的0x77b62864。計算方式如下:intfindFunc<char*dll_name,char*func_name>{HINSTANCEhandle=LoadLibraryA<dll_name>;//獲取dll加載地址return<int>GetProcAddress<handle,func_name>;}這個函數地址是在本地計算的,如果被攻擊計算機的操作系統版本差別較大的話,這個地址可能是錯誤的。不過在《0day安全:軟件漏洞分析技術》中,作者提供了一個更好的方式,感興趣的讀者可以參考該書提供的代碼。因此構造一個通用的shellcode并非十分容易,如果想讓攻擊變得有效的話。六、匯編語言自動轉換寫出shellcode后〔無論是簡單的還是通用的,我們還需要將這段匯編代碼轉換為機器代碼。如果讀者對x86匯編十分熟悉的話,選擇手工敲出二進制代碼的話也未嘗不可。不過我們都希望能讓計算機幫助做完這些事,既然開發環境提供了編譯器,用它們幫忙何樂而不為呢?既不用OllyDbg工具,也不適用其他的第三方工具,我們寫一個簡單的函數來完成這個工作。//將內嵌匯編的二進制指令dump到文件,style指定輸出數組格式還是二進制形式,返回代碼長度intdumpCode<unsignedchar*buffer>{gotoEND;//略過匯編代碼BEGIN:__asm{//在這里定義任意的合法匯編代碼}END://確定代碼范圍UINTbegin,end;__asm{moveax,BEGIN;movbegin,eax;moveax,END;movend,eax;}//輸出intlen=end-begin;memcpy<buffer,<void*>begin,len>;//四字節對齊intfill=<len-len%4>%4;while<fill-->buffer[len+fill]=0x90;//返回長度returnlen+fill;}因為C++是支持嵌入式匯編代碼的,因此在函數內的匯編代碼都會被整成編譯為二進制代碼。實現二進制轉換的基本思想是讀取編譯器最終生成的二進制代碼段數據,將數據導出到指定的緩沖區內。為了鎖定嵌入式匯編代碼的位置和長度,我們定義了兩個標簽BEGIN和END。這兩個標簽在匯編語言級
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 院內低血糖的防治
- 湖南省長沙市2024屆高三數學下學期三模試題含答案
- 江蘇省泗洪縣2025年高中畢業生班階段性測試(三)語文試題含解析
- 上海電子信息職業技術學院《軟件項目管理》2023-2024學年第一學期期末試卷
- 天津市職業大學《中國民族樂器發展史》2023-2024學年第二學期期末試卷
- 山西運城農業職業技術學院《路橋檢測》2023-2024學年第一學期期末試卷
- 江蘇省如東縣2025年初三年級模擬考試數學試題含解析
- 南昌職業大學《家畜環境衛生學實驗》2023-2024學年第二學期期末試卷
- 錦州醫科大學醫療學院《電信專業英語》2023-2024學年第一學期期末試卷
- 江蘇省泰興市分界鎮初級中學2025年初三下學期3月物理試題試卷含解析
- 油潑面廣告語
- 咽部腫瘤的診治
- 情景題心理測試題目及答案
- 2023-2024學年北京東城區北京匯文中學化學高一第一學期期末質量檢測模擬試題含解析
- 你好大學新生第四期學習通章節答案期末考試題庫2023年
- GB/T 25436-2023茶葉濾紙
- 中國癡呆與認知障礙的診治指南
- 戰國虎符拍賣18億
- 科特勒營銷管理課件
- 10 奪取抗日戰爭和人民解放戰爭的勝利(說課稿)-2022-2023學年道德與法治五年級下冊
- 感染性休克教學查房-2
評論
0/150
提交評論