C語言中的一個大惡魔之溢出問題,“野獸出沒”,小心小心再小心

整型溢出有點老生常談了,但似乎沒有引起多少人的重視。

C/C++學習資料。C/C++深度學習 :QQ群:747821062


整型溢出會有可能導致緩沖區溢出,緩沖區溢出會導致各種黑客攻擊,比如最近OpenSSL的heartbleed事件,就是一個buffer overread的事件。在這里寫下這篇文章,希望大家都了解一下整型溢出,編譯器的行為,以及如何防范,以寫出更安全的代碼。

什么是整型溢出

C語言的整型問題相信大家并不陌生了。對于整型溢出,分為無符號整型溢出和有符號整型溢出。

對于unsigned整型溢出,C的規范是有定義的——“溢出后的數會以2^(8*sizeof(type))作模運算”,也就是說,如果一個unsigned char(1字符,8bits)溢出了,會把溢出的值與256求模。例如:

unsigned?char?x = 0xff;

printf("%d\n", ++x);

上面的代碼會輸出:0 (因為0xff + 1是256,與2^8求模后就是0)

對于signed整型的溢出,C的規范定義是“undefined behavior”,也就是說,編譯器愛怎么實現就怎么實現。對于大多數編譯器來說,算得啥就是啥。比如:

signed?char?x =0x7f; //注:0xff就是-1了,因為最高位是1也就是負數了

printf("%d\n", ++x);

上面的代碼會輸出:-128,因為0x7f + 0x01得到0x80,也就是二進制的1000 0000,符號位為1,負數,后面為全0,就是負的最小數,即-128。

另外,千萬別以為signed整型溢出就是負數,這個是不定的。比如:

signed?char?x = 0x7f;

signed?char?y = 0x05;

signed?char?r = x * y;

printf("%d\n", r);

上面的代碼會輸出:123

相信對于這些大家不會陌生了。

整型溢出的危害

下面說一下,整型溢出的危害。

示例一:整形溢出導致死循環

short?len = 0;

while(len< MAX_LEN) {

len += readFromInput(fd, buf);

buf += len;

}

C/C++學習資料。C/C++深度學習 :QQ群:747821062

上面這段代碼可能是很多程序員都喜歡寫的代碼(我在很多代碼里看到過多次),其中的MAX_LEN 可能會是個比較大的整型,比如32767,我們知道short是16bits,取值范圍是-32768 到 32767 之間。但是,上面的while循環代碼有可能會造成整型溢出,而len又是個有符號的整型,所以可能會成負數,導致不斷地死循環。

示例二:整形轉型時的溢出

int?copy_something(char?*buf,?int?len)

{

#define MAX_LEN 256

char?mybuf[MAX_LEN];

if(len > MAX_LEN){ // <---- [1]

return?-1;

}

return?memcpy(mybuf, buf, len);

}

上面這個例子中,還是[1]處的if語句,看上去沒有會問題,但是len是個signed int,而memcpy則需一個size_t的len,也就是一個unsigned 類型。于是,len會被提升為unsigned,此時,如果我們給len傳一個負數,會通過了if的檢查,但在memcpy里會被提升為一個正數,于是我們的mybuf就是overflow了。這個會導致mybuf緩沖區后面的數據被重寫。

示例三:分配內存

關于整數溢出導致堆溢出的很典型的例子是,OpenSSH Challenge-Response SKEY/BSD_AUTH 遠程緩沖區溢出漏洞。下面這段有問題的代碼摘自OpenSSH的代碼中的auth2-chall.c中的input_userauth_info_response() 函數:

nresp = packet_get_int();

if?(nresp > 0) {

response = xmalloc(nresp*sizeof(char*));

for?(i = 0; i < nresp; i++)

response[i] = packet_get_string(NULL);

}

上面這個代碼中,nresp是size_t類型(size_t一般就是unsigned int/long int),這個示例是一個解數據包的示例,一般來說,數據包中都會有一個len,然后后面是data。如果我們精心準備一個len,比如:1073741825(在32位系統上,指針占4個字節,unsigned int的最大值是0xffffffff,我們只要提供0xffffffff/4 的值——0x40000000,這里我們設置了0x4000000 + 1), nresp就會讀到這個值,然后nresp*sizeof(char*)就成了 1073741825 * 4,于是溢出,結果成為了 0x100000004,然后求模,得到4。于是,malloc(4),于是后面的for循環1073741825 次,就可以干環事了(經過0x40000001的循環,用戶的數據早已覆蓋了xmalloc原先分配的4字節的空間以及后面的數據,包括程序代碼,函數指針,于是就可以改寫程序邏輯。關于更多的東西,你可以看一下這篇文章《Survey of Protections from Buffer-Overflow Attacks》)。

示例四:緩沖區溢出導致安全問題

C/C++學習資料。C/C++深度學習 :QQ群:747821062


int?func(char?*buf1, unsigned?int?len1,

char?*buf2, unsigned?int?len2 )

{

char?mybuf[256];

if((len1 + len2) > 256){ //<--- [1]

return?-1;

}

memcpy(mybuf, buf1, len1);

memcpy(mybuf + len1, buf2, len2);

do_some_stuff(mybuf);

return?0;

}

上面這個例子本來是想把buf1和buf2的內容copy到mybuf里,其中怕len1 + len2超過256 還做了判斷,但是,如果len1+len2溢出了,根據unsigned的特性,其會與2^32求模,所以,基本上來說,上面代碼中的[1]處有可能為假的。(注:通常來說,在這種情況下,如果你開啟-O代碼優化選項,那個if語句塊就全部被和諧掉了——被編譯器給刪除了)比如,你可以測試一下 len1=0x104, len2 = 0xfffffffc 的情況。

示例五:size_t 的溢出

for?(int?i=?strlen(s)-1; i>=0; i--) { ... }

for?(int?i=v.size()-1; i>=0; i--) { ... }

上面這兩個示例是我們經常用的從尾部遍歷一個數組的for循環。第一個是字符串,第二個是C++中的vector容器。strlen()和vector::size()返回的都是 size_t,size_t在32位系統下就是一個unsigned int。你想想,如果strlen(s)和v.size() 都是0呢?這個循環會成為個什么情況?于是strlen(s) – 1 和 v.size() – 1 都不會成為 -1,而是成為了 (unsigned int)(-1),一個正的最大數。導致你的程序越界訪問。

這樣的例子有很多很多,這些整型溢出的問題如果在關鍵的地方,尤其是在搭配有用戶輸入的地方,如果被黑客利用了,就會導致很嚴重的安全問題。

C語言中的一個大惡魔—— Undefined! 這里都是“野獸出沒”的地方,你一定要小心小心再小心

其它

C/C++學習資料。C/C++深度學習 :QQ群:747821062

對于C++來說,你應該使用STL中的numeric_limits::max() 來檢查溢出。

可見,寫一個安全的代碼并不容易,尤其對于C/C++來說。對于黑客來說,他們只需要搜一下開源軟件中代碼有memcpy/strcpy之類的地方,然后看一看其周邊的代碼,是否可以通過用戶的輸入來影響,如果有的話,你就慘了。

最后, 不好意思,這篇文章可能羅嗦了一些,大家見諒。C/C++深度學習 私信我 “代碼” 獲取資料。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容