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