溢出與下溢圖解指南:Coursebook 中 4 個(gè)經(jīng)典內(nèi)存錯(cuò)誤剖析與預(yù)防方法)
緩沖區(qū)溢出與下溢圖解指南Coursebook 中 4 個(gè)經(jīng)典內(nèi)存錯(cuò)誤剖析與預(yù)防方法【免費(fèi)下載鏈接】coursebookOpen Source Introductory Systems Programming Textbook for the University of Illinois項(xiàng)目地址: https://gitcode.com/GitHub_Trending/co/coursebook緩沖區(qū)溢出與下溢Buffer Overflow / Underflow是系統(tǒng)編程中最經(jīng)典的內(nèi)存錯(cuò)誤也是 C 語(yǔ)言新手最容易踩中的坑。本文基于Coursebook—— 伊利諾伊大學(xué)開(kāi)源的入門(mén)級(jí)系統(tǒng)編程教材用書(shū)中 4 個(gè)真實(shí)場(chǎng)景和隨書(shū)插圖帶你 10 分鐘看懂緩沖區(qū)溢出與下溢是如何發(fā)生的、為什么能從小 Bug升級(jí)為安全事故以及 4 條可直接上手的預(yù)防檢查清單零基礎(chǔ)也能看懂。 緩沖區(qū)溢出與下溢是什么一張內(nèi)存圖講清楚在 C 語(yǔ)言里緩沖區(qū)Buffer就是一段連續(xù)的字節(jié)內(nèi)存數(shù)組是緩沖區(qū)字符串是緩沖區(qū)malloc出來(lái)的堆空間也是緩沖區(qū)。關(guān)鍵事實(shí)只有一條C 語(yǔ)言不會(huì)檢查你的指針是否越界。寫(xiě)多一個(gè)字節(jié)不會(huì)報(bào)錯(cuò)、不會(huì)警告而是悄悄覆蓋掉鄰居的內(nèi)存——這就是緩沖區(qū)溢出寫(xiě)過(guò)頭而緩沖區(qū)下溢則是從緩沖區(qū)的前面、甚至更低地址處開(kāi)始讀寫(xiě)同樣會(huì)踩壞別人。下面這張圖來(lái)自 Coursebook 的內(nèi)存模型章節(jié)它把一塊緩沖區(qū)拆成一個(gè)個(gè)字節(jié)格子寫(xiě)字符串person后前 4 格存長(zhǎng)度0,0,0,6后面 7 格依次是p e r s o n \0——你能直觀看到字節(jié)就是字節(jié)C 編譯器只負(fù)責(zé)預(yù)留空間不幫你畫(huà)邊界線。正是這種無(wú)邊界保護(hù)的設(shè)計(jì)讓緩沖區(qū)溢出與下溢成為 C 程序最危險(xiǎn)的內(nèi)存錯(cuò)誤類(lèi)別教材專(zhuān)門(mén)在 introc/common_bugs.tex 一節(jié)列出經(jīng)典案例。 Coursebook 書(shū)中的 4 個(gè)經(jīng)典內(nèi)存錯(cuò)誤場(chǎng)景場(chǎng)景 1循環(huán)多寫(xiě)一格 —— 最經(jīng)典的緩沖區(qū)溢出教材給出的最小案例循環(huán)從i N寫(xiě)到i 0當(dāng)i N時(shí)寫(xiě)入array[N]——越界一格#define N (10) int array[N]; for (int i N; i 0; i--) array[i] i; /* 寫(xiě)到了 array[10]越界 */這一格內(nèi)存大概率正被別的東西使用于是內(nèi)存損壞悄然發(fā)生。更隱蔽的是溢出可能發(fā)生在庫(kù)函數(shù)內(nèi)部你的代碼本身看起來(lái)完全正確。場(chǎng)景 2字符串少分配一個(gè)字節(jié) —— strlen 下溢陷阱C 字符串必須在末尾有一個(gè) NUL 字節(jié)\0。存Hi需要 3 個(gè)字節(jié)而不是 2 個(gè)。教材用一個(gè)strdup示例連擺三種寫(xiě)法malloc(sizeof(char*))錯(cuò)只分配了指針大小、malloc(strlen(input))差一點(diǎn)漏了 NUL、正確寫(xiě)法是malloc(strlen(input) 1)。記住口訣字符串 strlen(s) 1 個(gè)字節(jié)。差這一個(gè)字節(jié)strcpy會(huì)一直越過(guò)你分配的邊界去找 NUL這就是典型的緩沖區(qū)下溢式越界。場(chǎng)景 3踩壞鄰居變量 —— aoo 驚嚇實(shí)驗(yàn)安全章節(jié) security/security.tex 有一個(gè)著名的小實(shí)驗(yàn)棧上并排放著兩個(gè)緩沖區(qū)out[10]和in[10]程序從輸入讀字符串到in再打印out。輸入短字符串時(shí)一切正常一旦輸入超過(guò) 10 個(gè)字符溢出的字節(jié)順著內(nèi)存覆蓋到out上打印結(jié)果變成了詭異的aoo。如果并排的不是無(wú)關(guān)變量而是密碼哈希呢教材緊接著給出反例pass_hash緊鄰一個(gè)不檢查長(zhǎng)度的輸入緩沖區(qū)——一次緩沖區(qū)溢出就能污染敏感數(shù)據(jù)。場(chǎng)景 4不限長(zhǎng)度的輸入 —— gets() 這顆地雷教材把gets(array)稱(chēng)為老朋友它根本不接收緩沖區(qū)大小參數(shù)輸入多長(zhǎng)就寫(xiě)多長(zhǎng)。希望輸入比我的數(shù)組短是編程界最昂貴的希望?,F(xiàn)代開(kāi)發(fā)中應(yīng)使用帶長(zhǎng)度限制的fgets(array, sizeof(array), stdin)。? 為什么緩沖區(qū)溢出會(huì)成為安全事故緩沖區(qū)溢出與下溢不只是程序崩潰。教材的事后復(fù)盤(pán)章節(jié)記錄了史上最著名的案例HeartbleedSSL 心跳包允許請(qǐng)求方謊報(bào)長(zhǎng)度——只發(fā) 3 個(gè)字節(jié)卻要求服務(wù)器回傳 500 字節(jié)于是服務(wù)器把緩沖區(qū)后面不屬于你的內(nèi)存也讀了出來(lái)密碼、密鑰隨之泄露。根因就一句話(huà)沒(méi)有對(duì)緩沖區(qū)做邊界檢查。更狠的情況是溢出發(fā)生在棧上。每個(gè)線程都有自己的棧區(qū)棧幀彼此緊鄰返回地址、棧保護(hù)值就放在緩沖區(qū)旁邊現(xiàn)代操作系統(tǒng)已布下多道防線見(jiàn) security/security.tex棧保護(hù)Stack Canary編譯器在棧上埋一個(gè)哨兵值函數(shù)返回前校驗(yàn)它是否被改一旦棧被搗毀立即中止程序——這就是為什么教材里的溢出實(shí)驗(yàn)要加-fno-stack-protector才能復(fù)現(xiàn)ASLR每次運(yùn)行隨機(jī)化棧、堆、庫(kù)的地址讓攻擊者猜不到目標(biāo)位置DEP數(shù)據(jù)執(zhí)行保護(hù)內(nèi)存頁(yè)要么可寫(xiě)、要么可執(zhí)行不能兩者兼得阻止把惡意代碼寫(xiě)進(jìn)緩沖區(qū)再執(zhí)行。? 4 步預(yù)防檢查寫(xiě)代碼前過(guò)一遍算準(zhǔn)字節(jié)數(shù)分配內(nèi)存時(shí)想清楚我要存多少個(gè)字節(jié)包括結(jié)尾的 NUL。堆內(nèi)存是一塊塊連續(xù)的塊少分配哪怕一個(gè)字節(jié)寫(xiě)入時(shí)就會(huì)撕裂到相鄰塊里——這塊空閑塊切分圖出自 malloc/malloc.tex提醒我們堆上每一塊都有明確的尺寸邊界。每次訪問(wèn)都查邊界任何索引都要先確認(rèn)0 i N。教材同步章節(jié)的環(huán)形緩沖區(qū)就是一個(gè)優(yōu)秀示范讀寫(xiě)指針在 16 個(gè)槽位內(nèi)順時(shí)針移動(dòng)、取?;乩@buffer[in]、buffer[out]永遠(yuǎn)不會(huì)寫(xiě)出圈外——拒絕無(wú)長(zhǎng)度限制的輸入函數(shù)gets一律換成fgets、scanf用%10s這類(lèi)帶寬度的格式串。編譯時(shí)開(kāi)保護(hù)別隨手關(guān)默認(rèn)啟用棧保護(hù)、ASLR調(diào)試復(fù)現(xiàn)時(shí)可以臨時(shí)關(guān)閉但生產(chǎn)環(huán)境永遠(yuǎn)不要關(guān)。 延伸閱讀Coursebook 相關(guān)章節(jié)內(nèi)容文件路徑緩沖區(qū)溢出/下溢、字符串少一字節(jié)等常見(jiàn) Bugintroc/common_bugs.tex字節(jié)級(jí)內(nèi)存模型與 strcpy 圖解introc/c_memory_model.tex緩沖區(qū)溢出實(shí)驗(yàn)、棧保護(hù)、ASLR 與 DEPsecurity/security.texHeartbleed 等真實(shí)事故復(fù)盤(pán)post_mortems/post_mortems.tex堆內(nèi)存分配與塊管理malloc/malloc.tex線程棧布局threads/threads.tex一句話(huà)總結(jié)C 語(yǔ)言給了你自由也收回了護(hù)欄——只要把算準(zhǔn)字節(jié)數(shù)、檢查邊界、限制輸入這三件事變成肌肉記憶緩沖區(qū)溢出與下溢這兩個(gè)經(jīng)典內(nèi)存錯(cuò)誤就再也難不住你?!久赓M(fèi)下載鏈接】coursebookOpen Source Introductory Systems Programming Textbook for the University of Illinois項(xiàng)目地址: https://gitcode.com/GitHub_Trending/co/coursebook創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考