电竞比分网-中国电竞赛事及体育赛事平台

分享

溢出

 lamar0707 2016-06-15

溢出是程序設計者設計時的不足所帶來的錯誤。溢出是黑客利用操作系統(tǒng)的漏洞,專門開發(fā)了一種程序,加相應的參數運行后,就可以得到用戶電腦具有管理員資格的控制權,用戶在自己電腦上能夠運行的東西他可以全部做到,等于用戶的電腦就是他的了。溢出可分為緩沖區(qū)溢出、內存溢出、數據溢出等多類,使緩沖區(qū)溢出的任何嘗試通常都會被該語言本身自動檢測并阻止。

基本信息

  • 中文名:溢出
  • buffers:長跳轉緩沖區(qū)
  • 激活紀錄:Activation Records
  • 分類:緩沖區(qū)溢出

基本介紹

溢出正在加載溢出

溢出是在你自己電腦上能夠運行的東西他可以全部做到,等于你的電腦就是他的了。在黑客頻頻攻擊、在系統(tǒng)漏洞層出不窮的今天,作為網絡管理員、系統(tǒng)管理員的我們雖然在服務器的安全上都下了不少功夫:諸如,及時的打上系統(tǒng)安全補丁、進行一些常規(guī)的安全配置,但是仍然不太可能每臺服務器都會在第一時間內給系統(tǒng)打上全新補丁。因此我們必需要在還未被入侵之前,通過一些系列安全設置,來將入侵者們擋在“安全門”之外。

溢出分類

1.1在程序的地址空間里安排適當的代碼

1.1.1殖入法

攻擊者用被攻擊程序的緩沖區(qū)來存放攻擊代碼。 攻擊者向被攻擊的程序輸入一個字符串,程序會把這個字符串放到緩沖區(qū)里。這個字符串包含的數據是可以在這個被攻擊的硬件平臺上運行的指令序列。

1.1.2利用已經存在的代碼

有時候,攻擊者想要的代碼已經在被攻擊的程序中了,攻擊者所要做的只是對代碼傳遞一些參數,然后使程序跳轉到指定目標。比如,在C語言中,攻擊代碼要求執(zhí)行“exec('/bin/sh')”,而在libc庫中的代碼執(zhí)行“exec(arg)”,其中arg是指向一個字符串的指針參數,那么攻擊者只要把傳入的參數指針指向'/bin/sh',就可以調轉到libc庫中的相應的指令序列。

1.2控制程序轉移到攻擊代碼

這種方法旨在改變程序的執(zhí)行流程,使之跳轉到攻擊代碼。最基本方法的就是溢出一個沒有邊界檢查或者其他弱點的緩沖區(qū),這樣就擾亂了程序的正常的執(zhí)行順序。通過溢出一個緩沖區(qū),攻擊者可以用近乎暴力的方法改寫相鄰的程序空間而直接跳過了系統(tǒng)的檢查。

1.2.1激活紀錄(Activation Records)

每當一個函數調用發(fā)生時,調用者會在堆棧中留下一個激活紀錄,它包含了函數結束時返回的地址。攻擊者通過溢出這些自動變量,使這個返回地址指向攻擊代碼。通過改變程序的返回地址,當函數調用結束時,程序就跳轉到攻擊者設定的地址,而不是原先的地址。這類的緩沖區(qū)溢出被稱為“stack smashing attack”,是目前常用的緩沖區(qū)溢出攻擊方式。

1.2.2函數指針(Function Pointers)

C語言中,“void (* foo)()”聲明了一個返回值為void函數指針的變量foo。函數指針可以用來定位任何地址空間,所以攻擊者只需在任何空間內的函數指針附近找到一個能夠溢出的緩沖區(qū),然后溢出這個緩沖區(qū)來改變函數指針。在某一時刻,當程序通過函數指針調用函數時,程序的流程就按攻擊者的意圖實現了!它的一個攻擊范例就是在Linux系統(tǒng)下的super probe程序。

1.2.3長跳轉緩沖區(qū)(Longjmp buffers)

在C語言中包含了一個簡單的檢驗/恢復系統(tǒng),稱為setjmp/longjmp。意思是在檢驗點設定“setjmp(buffer)”,用“l(fā)ongjmp(buffer)”來恢復檢驗點。然而,如果攻擊者能夠進入緩沖區(qū)的空間,那么“l(fā)ongjmp(buffer)”實際上是跳轉到攻擊者的代碼。象函數指針一樣,longjmp緩沖區(qū)能夠指向任何地方,所以攻擊者所要做的就是找到一個可供溢出的緩沖區(qū)。一個典型的例子就是Perl 5.003,攻擊者首先進入用來恢復緩沖區(qū)溢出的的longjmp緩沖區(qū),然后誘導進入恢復模式,這樣就使Perl的解釋器跳轉到攻擊代碼上了!

最簡單和常見的緩沖區(qū)溢出攻擊類型就是在一個字符串里綜合了代碼殖入和激活紀錄。攻擊者定位一個可供溢出的自動變量,然后向程序傳遞一個很大的字符串,在引發(fā)緩沖區(qū)溢出改變激活紀錄的同時殖入了代碼。這個是由Levy指出的攻擊的模板。因為C語言在習慣上只為用戶和參數開辟很小的緩沖區(qū),因此這種漏洞攻擊的實例不在少數。

代碼殖入和緩沖區(qū)溢出不一定要在一次動作內完成。攻擊者可以在一個緩沖區(qū)內放置代碼,這是不能溢出緩沖區(qū)。然后,攻擊者通過溢出另外一個緩沖區(qū)來轉移程序的指針。這種方法一般用來解決可供溢出的緩沖區(qū)不夠大的情況。

如果攻擊者試圖使用已經常駐的代碼而不是從外部殖入代碼,他們通常有必須把代碼作為參數化。舉例來說,在libc中的部分代碼段會執(zhí)行“exec(something)”,其中something就是參數。攻擊者然后使用緩沖區(qū)溢出改變程序的參數,利用另一個緩沖區(qū)溢出使程序指針指向libc中的特定的代碼段。

為什么緩沖區(qū)溢出如此常見

在幾乎所有計算機語言中,不管是新的語言還是舊的語言,使緩沖區(qū)溢出的任何嘗試通常都會被該語言本身自動檢測并阻止(比如通過引發(fā)一個異?;蚋鶕枰o緩沖區(qū)添加更多空間)。但是有兩種語言不是這樣:C 和 C++ 語言。C 和 C++ 語言通常只是讓額外的數據亂寫到其余內存的任何位置,而這種情況可能被利用從而導致恐怖的結果。更糟糕的是,用 C 和 C++ 編寫正確的代碼來始終如一地處理緩沖區(qū)溢出則更為困難;很容易就會意外地導致緩沖區(qū)溢出。除了 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++ 程序改為使用那些語言是一項艱巨的任務。

然而,其他語言的用戶也不應該沾沾自喜。有些語言存在允許緩沖區(qū)溢出發(fā)生的“轉義”子句。Ada 一般會檢測和防止緩沖區(qū)溢出(即針對這樣的嘗試引發(fā)一個異常),但是不同的程序可能會禁用這個特性。C# 一般會檢測和防止緩沖區(qū)溢出,但是它允許程序員將某些例程定義為“不安全的”,而這樣的代碼 可能 會導致緩沖區(qū)溢出。因此如果您使用那些轉義機制,就需要使用 C/C++ 程序所必須使用的相同種類的保護機制。許多語言都是用 C 語言來實現的(至少部分是用 C 語言來實現的 ),并且用任何語言編寫的所有程序本質上都依賴用 C 或 C++ 編寫的庫。因此,所有程序都會繼承那些問題,所以了解這些問題是很重要的。

防止緩沖區(qū)溢出的新技術

當然,要讓程序員 不犯常見錯誤是很難的,而讓程序(以及程序員)改為使用另一種語言通常更為困難。那么為何不讓底層系統(tǒng)自動保護程序避免這些問題呢?最起碼,避免 stack-smashing 攻擊是一件好事,因為 stack-smashing 攻擊是特別容易做到的。

一般來說,更改底層系統(tǒng)以避免常見的安全問題是一個極好的想法,我們在本文后面也會遇到這個主題。事實證明存在許多可用的防御措施,而一些最受歡迎的措施可分組為以下類別:

基于探測方法(canary)的防御。這包括 StackGuard(由 Immunix 所使用)、ProPolice(由 OpenBSD 所使用)和 Microsoft 的 /GS 選項。

非執(zhí)行的堆棧防御。這包括 Solar Designer 的 non-exec 補?。ㄓ?OpenWall 所使用)和 exec shield(由 Red Hat/Fedora 所使用)。

其他方法。這包括 libsafe(由 Mandrake 所使用)和堆棧分割方法。

遺憾的是,迄今所見的所有方法都具有弱點,因此它們不是萬能藥,但是它們會提供一些幫助。

▲基于探測方法的防御

研究人員 Crispen Cowan 創(chuàng)建了一個稱為 StackGuard 的有趣方法。Stackguard 修改 C 編譯器(gcc),以便將一個“探測”值插入到返回地址的前面。“探測儀”就像煤礦中的探測儀:它在某個地方出故障時發(fā)出警告。在任何函數返回之前,它執(zhí)行檢查以確保探測值沒有改變。如果攻擊者改寫返回地址(作為 stack-smashing 攻擊的一部分),探測儀的值或許就會改變,系統(tǒng)內就會相應地中止。這是一種有用的方法,不過要注意這種方法無法防止緩沖區(qū)溢出改寫其他值(攻擊者仍然能夠利用這些值來攻擊系統(tǒng))。人們也曾擴展這種方法來保護其他值(比如堆上的值)。Stackguard(以及其他防御措施)由 Immunix 所使用。

IBM 的 stack-smashing 保護程序(ssp,起初名為 ProPolice)是 StackGuard 的方法的一種變化形式。像 StackGuard 一樣,ssp 使用一個修改過的編譯器在函數調用中插入一個探測儀以檢測堆棧溢出。然而,它給這種基本的思路添加了一些有趣的變化。 它對存儲局部變量的位置進行重新排序,并復制函數參數中的指針,以便它們也在任何數組之前。這樣增強了ssp 的保護能力;它意味著緩沖區(qū)溢出不會修改指針值(否則能夠控制指針的攻擊者就能使用指針來控制程序保存數據的位置)。默認情況下,它不會檢測所有函數,而只是檢測確實需要保護的函數(主要是使用字符數組的函數)。從理論上講,這樣會稍微削弱保護能力,但是這種默認行為改進了性能,同時仍然能夠防止大多數問題??紤]到實用的因素,它們以獨立于體系結構的方式使用 gcc 來實現它們的方法,從而使其更易于運用。從 2003 年 5 月的發(fā)布版本開始,廣受贊譽的 OpenBSD(它重點關注安全性)在他們的整個發(fā)行套件中使用了 ssp(也稱為 ProPolice)。

Microsoft 基于 StackGuard 的成果,添加了一個編譯器標記(/GS)來實現其 C 編譯器中的探測儀。

▲非執(zhí)行的堆棧防御

另一種方法首先使得在堆棧上執(zhí)行代碼變得不可能。 遺憾的是,x86 處理器(最常見的處理器)的內存保護機制無法容易地支持這點;通常,如果一個內存頁是可讀的,它就是可執(zhí)行的。一個名叫 Solar Designer 的開發(fā)人員想出了一種內核和處理器機制的聰明組合,為 Linux 內核創(chuàng)建了一個“非執(zhí)行的堆棧補丁”;有了這個補丁,堆棧上的程序就不再能夠像通常的那樣在 x86 上運行。 事實證明在有些情況下,可執(zhí)行程序 需要在堆棧上;這包括信號處理和跳板代碼(trampoline)處理。trampoline 是有時由編譯器(比如 GNAT Ada 編譯器)生成的奇妙結構,用以支持像嵌套子例程之類的結構。Solar Designer 還解決了如何在防止攻擊的同時使這些特殊情況不受影響的問題。

Linux 中實現這個目的的最初補丁在 1998 年被 Linus Torvalds 拒絕,這是因為一個有趣的原因。即使不能將代碼放到堆棧上,攻擊者也可以利用緩沖區(qū)溢出來使程序“返回”某個現有的子例程(比如 C 庫中的某個子例程),從而進行攻擊。簡而言之,僅只是擁有非可執(zhí)行的堆棧是不足夠的。

一段時間之后,人們又想出了一種防止該問題的新思路:將所有可執(zhí)行代碼轉移到一個稱為“ASCII 保護(ASCII armor)”區(qū)域的內存區(qū)。要理解這是如何工作的,就必須知道攻擊者通常不能使用一般的緩沖區(qū)溢出攻擊來插入 ASCII NUL 字符(0)這個事實。 這意味著攻擊者會發(fā)現,要使一個程序返回包含 0 的地址是很困難的。由于這個事實,將所有可執(zhí)行代碼轉移到包含 0 的地址就會使得攻擊該程序困難多了。

具有這個屬性的最大連續(xù)內存范圍是從 0 到 0x01010100 的一組內存地址,因此它們就被命名為 ASCII 保護區(qū)域(還有具有此屬性的其他地址,但它們是分散的)。與非可執(zhí)行的堆棧相結合,這種方法就相當有價值了:非可執(zhí)行的堆棧阻止攻擊者發(fā)送可執(zhí)行代碼,而 ASCII 保護內存使得攻擊者難于通過利用現有代碼來繞過非可執(zhí)行堆棧。這樣將保護程序代碼避免堆棧、緩沖區(qū)和函數指針溢出,而且全都不需重新編譯。

然而,ASCII 保護內存并不適用于所有程序;大程序也許無法裝入 ASCII 保護內存區(qū)域(因此這種保護是不完美的),而且有時攻擊者 能夠將 0 插入目的地址。 此外,有些實現不支持跳板代碼,因此可能必須對需要這種保護的程序禁用該特性。Red Hat 的 Ingo Molnar 在他的“exec-shield”補丁中實現了這種思想,該補丁由 Fedora 核心(可從 Red Hat 獲得它的免費版本)所使用。最新版本的 OpenWall GNU/Linux (OWL)使用了 Solar Designer 提供的這種方法的實現(請參閱 參考資料 以獲得指向這些版本的鏈接)。

▲其他方法

還有其他許多方法。一種方法就是使標準庫對攻擊更具抵抗力。Lucent Technologies 開發(fā)了 Libsafe,這是多個標準 C 庫函數的包裝,也就是像 strcpy() 這樣已知的對 stack-smashing 攻擊很脆弱的函數。Libsafe 是在 LGPL 下授予許可證的開放源代碼軟件。那些函數的 libsafe 版本執(zhí)行相關的檢查,確保數組改寫不會超出堆棧楨。然而,這種方法僅保護那些特定的函數,而不是從總體上防止堆棧溢出缺陷,并且它僅保護堆棧,而不保護堆棧中的局部變量。它們的最初實現使用了 LD_PRELOAD ,而這可能與其他程序產生沖突。Linux 的 Mandrake 發(fā)行套件(從 7.1 版開始)包括了 libsafe。

另一種方法稱為“分割控制和數據堆?!报D― 基本的思路是將堆棧分割為兩個堆棧,一個用于存儲控制信息(比如“返回”地址),另一個用于控制其他所有數據。Xu et al. 在 gcc 中實現了這種方法,StackShield 在匯編程序中實現了這種方法。這樣使得操縱返回地址困難多了,但它不會阻止改變調用函數的數據的緩沖區(qū)溢出攻擊。

事實上還有其他方法,包括隨機化可執(zhí)行程序的位置;Crispen 的“PointGuard”將這種探測儀思想引申到了堆中,等等。如何保護當今的計算機現在已成了一項活躍的研究任務。

一般保護是不足夠的

如此多不同的方法意味著什么呢?對用戶來說,好的一面在于大量創(chuàng)新的方法正在試驗之中;長期看來,這種“競爭”會更容易看出哪種方法最好。而且,這種多樣性還使得攻擊者躲避所有這些方法更加困難。然而,這種多樣性也意味著開發(fā)人員需要 避免編寫會干擾其中任何一種方法的代碼。這在實踐上是很容易的;只要不編寫對堆棧楨執(zhí)行低級操作或對堆棧的布局作假設的代碼就行了。即使不存在這些方法,這也是一個很好的建議。

操作系統(tǒng)供應商需要參與進來就相當明顯了:至少挑選一種方法,并使用它。緩沖區(qū)溢出是第一號的問題,這些方法中最好的方法通常能夠減輕發(fā)行套件中幾乎半數已知缺陷的影響??梢宰C明,不管是基于探測儀的方法更好,還是基于非可執(zhí)行堆棧的方法更好,它們都具有各自的優(yōu)點。可以將它們結合起來使用,但是少數方法不支持這樣使用,因為附加的性能損失使得這樣做不值得。我并沒有其他意思,至少就這些方法本身而言是這樣;libsafe 和分割控制及數據堆棧的方法在它們所提供的保護方面都具有局限性。當然,最糟糕的解決辦法就是根本不對這個第一號的缺陷提供保護。還沒有實現一種方法的軟件供應商需要立即計劃這樣做。從 2004 年開始,用戶應該開始避免使用這樣的操作系統(tǒng),即它們至少沒有對緩沖區(qū)溢出提供某種自動保護機制。

然而,沒有哪種方法允許開發(fā)人員忽略緩沖區(qū)溢出。所有這些方法都能夠被攻擊者破壞。 攻擊者也許能夠通過改變函數中其他數據的值來利用緩沖區(qū)溢出;沒有哪種方法能夠防止這點。如果能夠插入某些難于創(chuàng)建的值(比如 NUL 字符),那么這其中的許多方法都能被攻擊者繞開;隨著多媒體和壓縮數據變得更加普遍,攻擊者繞開這些方法就更容易了。從根本上講,所有這些方法都能減輕從程序接管攻擊到拒絕服務攻擊的緩沖區(qū)溢出攻擊所帶來的破壞。遺憾的是,隨著計算機系統(tǒng)在更多關鍵場合的使用,即使拒絕服務通常也是不可接受的。因而,盡管發(fā)行套件應該至少包括一種適當的防御方法,并且開發(fā)人員應該使用(而不是反對)那些方法,但是開發(fā)人員仍然需要最初就編寫無缺陷的軟件。

解決方法

下面討論內存溢出問題的解決和預防措施。

受控代碼

2002 年 2 月和 3 月,微軟公司展開了 Microsoft Windows Security Push 活動。在此期間,我所在的小組一共培訓了超過 8500 人,教授他們如何在設計、測試和文檔編制過程中解決安全問題。在我們向所有程序設計人員提出的建議中,有一條就是:緊跟微軟公司軟件開發(fā)策略的步伐,將某些應用程序和工具軟件由原先基于本地 Win32 的 C++ 代碼改造成基于 .NET 的受控代碼。我們的理由很多,但其中最根本的一條,就是為了解決內存溢出問題?;谑芸卮a的軟件發(fā)生內存溢出問題的機率要小得多,因為受控代碼無法直接存取系統(tǒng)指針、寄存器或者內存。作為開發(fā)人員,你應該考慮(至少是打算)用受控代碼改寫某些應用程序或工具軟件。例如:企業(yè)管理工具就是很好的改寫對象之一。當然,你也應該很清楚,不可能在一夜之間把所有用 C++ 開發(fā)的軟件用 C# 之類的受控代碼語言改寫。

黃金規(guī)則

當你用 C/C++ 書寫代碼時,應該處處留意如何處理來自用戶的數據。如果一個函數的數據來源不可靠,又用到內存緩沖區(qū),那么它就必須嚴格遵守下列規(guī)則:

必須知道內存緩沖區(qū)的總長度。

檢驗內存緩沖區(qū)。

提高警惕。

讓我們來具體看看這些“黃金規(guī)則”:

(1)必須知道內存緩沖區(qū)的總長度。

類似這樣的代碼就有可能導致 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 兩個參數以謊報數據長度和內存緩沖區(qū)長度。因此,你還得檢檢這兩個參數!

(2)檢驗內存緩沖區(qū)

如何知道由參數傳來的內存緩沖區(qū)長度是否真實呢?你會完全信任來自用戶的數據嗎?通常,答案是否定的。其實,有一種簡單的辦法可以檢驗內存緩沖區(qū)是否溢出。請看如下代碼片斷:

void Function(char *szName, DWORD cbName) {

char szBuff[MAX_NAME];

// 檢測內存

szBuff[cbName] = 0x42;

// 復制并使用 szName

if (cbName < max_name)="">

strcpy(szBuff,szName);

}

這段代碼試圖向欲檢測的內存緩沖區(qū)末尾寫入數據 0x42 。你也許會想:真是多此一舉,何不直接復制內存緩沖區(qū)呢?事實上,當內存緩沖區(qū)已經溢出時,一旦再向其中寫入常量值,就會導致程序代碼出錯并中止運行。這樣在開發(fā)早期就能及時發(fā)現代碼中的 bug 。試想,與其讓攻擊者得手,不如及時中止程序;你大概也不愿看到攻擊者隨心所欲地向內存緩沖區(qū)復制數據吧。

(3)提高警惕

雖然檢驗內存緩沖區(qū)能夠有效地減小內存溢出問題的危害,卻不能從根本上避免內存溢出攻擊。只有從源代碼開始提高警惕,才能真正免除內存溢出攻擊的危脅。不錯,上一段代碼已經很對用戶數據相當警惕了。它能確保復制到內存緩沖區(qū)的數據總長度不會超過 szBuff 的長度。然而,某些函數在使用或復制不可靠的數據時也可能潛伏著內存溢出漏洞。為了檢查你的代碼是否存在內存溢出漏洞,你必須盡量追蹤傳入數據的流向,向代碼中的每一個假設提出質疑。一旦這么做了,你將會意識到其中某些假設是錯誤的;然后你還會驚訝地叫道:好多 bug 呀!

在調用 strcpy、strcat、gets 等經典函數時當然要保持警惕;可對于那些所謂的第 n 版 (n-versions) strcpy 或 strcat 函數 —— 比如 strncpy 或 strncat (其中 n = 1,2,3 ……) —— 也不可輕信。的確,這些改良版本的函數是安全一些、可靠一些,因為它們限制了進入內存緩沖區(qū)的數據長度;然而,它們也可能導致內存溢出問題!請看下列代碼,你能指出其中的錯誤嗎?

#define SIZE(b) (sizeof(b))

char buff;

strncpy(buff,szSomeData,SIZE(buff));

strncat(buff,szMoreData,SIZE(buff));

strncat(buff,szEvenMoreData,SIZE(buff));

給點提示:請注意這些字符串函數的最后一個參數。怎么,棄權?我說啊,如果你是執(zhí)意要放棄那些“不安全”的經典字符串函數,并且一律改用“相對安全”的第 n 版函數的話,恐怕你這下半輩子都要為了修復這些新函數帶來的新 bug 而疲于奔命了。呵呵,開個玩笑而已。為何這么說?首先,它們的最后一個參數并不代表內存緩沖區(qū)的總長度,它們只是其剩余空間的長度;不難看出,每執(zhí)行完一個 strn... 函數,內存緩沖區(qū) buff 的長度就減少一些。其次,傳遞給函數的內存緩沖區(qū)長度難免存在“大小差一”(off-by-one)的誤差。你認為在計算 buff 的長度時包括了字符串末尾的空字符嗎?當我向聽眾提出這個問題時,得到的肯定答復和否定答復通常是 50 比 50 ,對半開。也就是說,大約一半人認為計算了末尾的空字符,而另一半人認為忽略了該字符。最后,那些第 n 版函數所返回的字符串不見得以空字符結束,所以在使用前務必要仔細閱讀它們的說明文檔。

編譯選項

“/GS”是Visual C++.NET 新引入的一個編譯選項。它指示編譯器在某些函數的堆棧幀(stack-frames) 中插入特定數據,以幫助消除針對堆棧的內存溢出問題隱患。切記,使用該選項并不能替你清除代碼中的內存溢出漏洞,也不可能消滅任何 bug 。它只是亡羊補牢,讓某些內存溢出問題隱患無法演變成真正的內存溢出問題;也就是說,它能防止攻擊者在發(fā)生內存溢出時向進程中插入和運行惡意代碼。無論如何,這也算是小小的安全保障吧。請注意,在新版的本地 Win32 C++ 中使用 Win32應用程序向導創(chuàng)建新項目時,默認設置已經打開了此選項。同樣,Windows .NET Server 環(huán)境也默認打開了此選項。關于 /GS 選項的更多信息,請參考 Brandon Bray 的《Compiler Security Checks In Depth》一書。

所謂定點數溢出是指定點數的運算結果的絕對值大于計算機能表示的最大數的絕對值。浮點數的溢出又可分為“上溢出”和“下溢出”兩種,“上溢出”與整數、定點數的含義相同,“下溢出”是指浮點數的運算結果的絕對值小于機器所能表示的最小數絕對值,此時將該運算結果處理成機器零。若發(fā)現溢出(上溢出),運算器將產生溢出標志或發(fā)出中斷請求,當溢出中斷未被屏蔽時,溢出中斷信號的出現可中止程序的執(zhí)行而轉入溢出中斷處理程序。

例如:有兩個數0.1001111和0.1101011相加,其結果應為1.0111010。由于定點數計算機只能表示小于1的數,如果字長只有8位,那么小數點前面的1,會因沒有觸發(fā)器存放而丟失。這樣,上述兩個數在計算機中相加,其結果變?yōu)?.0111010。若字長只有8位,則結果顯示為0.0000000,后面的1個0和6個1全部丟失,顯然這個結果有誤差。計算機的任何運算都不允許溢出,除非專門利用溢出做判斷,而不使用所得的結果。所以,當發(fā)生和不允許出現的溢出時,就要停機或轉入檢查程序,以找出產生溢出的原因,做出相應的處理。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多