緩衝區溢出

    一個正常的程序在內存中一般分爲程序段,數據端和堆棧三部分。程序段裏放着程序的機器碼和只讀數據,這個段一般是隻讀,對它的寫操做是非法的。數據段放的是程序中的靜態數據。動態數據則經過堆棧來存放。ide

    在內存中,它們的位置以下: 函數


  
 
  /――――――――\  內存低端
 
  程序段
 
  
――――――――― 
  數據段
 
  
――――――――― 
  堆棧
 
  \―――――――――/內存高端
 
堆棧是內存中的一個連續的塊。一個叫堆棧指針的寄存器(SP)指向堆棧的棧頂。堆棧的底部是一個固定地址。堆棧有一個特色就是,後進先出。也就是說,後放入的數據第一個取出。它支持兩個操做,PUSHPOPPUSH是將數據放到棧的頂端,POP是將棧頂的數據取出。
 

在高級語言中,程序函數調用和函數中的臨時變量都用到堆棧。爲何呢?由於在調用一個函數時,咱們須要對當前的操做進行保護,也爲了函數執行後,程序能夠正確的找到地方繼續執行,因此參數的傳遞和返回值也用到了堆棧。一般對局部變量的引用是經過給出它們對SP的偏移量來實現的。另外還有一個基址指針(FP,在Intel芯片中是BP),許多編譯器其實是用它來引用本地變量和參數的。一般,參數的相對FP的偏移是正的,局部變量是負的。
編碼

在C語言中,靜態變量是分配在數據段中的,動態變量是分配在堆棧段的。緩衝區溢出是利用堆棧段的溢出的。spa

當程序中發生函數調用時,計算機作以下操做:首先把參數壓入堆棧;而後保存指令寄存器(IP)中的內容,作爲返回地址(RET);第三個放入堆棧的是基址寄存器(FP);而後把當前的棧指針(SP)拷貝到FP,作爲新的基地址;最後爲本地變量留出必定空間,把SP減去適當的數值。 指針

 

好比說下面這個程序: 
void function(int a, int b, int c){ 
char buffer1[10]; 
char buffer2[15]; 

void main(){ 
function(1,2,3); 

假設咱們在Linux下,用gcc對這段源碼進行編譯,產生彙編代碼輸出:
 
  
$ gcc -S -o example1.s example1.c 
  看看輸出文件中調用函數的那部分:
 
  
pushl $3 
  
pushl $2 
  
pushl $1 
  
call function 
  這就將3個參數推入堆棧裏了,並調用function()。指令call會將指令指針IP壓入堆棧。在返回時,RET要用到這個保存的IP。在函數中,第一要作的事是進行一些必要的處理。每一個函數都必須有這些過程(爲了保護呀,否則就找不到了。):
 

  
 
  
pushl %ebp 
  
movl %esp,%ebp 
  
subl $20,%esp 
這幾條指令將EBP,基址指針放入堆棧。而後將當前SP拷貝到EBP。而後,爲本地變量分配空間,並將它們的大小從SP裏減掉。因爲內存分配是以字爲單位的,所以,這裏的buffer1用了8字節(2個字,一個字4字節)。Buffer2用了12字節(3個字)。因此這裏將ESP減了20。這樣,如今,堆棧看起來應該是這樣的。
 


  低端內存 高端內存
 
  
 buffer2 buffer1 sfp ret a b c 
  
< ------ [ ] [ ] [ ] [ ] [ ] [ ] [ ] 
  棧頂 棧底
 
  那是什麼致使了溢出呢?緩衝區溢出就是在一個緩衝區裏寫入過多的數據。要怎麼樣利用呢?看下面這個程序:
 
void function(char *str) { 
  
char buffer[16]; 
  
 
  
strcpy(buffer,str); 
  


   
void main() { 
  
char large_string[256]; 
  
int i; 
  
 
  
for( i = 0; i < 255; i++) 
  
large_string = 'A'; 
   
  
function(large_string); 
  


  這個程序是一個經典的緩衝區溢出編碼錯誤。函數將一個字符串不通過邊界檢查,拷貝到另外一內存區域。當調用函數function()時,堆棧以下:
 

  
 
  低內存端 高內存端
 
buffer sfp ret *str 
  
< ------ [ ] [ ] [ ] [ ] 
  棧頂 棧底
 
很明顯,程序執行的結果是
&quot;Segmentation fault (core 
dumped)&quot;
或相似的出錯信息。由於從buffer開始的256個字節都將被*str的內容'A'覆蓋,包括
sfp, 
ret,
甚至*str'A'的十六進值爲0x41,因此函數的返回地址變成了
0x41414141, 
這超出了程序的地址空間,因此出現段錯誤。可見,緩衝區溢出容許咱們改變一個函數的返回地址,在這個例子中,咱們能夠經過修改0x41414141來改變程序返回後的入口地址。一樣經過這種方式,就能夠改變程序的執行順序了。
 

存在象strcpy這樣的問題的標準函數還有strcat(),sprintf(),vsprintf(),gets(),scanf(),以及在循環內的getc(),fgetc(),getchar()等。
 
blog

 

 

 

 

 

 

原創做品,容許轉載,轉載時請務必以超連接形式標明文章 原始出處 、做者信息和本聲明。不然將追究法律責任。http://stantic.blog.51cto.com/2179706/402708內存

相關文章
相關標籤/搜索