2011-12-06 88 views
2

下面的代碼來自着名的文章Smashing The Stack For Fun And Profit我的溢出代碼不起作用

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); 
} 

我想我必須解釋我的這段代碼的目標。 堆棧模型如下。該字下面的數字是堆棧中變量的字節數。所以,如果我想重寫RET來跳過我想要的語句,我計算從buffer1到RET的偏移量是8 + 4 = 12。由於該架構是x86 Linux。

buffer2 buffer1 BSP RET a b c 
(12) (8)  (4) (4) (4) (4) (4) 

我想跳過聲明x=1;,讓屏幕上printf()輸出0

我編譯代碼:

gcc stack2.c -g 

,並在gdb運行它:

gdb ./a.out 

GDB給我的結果是這樣的:

Program received signal SIGSEGV, Segmentation fault. 
main() at stack2.c:17 
17 x = 1; 

我認爲Linux使用一些防止堆棧溢出的機制。也許Linux將RET地址存儲在另一個地方,並在函數返回之前比較堆棧中的RET地址。

這個機制的細節是什麼?我應該如何重寫代碼才能使程序輸出0

好的,反彙編代碼在下面。它來自gdb的輸出,因爲我覺得更容易閱讀。有人可以告訴我如何粘貼一個長代碼序列嗎?一個接一個地複製粘貼我太累了......

Dump of assembler code for function main: 
0x08048402 <+0>: push %ebp 
0x08048403 <+1>: mov %esp,%ebp 
0x08048405 <+3>: sub $0x10,%esp 
0x08048408 <+6>: movl $0x0,-0x4(%ebp) 
0x0804840f <+13>: movl $0x3,0x8(%esp) 
0x08048417 <+21>: movl $0x2,0x4(%esp) 
0x0804841f <+29>: movl $0x1,(%esp) 
0x08048426 <+36>: call 0x80483e4 <function> 
0x0804842b <+41>: movl $0x1,-0x4(%ebp) 
0x08048432 <+48>: mov $0x8048520,%eax 
0x08048437 <+53>: mov -0x4(%ebp),%edx 
0x0804843a <+56>: mov %edx,0x4(%esp) 
0x0804843e <+60>: mov %eax,(%esp) 
0x08048441 <+63>: call 0x804831c <[email protected]> 
0x08048446 <+68>: mov $0x0,%eax 
0x0804844b <+73>: leave 
0x0804844c <+74>: ret 


Dump of assembler code for function function: 
0x080483e4 <+0>: push %ebp 
0x080483e5 <+1>: mov %esp,%ebp 
0x080483e7 <+3>: sub $0x14,%esp 
0x080483ea <+6>: lea -0x9(%ebp),%eax 
0x080483ed <+9>: add $0x3,%eax 
0x080483f0 <+12>: mov %eax,-0x4(%ebp) 
0x080483f3 <+15>: mov -0x4(%ebp),%eax 
0x080483f6 <+18>: mov (%eax),%eax 
0x080483f8 <+20>: lea 0x8(%eax),%edx 
0x080483fb <+23>: mov -0x4(%ebp),%eax 
0x080483fe <+26>: mov %edx,(%eax) 
0x08048400 <+28>: leave 
0x08048401 <+29>: ret 

我檢查彙編代碼,發現我的一些程序錯誤,我必須重寫(*ret)+=8(*ret)+=7,因爲0x08048432 <+48>減去0x0804842b <+41>是7

+1

檢查程序集生成,可能你的函數已經內聯。 – kan

+0

我保證函數沒有被內聯,因爲我檢查了彙編代碼。 –

+1

假設'x = 1'生成8個字節的彙編代碼,並且堆棧按照您顯示的方式進行佈局(可能還有其他內容)。添加彙編列表,答案將變得非常明顯。 – Skizz

回答

2

因爲那篇文章是從1996年開始的,而且這些假設是錯誤的。

請參閱 「砸現代堆棧的樂趣和利潤」

http://www.ethicalhacker.net/content/view/122/24/

從上面的鏈接:

然而,GNU C編譯器(GCC)自1998年以來的演變,結果,許多人仍然想知道爲什麼他們不能爲他們工作的例子,或者如果他們確實得到代碼工作,爲什麼他們必須做出他們所做的改變。

+0

非常感謝! –

0

功能function覆蓋一些其堆棧外的地方wn,這種情況是main的堆棧。重寫的內容我不知道,但會導致你看到的分段錯誤。它可能是操作系統採用的一些保護措施,但當錯誤的值位於堆棧上的位置時,生成的代碼可能會出錯。

這是一個很好的例子,說明當您在分配的內存之外寫入時可能發生的情況。它可能會直接崩潰,它可能會崩潰在完全不同的地方,或者如果可能不會崩潰,但只是做一些錯誤的計算。

+0

-1沒有什麼能夠阻止你覆蓋任何其他函數的堆棧幀到堆棧的根目錄。在這種情況下,他可能會寫入堆棧的起始位置(具體取決於調用'main()'的代碼) –

+2

分段錯誤是由進程在其內存之外寫入引起的。由於這兩個函數屬於同一個進程,因此它們可以在不提高SIGSEGV的情況下對對方的內存進行塗寫(除非編譯器或操作系統已經做了某些*非常奇怪的事情來防止堆棧崩潰)。 – Jonathan

0

ret = buffer1 + 3;嘗試

說明:ret是整數指針;遞增1增加4個字節到32位機器上的地址。

+0

它不起作用。 –

+3

打印'x','a'和'buffer1'的地址。這應該給你一個錯誤是哪一方的想法。 PS:「不起作用」作爲錯誤信息是沒有用的。請不要再使用這個句子。 :-) –

+0

OK。我已經厭倦了重寫「ret = buffer1 + 3」並運行該程序。並且在我厭倦了代碼之後發佈了第一條評論。但它不起作用。 –