標籤:des style blog color 使用 資料
時間:2014.07.03
地點:基地
-----------------------------------------------------------------------------
一、什麼是記憶體溢出
在記憶體空間中,當要表示的資料超出電腦為該資料分配的空間範圍時或者訪問超出為資料分配的空間的記憶體範圍時,或者為資料分配好了給定大小的記憶體,而我們對超出該部分記憶體部分進行操作時就會產生記憶體溢出。作業系統中50%的安全性漏洞都是由記憶體溢出引起的。記憶體溢出是C/C++語言的固有缺陷,因為它們既不檢查數組邊界,也不檢查類型的可靠性。
假設我們的代碼申請了X位元組的記憶體緩衝區,隨後向其複製超過X位元組的資料,那麼多出來的位元組就會溢出原本為之分配的記憶體區。而C/C++編譯器開闢的記憶體緩衝區常常鄰近很重要的資料結構,這樣很容易被惡意攻擊者刻意覆蓋原本安全可信的資料,從而讓機器變為他們肆意攻擊的肉雞。
-----------------------------------------------------------------------------
二、防範措施——針對庫函數
舉例來說,C語言中的字串由於沒有採用相應的安全措施,為此很容易發生記憶體泄露問題。比如strcpy、strcat等函數在執行時不會對緩衝區的大小進行檢查,如此很容易引發安全問題:
const int kMAX_DATA_LENGTH=32;void DataCopy(char* src_data){ char dest_data[kMAX_DATA_LENGTH]; strcpy(dest_data,src_data); //......}上述代碼是危險代碼,輸入資料來源src_data的長度不超過規定的長度,那麼代碼錶現很正常,但因為strcpy()不會檢查資料來源長度,在執行複製操作時能讓它停下來的只有字串中的結束符 ’\0‘ ,只有碰到字串結束符時字串複製才會停止,如果沒碰到這個結束符時,它就一個位元組一個位元組地複製src_data內容,在填滿32位元組預設空間後,溢出的字元部分就會去覆蓋緩衝區後面的記憶體地區,打個比方來說,我們雖然為這個儲存區劃分了一塊地區,但這就像國家領土的劃分,劃界是划了,但人還是可以通過的,但這是很危險的。在這裡若是溢出的資料恰好覆蓋了後面的DataCopy函數的返回地址,那麼在函數調用完畢後,程式就轉入攻擊者設定的返回地址中去了,乖乖地進入了預先設定好的陷阱,執行駭客們的代碼去了。
為了避免這種情況,我們在處理來自使用者的資料時應該處處留意,如果一個函數的資料來源不可靠,又要用到記憶體緩衝區,就必須提供警惕,我們應該獲知記憶體緩衝區的總長度,並檢測記憶體緩衝區,將上述代碼改善如下:
const int kMAX_DATA_LENGTH=32;void DataCopy(char* src_data,DWORD data_len){ char dest_data[kMAX_DATA_LENGTH]; if(data_len<kMAX_DATA_LENGTH strcpy(dest_data,src_data); dest_data[data_len]='\0'; //......}在這裡,我們首先要獲得來源資料src_data的長度,保證資料長度不大於緩衝區長度32,;其次,要保證參數傳來的資料長度真實有效,可以向緩衝區後一個位置處寫入資料,因為,當緩衝區溢位時,一旦向其寫入常量值,代碼就會出錯,與其落入陰謀家的陷阱還不如及時終止程式運行。這種方法只是有效降低了記憶體溢出的危害,無法從根本上避免對記憶體溢出的攻擊。我們在使用諸如strcpy 、strcat、gets等函數時,得從原始碼開始提高警惕,追蹤傳入資料流向,即便是使用所謂的相對安全可靠的改良版本N-Versions(strncpy和strncat)也不可輕信。
-----------------------------------------------------------------------------
三、防範措施——針對數組
當我們訪問邊界資料時同樣肯能引起緩衝區溢位。比如:
const int kDataLength=16;int data[16]={1,2,3,4,5,0,7,8,4,0,3,0,3,2,5,0};void PrintData(){ for(int i=0;data[i]!=0&&i<kDataLength;i++) cout<<data[i]<<endl;}在這裡,但i等於16時,還是會訪問data[16],於是已經越界訪問到非法地區了,於是會引起緩衝區溢位,正確的做法是:不要將索引號i和資料本身data[i]的判斷放在一起。而是將判斷條件分開為兩句,如下:
const int kDataLength=16;int data[16]={1,2,3,4,5,0,7,8,4,0,3,0,3,2,5,0};void PrintData(){ for(int i=0;i<kDataLength;i++; { if(data[i]!=0) cout<<data[i]<<endl;; }}類似的,我們在訪問未初始化的指標或失效指標時,因為未初始化指標和失效為置nullptr的野指標都是指向未知的記憶體空間,對這樣的指標進行操作很有可能訪問或改寫未知的記憶體地區,也就可能引起緩衝區溢位問題。要杜絕這樣的情況。