全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1562
推到 Plurk!
推到 Facebook!

Windows 2000緩衝區溢出入門

 
jackkcg
站務副站長


發表:891
回覆:1050
積分:848
註冊:2002-03-23

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-06-09 01:57:35 IP:61.64.xxx.xxx 未訂閱
此為轉貼資料    http://gtogo.myetang.com/po/biancheng/8.htm Windows 2000緩衝區溢出入門     原著:Jason 翻譯/整理/改編:backend        --[ 前言        我在互聯網上閱讀過許多關於緩衝區溢出的文章。其中的絕大多數都是基於*NIX作業系統平臺的。後來有幸拜讀了ipxodi所著的《Windows系統下的堆疊溢位》(已刊登在綠盟網路安全月刊2000年第三期中),又碰巧看到了Jason先生的《Windows NT Buffer Overflow’s From Start to Finish》,得益匪淺。在翻譯Jason先生的文章時,由於我的機器安裝了Windows 2000 Server,在調試原文程式時發現細節略有出入。因此本文提供的有關根源程式、動態連結程式庫、偏移量等是以我在自己機器上調試爲准。(對不同版本的動態連結程式庫,都需要編程者自己調試。)        這篇文章應該屬入門級。雖然比較簡單,但對於Windows系統下的緩衝區溢出具有一定的通用性。例如,堆疊溢位位址的確定,跳轉指令的查找和使用,溢出執行代碼的編寫,等等。只要發現Windows系統下存在緩衝區溢出漏洞的程式,基本上都可通過這些步驟進行攻擊測試。但正如ipxodi所指出的,由於Windows下動態連結程式庫的版本更新較快,一定要根據編程者的實際平臺進行調試。在發佈此類安全漏洞公告或溢出攻擊程式時,源代碼、系統平臺和動態連結程式庫的版本號都應該儘量列清楚。否則別人調試起來可能會頭疼得很厲害。;)        --[ 調試、測試環境    Microsoft Visual C++ 6.0  Microsoft Windows 2000 Server (中文版,內部版本號:2195)         --[ 調試、測試過程    首先,寫一個存在緩衝區溢出漏洞的應用程式。該程式可讀取文件的內容,這樣我們就能通過修改被讀取文件的內容來使程式溢出。;-) 在Visual C++開發環境中創建一個新的控制臺應用程式,選擇”An Application that supports MFC”並單擊”Finish”。(注:其實並不一定非是MFC應用程式不可,只不過是我自己的習慣而已。;-)))向這個應用程式中添加一些必要的代碼,如下:    CWinApp theApp;     using namespace std;     void overflow(char* buff);     void overflow(char* buff)  {  CFile file;  CFileException er;  if(!file.Open(_T("overflow.txt"),CFile::modeRead,&er))  {  er.ReportError();  return;  }     int x = file.GetLength();  file.Read(buff,x);  }     int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  {  int nRetCode = 0;     // initialize MFC and print and error on failure  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))  {  // TODO: change error code to suit your needs  cerr << _T("Fatal Error: MFC initialization failed") << endl;  nRetCode = 1;  }  else  {  char buff[10];  overflow(buff);  }  return nRetCode;  }      現在先來分析一下上面這段C++代碼,找一找哪里有漏洞。這是一個MFC控制臺應用程式,”main”函數與其他程式會有些不同,但工作機制基本一致。我們主要分析該函數中”else”那段代碼。首先是第一行”char buff[10]”,定義了一個10字元長的本地變數。我們都知道,本地變數的記憶體空間是在堆疊裏分配的。(如果你連這個都不知道,建議不要繼續往下看了。:))然後是將buff變數作爲參數調用overflow函數。好了,現在讓我們分析overflow函數。首先是一個Cfile物件,然後是一個CfileException物件。接下來會試圖以讀許可權打開當前目錄下的文件”overflow.txt”。如果打開成功,則將該文件中的所有內容讀取到buff陣列變數中。發現了問題沒有?buff變數只有10字元長。如果讀取的文件內容長度是100時會發生什麽問題呢?對了,“緩衝區溢出”!而且是在堆疊中發生的緩衝區溢出。在後面的測試中就能看到,我們利用這個漏洞能做些什麽!;)現在讓我們創建文字檔案”overflow.txt”,並將它放到這個應用程式的project目錄下。        在進行下一步前,先讓我們探討一下關於Windows NT/2000的記憶體結構。NT/2000的每一個進程都在啓動時分配了4GB(0xFFFFFFFF)的虛擬記憶體。其中的某些部份實際上是由所有進程共用的,例如核心和設備驅動程式區域。但它們都會被映射到每個進程的虛擬位址空間裏。實際上沒有進程分配到4GB的實體記憶體,而是僅當需要時才分配實體記憶體。因此每一個進程都有各自的4GB虛擬記憶體,編址範圍從0x00000000到0xFFFFFFFF。其中,0x00000000-0x0000FFFF是爲NULL指標分配而保留的。訪問該區域記憶體將導致“非法訪問”錯誤。0x00010000-0x7FFEFFFF是用戶進程空間。EXE文件的映射被載入到其中(起始地址0x00400000),DLL(動態連結程式庫)也被載入到這部份空間。如果DLL或EXE的代碼被裝入到該範圍的某些地址,就能夠被執行。訪問該區域中沒有代碼裝入的位址將導致“非法訪問”錯誤。0x7FFF0000-0x7FFFFFFF是保留區域,對此區域的任何訪問都將導致“非法訪問”錯誤。0x80000000-0xFFFFFFFF僅供作業系統使用。用於載入設備驅動程式和其他核心級代碼。從用戶級應用程式(ring 3)訪問此區域將導致“非法訪問”錯誤。        現在回到”overflow.txt”文件。現在我們將向這個文字檔案中不斷添加字元,直到彈出應用程式非法訪問的系統對話方塊。在這裏,填充什麽字元是很重要的(原因待會就知道了)。我選擇小寫字母”a”來填充文字檔案。我們已經知道緩衝區只有10字元長,那麽先填充11個字元。(注意:以debug方式編譯應用程式,否則結果可能會有所不同。)咦?沒反應。我們繼續填充字元……直到填充了18個字元應用程式才崩潰。但這個崩潰對我們的用處還不大。繼續填充!當字串長度爲24時,運行程式並觀察彈出的對話方塊資訊:“”0x61616161”指令引用的”0x61616161”記憶體。該記憶體不能爲”written”。”我想大家都應該知道”0x61”所代表的ASCII碼是什麽吧?;)如果你的機器安裝了Visual C++,單擊“取消”按鈕就能夠調試該應用程式。進入調試環境後,選擇”view”功能表――”debug windows”――”registers”,可打開寄存器窗口。如果你對彙編一竅不通,建議先去找本彙編的書看看。在寄存器視窗裏會看到EAX、EBS和EIP等寄存器的內容。EIP當然是最重要的了。EIP的內容就是程式下一步所要執行指令的位址。我們注意到ESP寄存器的值未被破壞,而且似乎離我們的buff變數不遠。下一步我們需要找出ESP的值是如何處理得到的。        現在開始會複雜些了(而這就是樂趣的源泉!:))。 在main函數的最後一行代碼處設置中斷點,因爲我們只關心這裏所發生的事情。現在啓動調試器,並讓程式無故障運行到該中斷點。然後切換到反彙編窗口(按Alt+8,或單擊”View”――”debug windows”――”disassembly”)。另外還要打開記憶體視窗和寄存器視窗。    0040155B 5F pop edi 0040155C 5E pop esi 0040155D 5B pop ebx 0040155E 83 C4 50 add esp,50h 00401561 3B EC cmp ebp,esp 00401563 E8 7E 00 00 00 call _chkesp (004015e6) 00401568 8B E5 mov esp,ebp 0040156A 5D pop ebp 0040156B C3 ret        以上這些東西是什麽?彙編代碼。如果你對彙編一點都不懂,我在這裏做一些簡單的說明。第一行是”pop edi”。指令pop用於將僅次於堆疊頂端的資料移到其後的指定寄存器中。需要注意的是ESP寄存器。ESP是32位堆疊指標。一個pop指令移動堆疊頂端的一個資料單元,在這裏是DWORD(雙字,4位元組),到指定寄存器中,並將堆疊指標加4(因爲共移動了4位元組)。在執行下一步前,讓我們看一下ESP寄存器。在記憶體視窗中輸入ESP,就能得到ESP當前指向的地址和內容。看一下ESP指向的記憶體位址中4個位元組的內容和EDI寄存器的內容。現在單步執行”pop.edi”,我們能夠看到EDI寄存器中填入了ESP所指向的記憶體位址的數值,同時ESP的數值也增加了4。後面的兩條指令是一樣的,只不過寄存器不同罷了。單步執行它們。跟著的三行指令對本文沒什麽意義,所以在這裏不作解釋。單步執行到指令”mov esp, ebp”,該指令會將EBP的值賦給ESP寄存器。然後是指令”pop ebp”,這條指令很重要。先讓我們在記憶體視窗輸入ESP,可以看到該記憶體位址有一串”0x61”(’a’的16進制值)。因此0x61616161將被彈出到EBP寄存器中。單步執行該指令可以檢驗我說的沒錯吧?;)好了,雖然我說的沒錯,但好象我們還沒能得到什麽有用的東西?現在到了最後一條指令”ret”。指令”ret”在彙編中是返回指令。它是如何知道應該返回到哪里的呢?由當前位於堆疊頂端的數值決定。這條指令如果用pop指令表示的話可以表示爲”pop eip”(雖然實際上你無法執行這條pop指令;))。它從ESP所指向記憶體位址處彈出4位元組內容,並賦給EIP寄存器(EIP寄存器是32位元指令指標)。這就意味著,不管EIP指向哪個記憶體位址,該位址處的指令將總會成爲下一條指令。我們再次在記憶體視窗中輸入ESP,看一下將要賦給EIP寄存器的位址的指令是什麽。其實我想此時大家都應該知道是4個位元組長的0x61串。現在讓我們單步執行該指令,看到EIP的值爲0x61616161,也就是說下一指令位址爲0x61616161,但指令卻顯示爲???(意爲無效指令)。因此再單步執行指令將導致“訪問非法”錯誤。現在再看看ESP寄存器。它正確地指向了堆疊中的下一個數值。也就是說,下一步工作是確定在使緩衝區成功溢出(EIP=0x61616161)時,ESP所指向的地址是否能夠存放我們的溢出代碼!我們在overflow.txt文件中再次增加4個’a’(共28個’a’),並再次調試程式,在執行到”ret”指令時觀察記憶體視窗和寄存器視窗,會發現執行”ret”指令後ESP所指向記憶體位址的內容爲4位元組長的0x61串。Great!這意味著什麽?!這個讓大家自己想去吧。;)))        現在我再回過頭來分析一下。我們剛才使用字元’a’(0x61)作爲文字檔案的填充內容,以確定存在緩衝區溢出。由於EIP=0x61616161,當我們的程式訪問試圖訪問該位址處的指令時,會因爲是無效指令而導致系統出錯。但如果所指向的地址存在可執行代碼時又如何呢?例如裝入記憶體的DLL代碼等。哈哈,這樣的話就會執行這些指令,從而可能做一些別人想像不到的事!;)        好了,到目前爲止,我們已經能控制EIP的數值,也知道ESP指向的堆疊位置,和能夠向堆疊寫入任意資料。那麽下一步做什麽呢?當然是找到使系統執行我們的溢出代碼的方法了。如果你看過ipxodi所著的文章《Windows系統下的堆疊溢位》,就會知道採用跳轉指令(jmp esp)是最好不過的了。原因在這裏就不再多講,請大家仔細閱讀《Windows系統下的堆疊溢位》就清楚了。正如前面分析過的,這是因爲執行完ret指令後ESP正好能夠指向我們的溢出代碼!(……哦,找不到,我沒分析過?在本文中查找單詞”Great”吧,呵呵。)現在我們就要在應用程式的記憶體空間中找到含有”jmp esp”指令的位址。首先當然是確定這條指令的機器碼了。怎麽確定?這也要教?好吧,教就教吧。僅此一次,下不違例。;)其實方法很簡單,按以下步驟就可以了。先在Visual C++中創建新的應用程式。(當然還是控制臺程式,還是支援MFC,這是我的習慣。呵呵。)輸入以下代碼:    CWinApp theApp;     using namespace std;     int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  {  int nRetCode = 0;     // initialize MFC and print and error on failure  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))  {  // TODO: change error code to suit your needs  cerr << _T("Fatal Error: MFC initialization failed") << endl;  nRetCode = 1;  }  else  {  return 0;  __asm jmp esp  }  return nRetCode;  }         好了,然後在Visual C++環境中設置正確的調試中斷點。哪里?對了,在“return 0;”處。接著運行程式,使其在中斷點處暫停運行。現在(選擇“view”功能表——“Debug Windows”——“Disassembly”)打開反彙編視窗,並在反彙編視窗中單擊滑鼠右鍵,在右鍵彈出功能表中選擇“Source Annotation”和“Code Bytes”。此時,在記憶體位址列右側、(jmp esp)指令列左側的"FF E4"就是指令"jmp esp"的機器碼。如果需要找出其他彙編指令的機器碼,基本上都可通過這種方法得到。        下一步是如何在我們的進程空間裏找到這串機器碼。也是非常簡單的,只要修改一下代碼即可:     CWinApp theApp;     using namespace std;     int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  {  int nRetCode = 0;     // initialize MFC and print and error on failure  if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))  {  // TODO: change error code to suit your needs  cerr << _T("Fatal Error: MFC initialization failed") << endl;  nRetCode = 1;  }  else  {  #if 0  return 0;  __asm jmp esp     #else     bool we_loaded_it = false;  HINSTANCE h;  TCHAR dllname[] = _T("User32");     h = GetModuleHandle(dllname);  if(h == NULL)  {  h = LoadLibrary(dllname);  if(h == NULL)  {  cout<<"ERROR LOADING DLL: "<
------
**********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好

Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind
系統時間:2024-11-23 6:16:54
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!