溢出的原因,現實狀況,客觀原因,普遍因素,溢出解決方法,受控代碼,黃金規則,編譯選項,溢出分類,緩衝區溢出,記憶體溢出,數據溢出,溢出原因,因素分析,防溢技術套用,基於探測方法,堆疊防禦,
溢出的原因 現實狀況 在幾乎所有計算機語言中,不管是新的語言還是舊的語言,使緩衝區溢出的任何嘗試通常都會被該語言本身自動檢測並阻止(比如通過引發一個異常或根據需要給緩衝區添加更多空間)。但是有兩種語言不是這樣:C 和 C++ 語言。C 和 C++ 語言通常只是讓額外的數據亂寫到其餘記憶體的任何位置,而這種情況可能被利用從而導致恐怖的結果。更糟糕的是,用 C 和 C++ 編寫正確的代碼來始終如一地處理緩衝區溢出則更為困難;很容易就會意外地導致緩衝區溢出。除了 C 和 C++ 使用得 非常廣泛外,上述這些可能都是不相關的事實;例如,Red Hat Linux 7.1 中 86% 的代碼行都是用 C 或 C ++ 編寫的。因此,大量的代碼對這個問題都是脆弱的,因為實現語言無法保護代碼避免這個問題。
客觀原因 在 C 和 C++ 語言本身中,這個問題是不容易解決的。該問題基於 C 語言的根本設計決定(特別是 C 語言中
指針 和
數組 的處理方式)。由於 C++ 是最兼容的 C 語言
超集 ,它也具有相同的問題。存在一些能防止這個問題的 C/C++ 兼容版本,但是它們存在極其嚴重的性能問題。而且一旦改變 C 語言來防止這個問題,它就不再是 C 語言了。許多語言(比如 Java 和 C#)在語法上類似 C,但它們實際上是不同的語言,將現有 C 或 C++ 程式改為使用那些語言是一項艱巨的任務。
普遍因素 然而,其他語言的用戶也不應該沾沾自喜。有些語言存在允許緩衝區溢出發生的“轉義”子句。Ada 一般會檢測和防止緩衝區溢出(即針對這樣的嘗試引發一個異常),但是不同的程式可能會禁用這個特性。C# 一般會檢測和防止緩衝區溢出,但是它允許程式設計師將某些例程定義為“不安全的”,而這樣的代碼 可能 會導致緩衝區溢出。因此如果您使用那些轉義機制,就需要使用 C/C++ 程式所必須使用的相同種類的保護機制。許多語言都是用 C 語言來實現的(至少部分是用 C 語言來實現的 ),並且用任何語言編寫的所有程式本質上都依賴用 C 或 C++ 編寫的庫。因此,所有程式都會繼承那些問題,所以了解這些問題是很重要的。
溢出解決方法 受控代碼 2002 年 2 月和 3 月,
微軟公司 展開了 Microsoft Windows Security Push 活動。在此期間,我所在的小組一共培訓了超過 8500 人,教授他們如何在設計、測試和
文檔編制 過程中解決安全問題。在我們向所有程式設計人員提出的建議中,有一條就是:緊跟微軟公司軟體開發策略的步伐,將某些應用程式和
工具軟體 由原先基於本地 Win32 的 C++ 代碼改造成基於 .NET 的受控代碼。我們的理由很多,但其中最根本的一條,就是為了解決
記憶體溢出 問題。基於受控代碼的軟體發生記憶體溢出問題的機率要小得多,因為受控代碼無法直接存取系統
指針 、暫存器或者記憶體。作為開發人員,你應該考慮(至少是打算)用受控代碼改寫某些應用程式或工具軟體。例如:企業管理工具就是很好的改寫對象之一。當然,你也應該很清楚,不可能在一夜之間把所有用 C++ 開發的軟體用 C# 之類的受控代碼語言改寫。
黃金規則 當你用 C/C++ 書寫代碼時,應該處處留意如何處理來自用戶的數據。如果一個函式的數據來源不可靠,又用到記憶體緩衝區,那么它就必須嚴格遵守下列規則:
必須知道記憶體緩衝區的總長度。
檢驗記憶體緩衝區。
提高警惕。
讓我們來具體看看這些“黃金規則”:
(1)必須知道記憶體緩衝區的總長度。
類似這樣的代碼就有可能導致 bug :
void Function(char *szName) {
char szBuff[MAX_NAME];
// 複製並使用 szName
strcpy(szBuff,szName);
}
它的問題出在函式並不知道 szName 的長度是多少,此時複製數據是不安全的。正確的做法是,在複製數據前首先獲取 szName 的長度:
void Function(char *szName, DWORD cbName) {
char szBuff[MAX_NAME];
// 複製並使用 szName
if (cbName < MAX_NAME)
strcpy(szBuff,szName);
}
這樣雖然有所改進,可它似乎又過於信任 cbName 了。攻擊者仍然有機會偽造 czName 和 szName 兩個參數以謊報數據長度和記憶體緩衝區長度。因此,你還得檢檢這兩個參數!
(2)檢驗記憶體緩衝區
如何知道由參數傳來的記憶體緩衝區長度是否真實呢?你會完全信任來自用戶的數據嗎?通常,答案是否定的。其實,有一種簡單的辦法可以檢驗記憶體緩衝區是否溢出。請看如下代碼片斷:
void Function(char *szName, DWORD cbName) {
char szBuff[MAX_NAME];
// 檢測記憶體
szBuff[cbName] = 0x42;
// 複製並使用 szName
if (cbName < MAX_NAME)
strcpy(szBuff,szName);
}
這段代碼試圖向欲檢測的記憶體緩衝區末尾寫入數據 0x42 。你也許會想:真是多此一舉,何不直接複製記憶體緩衝區呢?事實上,當記憶體緩衝區已經溢出時,一旦再向其中寫入
常量 值,就會導致程式代碼出錯並中止運行。這樣在開發早期就能及時發現代碼中的 bug 。試想,與其讓攻擊者得手,不如及時中止程式;你大概也不願看到攻擊者隨心所欲地向記憶體緩衝區複製數據吧。
(3)提高警惕
雖然檢驗記憶體緩衝區能夠有效地減小
記憶體溢出 問題的危害,卻不能從根本上避免記憶體溢出攻擊。只有從
原始碼 開始提高警惕,才能真正免除記憶體溢出攻擊的危脅。不錯,上一段代碼已經很對用戶數據相當警惕了。它能確保複製到記憶體緩衝區的數據總長度不會超過 szBuff 的長度。然而,某些函式在使用或複製不可靠的數據時也可能潛伏著記憶體溢出漏洞。為了檢查你的代碼是否存在記憶體溢出漏洞,你必須儘量追蹤傳入數據的流向,向代碼中的每一個假設提出質疑。一旦這么做了,你將會意識到其中某些假設是錯誤的;然後你還會驚訝地叫道:好多 bug 呀!
在調用 strcpy、strcat、gets 等經典函式時當然要保持警惕;可對於那些所謂的第 n 版 (n-versions) strcpy 或 strcat 函式 —— 比如 strncpy 或 strncat (其中 n = 1,2,3 ……) —— 也不可輕信。的確,這些改良版本的函式是安全一些、可靠一些,因為它們限制了進入記憶體緩衝區的數據長度;然而,它們也可能導致
記憶體溢出 問題!請看下列代碼,你能指出其中的錯誤嗎?
#define SIZE(b) (sizeof(b))
char buff[128];
strncpy(buff,szSomeData,SIZE(buff));
strncat(buff,szMoreData,SIZE(buff));
strncat(buff,szEvenMoreData,SIZE(buff));
給點提示:請注意這些
字元串函式 的最後一個參數。怎么,棄權?我說啊,如果你是執意要放棄那些“不安全”的經典字元串函式,並且一律改用“相對安全”的第 n 版函式的話,恐怕你這下半輩子都要為了修復這些新函式帶來的新 bug 而疲於奔命了。呵呵,開個玩笑而已。為何這么說?首先,它們的最後一個參數並不代表記憶體緩衝區的總長度,它們只是其剩餘空間的長度;不難看出,每執行完一個 strn... 函式,記憶體緩衝區 buff 的長度就減少一些。其次,傳遞給函式的記憶體緩衝區長度難免存在“大小差一”(off-by-one)的誤差。你認為在計算 buff 的長度時包括了字元串末尾的空字元嗎?當我向聽眾提出這個問題時,得到的肯定答覆和否定答覆通常是 50 比 50 ,對半開。也就是說,大約一半人認為計算了末尾的空字元,而另一半人認為忽略了該字元。最後,那些第 n 版函式所返回的字元串不見得以空字元結束,所以在使用前務必要仔細閱讀它們的說明文檔。
編譯選項 “/GS”是
Visual C++ .NET 新引入的一個編譯選項。它指示
編譯器 在某些函式的
堆疊 幀(stack-frames) 中插入特定數據,以幫助消除針對堆疊的
記憶體溢出 問題隱患。切記,使用該選項並不能替你
清除代碼 中的記憶體溢出漏洞,也不可能消滅任何 bug 。它只是亡羊補牢,讓某些記憶體溢出問題隱患無法演變成真正的記憶體溢出問題;也就是說,它能防止攻擊者在發生記憶體溢出時向進程中插入和運行惡意代碼。無論如何,這也算是小小的安全保障吧。請注意,在新版的本地 Win32 C++ 中使用 Win32
應用程式嚮導 創建新項目時,默認設定已經打開了此選項。同樣,Windows .NET Server 環境也默認打開了此選項。關於 /GS 選項的更多信息,請參考 Brandon Bray 的《Compiler Security Checks In Depth》一書。
所謂
定點數 溢出是指定點數的運算結果的絕對值大於計算機能表示的最大數的絕對值。
浮點數 的溢出又可分為“上溢出”和“
下溢出 ”兩種,“上溢出”與整數、定點數的含義相同,“下溢出”是指浮點數的運算結果的絕對值小於機器所能表示的最小數絕對值,此時將該運算結果處理成
機器零 。若發現溢出(上溢出),
運算器 將產生溢出標誌或發出
中斷請求 ,當溢出中斷未被禁止時,溢出中斷信號的出現可中止程式的執行而轉入溢出
中斷處理 程式。
例如:有兩個數0.1001111和0.1101011相加,其結果應為1.0111010。由於
定點數 計算機只能表示小於1的數,如果
字長 只有8位,那么小數點前面的1,會因沒有觸發器存放而丟失。這樣,上述兩個數在計算機中相加,其結果變為0.0111010。若字長只有8位,則結果顯示為0.0000000,後面的1個0和6個1全部丟失,顯然這個結果有誤差。計算機的任何運算都不允許溢出,除非專門利用溢出做判斷,而不使用所得的結果。所以,當發生和不允許出現的溢出時,就要停機或轉入檢查程式,以找出產生溢出的原因,做出相應的處理。
溢出分類 緩衝區溢出 緩衝區是用戶為程式運行時在計算機中申請的一段連續的記憶體,它保存了給定類型的數據。
緩衝區溢出 指的是一種常見且危害很大的系統攻擊手段,通過向程式的
緩衝區 寫入超出其長度的內容,造成緩衝區的溢出,從而破壞程式的
堆疊 ,使程式轉而執行其他的指令,以達到攻擊的目的。更為嚴重的是,
緩衝區溢出攻擊 占了遠程網路攻擊的絕大多數,這種攻擊可以使得一個匿名的
Internet 用戶有機會獲得一台
主機 的部分或全部的控制權!由於這類攻擊使任何人都有可能取得主機的控制權,所以它代表了一類極其嚴重的安全威脅。
緩衝區溢出攻擊的目的在於擾亂具有某些特權運行的程式的功能,這樣可以使得攻擊者取得程式的控制權,如果該程式具有足夠的許可權,那么整個主機就被控制了。一般而言,攻擊者攻擊root程式,然後執行類似“exec(sh)”的執行代碼來獲得root的shell。為了達到這個目的,攻擊者必須達到如下的兩個目標:在程式的
地址空間 里安排適當的代碼;通過適當地初始化
暫存器 和
存儲器 ,讓程式跳轉到事先安排的地址空間執行。根據這兩個目標,可以將
緩衝區溢出攻擊 分為以下3類。
【緩衝區溢出分類】
控制程式轉移到攻擊代碼
這種方法指在改變程式的執行流程,使之跳轉到攻擊代碼。最基本方法的就是溢出一個沒有邊界檢查或者其他弱點的緩衝區,這樣就擾亂了程式的正常的執行順序。通過溢出一個緩衝區,攻擊者可以用近乎暴力的方法改寫相鄰的程式空間而直接跳過了系統的檢查。
1.2.1
激活 紀錄(Activation Records)
每當一個函式調用發生時,調用者會在
堆疊 中留下一個激活紀錄,它包含了函式結束時返回的地址。攻擊者通過溢出這些
自動變數 ,使這個返回地址指向攻擊代碼。通過改變程式的返回地址,當
函式調用 結束時,程式就跳轉到攻擊者設定的地址,而不是原先的地址。這類的緩衝區溢出被稱為“stack smashing attack”,是目.前常用的緩衝區溢出攻擊方式。
1.2.2函式指針(Function Pointers)
C語言 中,“void (* foo)()”聲明了一個返回值為void
函式指針 的變數foo。函式指針可以用來定位任何
地址空間 ,所以攻擊者只需在任何空間內的函式指針附近找到一個能夠溢出的緩衝區,然後溢出這個緩衝區來改變函式指針。在某一時刻,當程式通過函式指針調用函式時,程式的流程就按攻擊者的意圖實現了!它的一個攻擊範例就是在Linux系統下的super probe程式。
1.2.3長跳轉緩衝區(Longjmp buffers)
在C語言中包含了一個簡單的檢驗/恢復系統,稱為setjmp/longjmp。意思是在檢驗點設定“setjmp(buffer)”,用“longjmp(buffer)”來恢復檢驗點。然而,如果攻擊者能夠進入緩衝區的空間,那么“longjmp(buffer)”實際上是跳轉到攻擊者的代碼。象
函式指針 一樣,longjmp緩衝區能夠指向任何地方,所以攻擊者所要做的就是找到一個可供溢出的緩衝區。一個典型的例子就是Perl 5.003,攻擊者首先進入用來恢復緩衝區溢出的的longjmp緩衝區,然後誘導進入恢復模式,這樣就使Perl的
解釋器 跳轉到攻擊代碼上了!
最簡單和常見的
緩衝區溢出攻擊 類型就是在一個字元串里綜合了代碼殖入和激活紀錄。攻擊者定位一個可供溢出的
自動變數 ,然後向程式傳遞一個很大的字元串,在引發緩衝區溢出改變激活紀錄的同時殖入了代碼。這個是由Levy指出的攻擊的模板。因為C語言在習慣上只為用戶和參數開闢很小的緩衝區,因此這種漏洞攻擊的實例不在少數。
代碼殖入和緩衝區溢出不一定要在一次動作內完成。攻擊者可以在一個緩衝區內放置代碼,這是不能溢出緩衝區。然後,攻擊者通過溢出另外一個緩衝區來轉移程式的指針。這種方法一般用來解決可供溢出的緩衝區不夠大的情況。
如果攻擊者試圖使用已經常駐的代碼而不是從外部殖入代碼,他們通常有必須把代碼作為參數化。舉例來說,在libc中的部分
代碼段 會執行“exec(something)”,其中something就是參數。攻擊者然後使用緩衝區溢出改變程式的參數,利用另一個緩衝區溢出使程式
指針 指向libc中的特定的代碼段。
記憶體溢出 記憶體溢出 已經是軟體開發歷史上存在了近40年的“老大難”問題,象在“
紅色代碼 ”病毒事件中表現的那樣,它已經成為
黑客攻擊 企業網路的“罪魁禍首”。
如在一個域中輸入的數據超過了它的要求就會引發數據溢出問題,多餘的數據就可以作為指令在計算機上運行。據有關安全小組稱,作業系統中超過50%的
安全漏洞 都是由記憶體溢出引起的,其中大多數與微軟的技術有關。
微軟的軟體是針對台式機開發的,記憶體溢出不會帶來嚴重的問題。但現有台式機一般都連上了網際網路,
記憶體溢出 就為黑客的入侵提供了便利條件。
網速溢出 數據溢出 在計算機中,當要表示的數據超出計算機所使用的數據的表示範圍時,則產生數據的溢出。
溢出原因 數據類型超過了計算機
字長 的界限就會出現數據溢出的情況。導致
記憶體溢出 問題的原因有很多,比如:
(1) 使用非
類型安全 (non-type-safe)的語言如 C/C++ 等。
(2) 以不可靠的方式存取或者複製記憶體緩衝區。
(3)
編譯器 設定的記憶體緩衝區太靠近關鍵數據結構。
因素分析 1.
記憶體溢出 問題是 C 語言或者 C++ 語言所固有的缺陷,它們既不檢查
數組 邊界,又不檢查類型可靠性(type-safety)。眾所周知,用 C/C++ 語言開發的程式由於
目標代碼 非常接近機器核心,因而能夠直接訪問記憶體和暫存器,這種特性大大提升了 C/C++ 語言代碼的性能。只要合理編碼,C/C++應用程式在執行效率上必然優於其它高級語言。然而,C/C++ 語言導致記憶體溢出問題的可能性也要大許多。其他語言也存在記憶體溢出問題,但它往往不是程式設計師的失誤,而是應用程式的運行時環境出錯所致。
2. 當應用程式讀取用戶(也可能是惡意攻擊者)數據,試圖複製到應用程式開闢的記憶體緩衝區中,卻無法保證緩衝區的空間足夠時(換言之,假設代碼申請了 N 位元組大小的記憶體緩衝區,隨後又向其中複製超過 N 位元組的數據)。記憶體緩衝區就可能會溢出。想一想,如果你向 12 盎司的玻璃杯中倒入 16 盎司水,那么多出來的 4 盎司水怎么辦?當然會滿到玻璃杯外面了!
3. 最重要的是,C/C++
編譯器 開闢的記憶體緩衝區常常鄰近重要的數據結構。假設某個函式的
堆疊 緊接在在記憶體緩衝區後面時,其中保存的函式返回地址就會與記憶體緩衝區相鄰。此時,惡意攻擊者就可以向記憶體緩衝區複製大量數據,從而使得記憶體緩衝區溢出並覆蓋原先保存於堆疊中的函式返回地址。這樣,函式的返回地址就被攻擊者換成了他指定的數值;一旦
函式調用 完畢,就會繼續執行“函式返回地址”處的代碼。非但如此,C++ 的某些其它數據結構,比如 v-table 、例外事件處理程式、
函式指針 等,也可能受到類似的攻擊。
具體的例子
請思考:以下代碼有何不妥之處?
void CopyData(char *szData) {
char cDest[32];
strcpy(cDest,szData);
// 處理 cDest
...
}
奇怪,這段代碼好像沒什麼不對勁啊!確實,只有調用上述 CopyData() 才會出問題。例如:這樣使用 CopyData() 是安全的:
char *szNames[] = {"Michael","Cheryl","Blake"};
CopyData(szName[1]);
為什麼呢?因為
數組 中的姓名("Michael"、"Cheryl"、"Blake")都是字元串常量,而且長度都不超過 32 個字元,用它們做 strcpy() 的參數總是安全的。再假設 CopyData 的唯一參數 szData 來自 socket
套接字 或者檔案等不可靠的數據源。由於 strcpy 並不在乎數據來源,只要沒遇上空字元,它就會一個字元一個字元地複製 szData 的內容。此時,複製到 cDest 的字元串就可能超過 32 字元,進而導致記憶體緩衝區 cDest 的溢出;溢出的字元就會取代記憶體緩衝區後面的數據。不幸的是,CopyData 函式的返回地址也在其中!於是,當 CopyData
函式調用 完畢以後,程式就會轉入攻擊者給出的“返回地址”,從而落入攻擊者的圈套!授人以柄,慘!
前面提到的其它數據結構也可能受到類似的攻擊。假設有人利用記憶體溢出漏洞覆蓋了下列 C++ 類中的 v-table :
void CopyData(char *szData) {
CFoo foo;
char cDest[32];
strcpy(cDest,szData);
foo.Init();
}
與其它 C++ 類一樣,這裡的 CFoo 類也對應一個所謂的 v-table,即用於保存一個類的全部方法地址的列表。若攻擊者利用記憶體溢出漏洞偷換了 v-table 的內容,則 CFoo 類中的所有方法,包括上述 Init() 方法,都會指向攻擊者給出的地址,而不是原先 v-table 中的方法地址。順便說一句,即使你在某個 C++ 類的
原始碼 中沒有調用任何方法,也不能認為這個類是安全的,因為它在運行時至少需要調用一個內部方法——
析構器 (destructor)!當然,如果真有一個類沒有調用任何方法,那么它的存在意義也就值得懷疑了。
防溢技術套用 基於探測方法 研究人員 Crispen Cowan 創建了一個稱為 StackGuard 的有趣方法。Stackguard 修改 C
編譯器 (gcc),以便將一個“探測”值插入到返回地址的前面。“探測儀”就像煤礦中的探測儀:它在
某個地方 出故障時發出警告。在任何函式返回之前,它執行檢查以確保探測值沒有改變。如果攻擊者改寫返回地址(作為 stack-smashing 攻擊的一部分),探測儀的值或許就會改變,系統內就會相應地中止。這是一種有用的方法,不過要注意這種方法無法防止緩衝區溢出改寫其他值(攻擊者仍然能夠利用這些值來攻擊系統)。人們也曾擴展這種方法來保護其他值(比如堆上的值)。Stackguard(以及其他防禦措施)由 Immunix 所使用。
IBM 的 stack-smashing 保護程式(ssp,起初名為 ProPolice)是 StackGuard 的方法的一種變化形式。像 StackGuard 一樣,ssp 使用一個修改過的
編譯器 在
函式調用 中插入一個探測儀以檢測
堆疊溢出 。然而,它給這種基本的思路添加了一些有趣的變化。 它對存儲
局部變數 的位置進行重新排序,並複製函式參數中的
指針 ,以便它們也在任何
數組 之前。這樣增強了ssp 的保護能力;它意味著緩衝區溢出不會修改指針值(否則能夠控制指針的攻擊者就能使用指針來控制程式保存數據的位置)。默認情況下,它不會檢測所有函式,而只是檢測確實需要保護的函式(主要是使用
字元 數組的函式)。從理論上講,這樣會稍微削弱保護能力,但是這種默認行為改進了性能,同時仍然能夠防止大多數問題。考慮到實用的因素,它們以獨立於
體系結構 的方式使用 gcc 來實現它們的方法,從而使其更易於運用。從 2003 年 5 月的發布版本開始,廣受讚譽的 OpenBSD(它重點關注安全性)在他們的整個發行套件中使用了 ssp(也稱為 ProPolice)。
Microsoft 基於 StackGuard 的成果,添加了一個
編譯器 標記(/GS)來實現其 C 編譯器中的探測儀。
堆疊防禦 另一種方法首先使得在堆疊上執行代碼變得不可能。 遺憾的是,x86 處理器(最常見的處理器)的記憶體保護機制無法容易地支持這點;通常,如果一個記憶體頁是可讀的,它就是可執行的。一個名叫 Solar Designer 的開發人員想出了一種
核心 和處理器機制的聰明組合,為 Linux 核心創建了一個“非執行的堆疊補丁”;有了這個補丁,堆疊上的程式就不再能夠像通常的那樣在 x86 上運行。 事實證明在有些情況下,可執行程式 需要在堆疊上;這包括信號處理和跳板代碼(trampoline)處理。trampoline 是有時由
編譯器 (比如 GNAT Ada 編譯器)生成的奇妙結構,用以支持像嵌套
子例程 之類的結構。Solar Designer 還解決了如何在防止攻擊的同時使這些特殊情況不受影響的問題。
Linux 中實現這個目的的最初補丁在 1998 年被 Linus Torvalds 拒絕,這是因為一個有趣的原因。即使不能將代碼放到
堆疊 上,攻擊者也可以利用緩衝區溢出來使程式“返回”某個現有的子例程(比如 C 庫中的某個子例程),從而進行攻擊。簡而言之,僅只是擁有非可執行的堆疊是不足夠的。
一段時間之後,人們又想出了一種防止該問題的新思路:將所有
可執行代碼 轉移到一個稱為“ASCII 保護(ASCII armor)”區域的記憶體區。要理解這是如何工作的,就必須知道攻擊者通常不能使用一般的
緩衝區溢出攻擊 來插入 ASCII NUL
字元 (0)這個事實。 這意味著攻擊者會發現,要使一個程式返回包含 0 的地址是很困難的。由於這個事實,將所有可執行代碼轉移到包含 0 的地址就會使得攻擊該程式困難多了。
具有這個屬性的最大連續記憶體範圍是從 0 到 0x01010100 的一組
記憶體地址 ,因此它們就被命名為 ASCII 保護區域(還有具有此屬性的其他地址,但它們是分散的)。與非可執行的
堆疊 相結合,這種方法就相當有價值了:非可執行的堆疊阻止攻擊者傳送
可執行代碼 ,而 ASCII 保護記憶體使得攻擊者難於通過利用現有代碼來繞過非可執行堆疊。這樣將保護程式代碼避免堆疊、緩衝區和
函式指針 溢出,而且全都不需重新編譯。
然而,ASCII 保護記憶體並不適用於所有程式;大程式也許無法裝入 ASCII 保護記憶體區域(因此這種保護是不完美的),而且有時攻擊者 能夠將 0 插入目的地址。 此外,有些實現不支持跳板代碼,因此可能必須對需要這種保護的程式禁用該特性。Red Hat 的 Ingo Molnar 在他的“exec-shield”補丁中實現了這種思想,該補丁由 Fedora 核心(可從 Red Hat 獲得它的免費版本)所使用。最新版本的 OpenWall GNU/Linux (OWL)使用了 Solar Designer 提供的這種方法的實現(請參閱 參考資料 以獲得指向這些版本的連結)。