溢出
溢出是在你自己電腦上能夠運(yùn)行的東西他可以全部做到,等于你的電腦就是他的了。在黑客頻頻攻擊、在系統(tǒng)漏洞層出不窮的今天,作為網(wǎng)絡(luò)管理員、系統(tǒng)管理員的我們雖然在服務(wù)器的安全上都下了不少功夫:諸如,及時(shí)的打上系統(tǒng)安全補(bǔ)丁、進(jìn)行一些常規(guī)的安全配置,但是仍然不太可能每臺(tái)服務(wù)器都會(huì)在第一時(shí)間內(nèi)給系統(tǒng)打上全新補(bǔ)丁。因此我們必需要在還未被入侵之前,通過一些系列安全設(shè)置,來將入侵者們擋在“安全門”之外。
內(nèi)存溢出已經(jīng)是軟件開發(fā)歷史上存在了近40年的“老大難”問題,象在“紅色代碼”病毒事件中表現(xiàn)的那樣,它已經(jīng)成為黑客攻擊企業(yè)網(wǎng)絡(luò)的“罪魁禍?zhǔn)住薄?BR>
如在一個(gè)域中輸入的數(shù)據(jù)超過了它的要求就會(huì)引發(fā)數(shù)據(jù)溢出問題,多余的數(shù)據(jù)就可以作為指令在計(jì)算機(jī)上運(yùn)行。據(jù)有關(guān)安全小組稱,操作系統(tǒng)中超過50%的安全漏洞都是由內(nèi)存溢出引起的,其中大多數(shù)與微軟的技術(shù)有關(guān)。
微軟的軟件是針對(duì)臺(tái)式機(jī)開發(fā)的,內(nèi)存溢出不會(huì)帶來嚴(yán)重的問題。但現(xiàn)在臺(tái)式機(jī)一般都連上了互聯(lián)網(wǎng),內(nèi)存溢出就為黑客的入侵提供了便利條件。
為什么會(huì)出現(xiàn)內(nèi)存溢出問題
導(dǎo)致內(nèi)存溢出問題的原因有很多,比如:
(1) 使用非類型安全(non-type-safe)的語言如 C/C++ 等。
(2) 以不可靠的方式存取或者復(fù)制內(nèi)存緩沖區(qū)。
(3) 編譯器設(shè)置的內(nèi)存緩沖區(qū)太靠近關(guān)鍵數(shù)據(jù)結(jié)構(gòu)。
下面來分析這些因素:
1. 內(nèi)存溢出問題是 C 語言或者 C++ 語言所固有的缺陷,它們既不檢查數(shù)組邊界,又不檢查類型可靠性(type-safety)。眾所周知,用 C/C++ 語言開發(fā)的程序由于目標(biāo)代碼非常接近機(jī)器內(nèi)核,因而能夠直接訪問內(nèi)存和寄存器,這種特性大大提升了 C/C++ 語言代碼的性能。只要合理編碼,C/C++ 應(yīng)用程序在執(zhí)行效率上必然優(yōu)于其它高級(jí)語言。然而,C/C++ 語言導(dǎo)致內(nèi)存溢出問題的可能性也要大許多。其他語言也存在內(nèi)容溢出問題,但它往往不是程序員的失誤,而是應(yīng)用程序的運(yùn)行時(shí)環(huán)境出錯(cuò)所致。
2. 當(dāng)應(yīng)用程序讀取用戶(也可能是惡意攻擊者)數(shù)據(jù),試圖復(fù)制到應(yīng)用程序開辟的內(nèi)存緩沖區(qū)中,卻無法保證緩沖區(qū)的空間足夠時(shí)(換言之,假設(shè)代碼申請(qǐng)了 N 字節(jié)大小的內(nèi)存緩沖區(qū),隨后又向其中復(fù)制超過 N 字節(jié)的數(shù)據(jù))。內(nèi)存緩沖區(qū)就可能會(huì)溢出。想一想,如果你向 12 盎司的玻璃杯中倒入 16 盎司水,那么多出來的 4 盎司水怎么辦?當(dāng)然會(huì)滿到玻璃杯外面了!
3. 最重要的是,C/C++ 編譯器開辟的內(nèi)存緩沖區(qū)常常鄰近重要的數(shù)據(jù)結(jié)構(gòu),F(xiàn)在假設(shè)某個(gè)函數(shù)的堆棧緊接在在內(nèi)存緩沖區(qū)后面時(shí),其中保存的函數(shù)返回地址就會(huì)與內(nèi)存緩沖區(qū)相鄰。此時(shí),惡意攻擊者就可以向內(nèi)存緩沖區(qū)復(fù)制大量數(shù)據(jù),從而使得內(nèi)存緩沖區(qū)溢出并覆蓋原先保存于堆棧中的函數(shù)返回地址。這樣,函數(shù)的返回地址就被攻擊者換成了他指定的數(shù)值;一旦函數(shù)調(diào)用完畢,就會(huì)繼續(xù)執(zhí)行“函數(shù)返回地址”處的代碼。非但如此,C++ 的某些其它數(shù)據(jù)結(jié)構(gòu),比如 v-table 、例外事件處理程序、函數(shù)指針等,也可能受到類似的攻擊。
好,閑話少說,現(xiàn)在來看一個(gè)具體的例子。
請(qǐng)思考:以下代碼有何不妥之處?
void CopyData(char *szData) {
char cDest[32];
strcpy(cDest,szData);
// 處理 cDest
...
}
奇怪,這段代碼好像沒什么不對(duì)勁!確實(shí),只有調(diào)用上述 CopyData() 才會(huì)出問題。例如:這樣使用 CopyData() 是安全的:
char *szNames[] = {"Michael","Cheryl","Blake"};
CopyData(szName[1]);
為什么呢?因?yàn)閿?shù)組中的姓名("Michael"、"Cheryl"、"Blake")都是字符串常量,而且長(zhǎng)度都不超過 32 個(gè)字符,用它們做 strcpy() 的參數(shù)總是安全的。再假設(shè) CopyData 的唯一參數(shù) szData 來自 socket 套接字或者文件等不可靠的數(shù)據(jù)源。由于 strcpy 并不在乎數(shù)據(jù)來源,只要沒遇上空字符,它就會(huì)一個(gè)字符一個(gè)字符地復(fù)制 szData 的內(nèi)容。此時(shí),復(fù)制到 cDest 的字符串就可能超過 32 字符,進(jìn)而導(dǎo)致內(nèi)存緩沖區(qū) cDest 的溢出;溢出的字符就會(huì)取代內(nèi)存緩沖區(qū)后面的數(shù)據(jù)。不幸的是,CopyData 函數(shù)的返回地址也在其中!于是,當(dāng) CopyData 函數(shù)調(diào)用完畢以后,程序就會(huì)轉(zhuǎn)入攻擊者給出的“返回地址”,從而落入攻擊者的圈套!授人以柄,慘!
前面提到的其它數(shù)據(jù)結(jié)構(gòu)也可能受到類似的攻擊。假設(shè)有人利用內(nèi)存溢出漏洞覆蓋了下列 C++ 類中的 v-table :
void CopyData(char *szData) {
char cDest[32];
CFoo foo;
strcpy(cDest,szData);
foo.Init();
}
與其它 C++ 類一樣,這里的 CFoo 類也對(duì)應(yīng)一個(gè)所謂的 v-table,即用于保存一個(gè)類的全部方法地址的列表。若攻擊者利用內(nèi)存溢出漏洞偷換了 v-table 的內(nèi)容,則 CFoo 類中的所有方法,包括上述 Init() 方法,都會(huì)指向攻擊者給出的地址,而不是原先 v-table 中的方法地址。順便說一句,即使你在某個(gè) C++ 類的源代碼中沒有調(diào)用任何方法,也不能認(rèn)為這個(gè)類是安全的,因?yàn)樗谶\(yùn)行時(shí)至少需要調(diào)用一個(gè)內(nèi)部方法