摘要:
本文主要演示Linux平台下的棧溢出,首先根據理論對示例代碼進行溢出攻擊;結果是溢出攻擊成立,但是與設想的有差別;然後采用GDB調試工具對發生的意外,進行深入的分析。
Linux下用GDB調試可加載模塊 http://www.linuxidc.com/Linux/2013-01/77969.htm
Fedora 16 x64 構建vim+vimGDB+GDB C/C++調試環境 http://www.linuxidc.com/Linux/2012-08/69081.htm
GDB調試程序用法 http://www.linuxidc.com/Linux/2013-06/86044.htm
GDB+GDBserver無源碼調試Android 動態鏈接庫的技巧 http://www.linuxidc.com/Linux/2013-06/85936.htm
使用hello-gl2建立ndk-GDB環境(有源碼和無源碼調試環境) http://www.linuxidc.com/Linux/2013-06/85935.htm
在Ubuntu上用GDB調試printf源碼 http://www.linuxidc.com/Linux/2013-03/80346.htm
Linux下用GDB調試可加載模塊 http://www.linuxidc.com/Linux/2013-01/77969.htm
Ubuntu下使用GDB斷點Go程序 http://www.linuxidc.com/Linux/2012-06/62941.htm
測試的平台:
1. Ubuntu 9; gcc 4.4.1; Gdb 7.0-ubuntu
2. Ubuntu系統安裝在VirtualBox 3.2.8虛擬機上;
示例代碼如下:
#include<string.h>
void overflow(char* arg)
{
char buf[12];
strcpy(buf, arg);
}
int main(int argc, char *argv[])
{
if(argc > 1)
overflow(argv[1]);
return 0;
}
如果按照一般的方式編譯:
gcc –o stackoverflow stackoverflow.c
linux系統能夠探測到程序中的stack overflow,從而終止程序,如下圖所示:
那有沒有辦法讓系統不探測到stack overflow,此處可以在編譯時,禁用堆棧保護,具體命令如下:
gcc –fno-stack-protector –o stackoverflow stackoverflow.c
然後采用gdb調試stackoverflow,
這裡的輸出跟設想的存在很大的差別,因為設想中的函數棧如下:
前面的12個字節填充buf,然後接下來的4個字節填充ebp,最後的4個字節填充RET地址,那麼照理說,這裡的eip應該是0x65656565,那為什麼此處是0x61616161,剛好是aaaa的值呢?
根據單步調試的結果,發現eip變為0x61616161是在main函數退出後達到的,按照設想應該是在overflow退出時,eip變為0x65656565。
為什麼overflow退出後還能回到main函數?可能的原因:輸入的字符串沒有覆蓋掉ret地址,但是字符串卻意外地將main的返回地址給覆蓋掉了?
但就算是覆蓋,為什麼覆蓋的值沒有采用e的值,而是采用的a的值?要知道a是在字符串的起始處?這點的確讓人奇怪。
1. 我們還是采用一步步調試的方式來觀察問題所在,先看下gcc編譯後的反匯編代碼:
使用到的命令:
set disassembly-flavor intel //將匯編設定為intel風格;
disassemble main //反匯編main函數;
Main函數:
1. push ebp
2. mvo ebp, esp
3. and esp, 0xfffffff0
4. sub esp, 0x10
5. cmp DWORD PTR [ebp+0x8], 0x1
6. jle 0x804841d <main+31>
7. mov eax, DWORD PTR [ebp+0xc]
8. add eax, 0x4
9. mov eax, DWORD PTR [eax]
10. mov DWORD PTR [esp], eax
11. call 0x80483e4 <overflow>
12. mov eax, 0x0
13. leave
14. ret
然後再來看下,overflow的反匯編代碼,命令:disassemble overflow
Overflow函數:
1. push ebp
2. mov ebp, esp
3. sub esp, 0x28
4. mov eax, DWORD PTR[ebp+0x8]
5. mov DWORD PTR[esp+0x4], eax
6. lea eax, [ebp-0x14]
7. mov DWORD PTR [esp], eax
8. call 0x804831c <strcpy@plt>
9. leave
10. ret
我們單步調試上述的指令,關注其中esp值的變化。總圖如下,後面是對其中每一步的分析:
在完成main.1後,命令p $esp後,esp的值變為:Esp = 0xbffff438
Main.3後,esp的值變為0xbffff430,估計是用於對齊;
Main.4後,esp的值為0xbffff420;
Main.7-10,這裡主要將argv[]的arg[1]字符串的首地址取出來,並且將其放置在esp中,此時esp的值為0xbffff420;
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/107768p2.htm