2013-10-27 66 views
3

在給定的URL這個函數給出: http://insecure.org/stf/smashstack.html我無法理解緩衝區溢出是如何在這段代碼發生

void function(int a, int b, int c) { 
    char buffer1[5]; 
    char buffer2[10]; 
    int *ret; 

    ret = buffer1 + 12; 
    (*ret) += 8; 
} 

void main() { 
    int x; 

    x = 0; 
    function(1,2,3); 
    x = 1; 
    printf("%d\n",x); 
} 

爲主要功能的相應的彙編代碼:

Dump of assembler code for function main: 
0x8000490 <main>:  pushl %ebp 
0x8000491 <main+1>:  movl %esp,%ebp 
0x8000493 <main+3>:  subl $0x4,%esp 
0x8000496 <main+6>:  movl $0x0,0xfffffffc(%ebp) 
0x800049d <main+13>: pushl $0x3 
0x800049f <main+15>: pushl $0x2 
0x80004a1 <main+17>: pushl $0x1 
0x80004a3 <main+19>: call 0x8000470 <function> 
0x80004a8 <main+24>: addl $0xc,%esp 
0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 
0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 
0x80004b5 <main+37>: pushl %eax 
0x80004b6 <main+38>: pushl $0x80004f8 
0x80004bb <main+43>: call 0x8000378 <printf> 
0x80004c0 <main+48>: addl $0x8,%esp 
0x80004c3 <main+51>: movl %ebp,%esp 
0x80004c5 <main+53>: popl %ebp 
0x80004c6 <main+54>: ret 
0x80004c7 <main+55>: nop 

在可變ret,它們都指向ret到下一條指令的地址上運行。我不明白只要保留ret變量中的下一條指令,程序將如何跳轉到下一個位置? 我知道如何工作緩衝區溢出,而是通過改變ret變量,這是怎麼做的緩衝區溢出? 即使是考慮到這是一個虛擬的計劃,只是應該讓我們瞭解如何工作緩衝區溢出,改變ret變量似乎是錯誤的。

回答

3

的如何,這是一個緩衝區溢出的例子說明:

function局部變量,包括buffer1,是在堆棧上,與返回地址,其爲12個字節以後計算沿buffer1。這是因爲溢出寫入一個地址超出buffer1 12個字節的buffer1適當範圍之外寫緩衝器的一個例子。通過由多個8比它大,在更換返回地址時function飾面,而不是突然離開下面的函數調用照常一回的聲明(x = 1;,在這種情況下),返回地址將8個字節以後(在這種情況下,在printf聲明)。

跳過x = 1;說法是不緩衝區溢出 - 這是該修改的返回地址緩衝區溢出的效果。對8計算

注爲跳過x = 1;聲明相應的位置:

參見8計算FrankH's careful reevaluation爲適當偏移添加到返回地址,實現跳躍x = 1;的意圖。他的發現與基於GDB的insecure.org source article分析相矛盾。 無論詳細情況如何,緩衝區溢出如何用於更改返回地址的說明仍然相同 - 這只是寫入溢出的問題。

爲了完整起見,這裏是insecure.org source article基於GDB的分析:

我們所做的是增加12 BUFFER1的[]地址。這個新的 地址是存儲返回地址的地方。我們想跳過傳遞給printf調用的 。我們怎麼知道要在 的返回地址中加8?首先,我們使用的測試值(例如1),編譯 程序,然後開始GDB:

[aleph1]$ gdb example3 
GDB is free software and you are welcome to 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. 
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc... 
(no debugging symbols found)... 
(gdb) disassemble main 
Dump of assembler code for function main: 
0x8000490 <main>:  pushl %ebp 
0x8000491 <main+1>:  movl %esp,%ebp 
0x8000493 <main+3>:  subl $0x4,%esp 
0x8000496 <main+6>:  movl $0x0,0xfffffffc(%ebp) 
0x800049d <main+13>: pushl $0x3 
0x800049f <main+15>: pushl $0x2 
0x80004a1 <main+17>: pushl $0x1 
0x80004a3 <main+19>: call 0x8000470 <function> 
0x80004a8 <main+24>: addl $0xc,%esp 
0x80004ab <main+27>: movl $0x1,0xfffffffc(%ebp) 
0x80004b2 <main+34>: movl 0xfffffffc(%ebp),%eax 
0x80004b5 <main+37>: pushl %eax 
0x80004b6 <main+38>: pushl $0x80004f8 
0x80004bb <main+43>: call 0x8000378 <printf> 
0x80004c0 <main+48>: addl $0x8,%esp 
0x80004c3 <main+51>: movl %ebp,%esp 
0x80004c5 <main+53>: popl %ebp 
0x80004c6 <main+54>: ret 
0x80004c7 <main+55>: nop 

我們可以看到,調用函數()時,RET會0x8004a8, 和我們想跳過0x80004ab處的任務。我們要執行的下一個 指令是0x8004b2。有點數學 告訴我們的距離是8個字節。

稍微好一點的數學告訴我們,距離是0x8004a8 - 0x8004b2 = 0xA或10個字節,而不是8個字節。

1

堆棧上的佈局是這樣的(向下地址 - 如堆棧增長):

buffer + ...  value found  description 
================================================================================= 
+24    3     # from main,  pushl $0x3 
+20    2     # from main,  pushl $0x2 
+16    1     # from main,  pushl $0x1 
+12    <main+24>   # from main,  call 0x8000470 <function> 
+8     <frameptr main> # from function, pushl %ebp 
+4 %ebp(function) padding (3 bytes) # ABI - compiler will not _pack_ vars 
+0     buffer[5]; 
...    buffer1[12];  # might be optimized out (unused) 
...    int *ret   # might be optimized out (reg used instead) 

的棘手的事情是buffer開始在四字節對齊的地址,即使它不是大小的倍數四個字節。 「有效大小」是八個字節,所以如果你在它的起始處添加八個字節,你會發現保存的幀指針,如果你再往下四個字節,保存的返回地址(根據你的反彙編,它是main+0x24 。/ 0x80004a8增加8跳轉「到中間的」兩說明資訊,結果是垃圾 - 你跳過x = 1聲明

+0

尼斯趕上更新了我的答案要注意你發現的錯誤+。 1 – kjhughes