《C和C 安全編碼》讀書筆記(一)

2021-10-16 18:56:13 字數 3870 閱讀 9086

1.1 衡量危險

1.2 安全概念

1.3 c&c++2.1 字串

2.1.1 字串資料型別

字串的一些術語:

2.1.2 utf-8

utf-8是乙個多位元組字符集,它可以表示在unicode字符集中的每個字元,而且與美國7位ascii字符集向後相容。每個utf-8字元由1~4個位元組表示。乙個具有前導0位的位元組是乙個單位元組碼,乙個具有多個前導1位的位元組是乙個多位元組序列的首位元組,而乙個具有前導「10」位模式的位元組是乙個多位元組序列的延續位元組。這種位元組格式允許檢測每個序列的開始,而無需從字串的開頭解碼。

2.1.3 寬字串

若要處理大字符集的字元,程式可以將每個字元都表示為乙個寬字元,寬字元一般比乙個普通字元需要更多的空間。乙個寬字串是乙個連續的寬字串行,它包括並由第乙個null寬字元終止。

2.1.4 字串字面值

const

char s[3]

="abc"

;

雖然字串字面值的大小是4,但陣列的大小是3,任何隨後將陣列作為乙個空位元組結尾的字串的使用都會導致漏洞,因為s沒有正確地以空字元結尾。乙個更好的方法是,對於乙個用字串字面值初始化的字串,不指定它的界限,因為編譯器會自動為整個字串字面值分配足夠的空間,包括終止的空字元:

const

char s=

"abc"

;

《c安全編碼標準》:str 36-c. 不要指定乙個用字串字面值初始化的字元陣列的界限。

2.1.5 c++中的字串

2.1.6 字元型別

char、signed char和unsigned char統稱為字元型別。編譯器可以自由地定義char,使它與signed char或unsigned char具有相同的範圍

表示方式和行為,不管編譯器作出的選擇是什麼,char都是獨特的型別。雖然沒有在任何地方指出,但c便準選擇字元型別遵從如下一致的理念:

2.1.7 計算字串的大小

為防止緩衝區溢位和其他一些執行時錯誤,正確地計算字串大小是必不可少的。使用不正確的字串大小會導致緩衝溢位,例如,會分配乙個大小不充足的緩衝區。

《c安全編碼標準》:str31-c. 保證字串的儲存空間具有容納字元資料和空終結符的足夠空間。

一些概念:

2.2 常見的字串錯誤操作

在c和c++中,操作字串很容易產生錯誤,最常見的錯誤由4中,分別是無界字串複製(unbounded string copy)、差一錯誤(off-by-one error)、空結尾錯誤(null termination error)以及字串截斷(string truncation)。

2.2.1 無界字串複製

無界字串複製發生於從源資料複製資料到乙個定長的字元陣列時(例如,從標準輸入讀取資料到乙個定長的緩衝區中)。

《c安全編碼標準》:msc34-c. 不要使用廢棄或過時的函式。

對於程式設計師而言,從無界資料來源(例如stdin)讀入資料是乙個有趣的問題。由於實現無法得知使用者將會輸入多少個字元,因此不可能預先分配乙個長度足夠的陣列。常見的解決方案是靜態分配乙個認為長度遠遠大於所需的陣列。

《c安全編碼標準》:str35-c. 不要從乙個無界源複製資料到定長陣列。

int

main

(int argc,

char

*ar**)

當分配空間不足以複製乙個程式的輸入(比如乙個命令列引數)時,就會產生漏洞。雖然按照慣例,ar**[0]包含程式名,但攻擊者可以控制ar**[0]的內容,在如下程式中,提供乙個超過128個位元組的字串就會造成乙個漏洞。而且攻擊者還可以把argc[0]設定為null來呼叫這個程式。

int

main

(int argc,

char

*ar**)

這個程式可以在microsoft visual c++2012下編譯並執行,但在警告級別/w3下會對使用strcy()發出警告。這個程式也能在g++4.7.2下編譯並執行,如果定義了_fortify_source,那麼在執行時,如果對strcpy()的呼叫導致了緩衝區溢位,由於對物件大小檢查的結果失敗,此程式會中止。

strlen()可以用於確定由ar**[0]到ar**[argc-1]引用的字串的長度,以便可動態分配足夠的記憶體。要記得加乙個位元組,以容納用於終止字串的空字元,必須注意避免假設ar**陣列中的任何元素(包括ar**[0])是非空的。

int

main

(int argc,

char

*ar**)

else

}

#include

intmain

(void

)

在微軟visual c++2012中,當警告級別是/w4時,這個程式可以正常編譯。在g++4.7.2中,當選項是-wall-wextra-pedantic時,它也可以正確編譯。通過設定域寬成員可以消除溢位:

#include

intmain

(void

)

2.2.2 差一錯誤

空字串結尾的字串的另乙個常見問題是差一錯誤,差一錯誤與無界字串複製有相似之處,即都涉及對陣列的越界寫問題。下面程式存在很多錯誤。

exp1:

#include

#include

#include

intmain()

dest[i]

='\0'

;printf

("dest=%s"

, dest)

;}

2.2.3 空字元結尾錯誤

乙個字串正確地以空字元結尾,是指在陣列最後乙個元素或在它之前存在乙個空終結符。如果乙個字串沒有以空字元結果,程式可能會被欺騙,導致在陣列邊界之外讀取或寫入資料。

**《c安全編碼標準》:str 32-c. 按要求提供空位元組結尾的字串。**要注意,該規則並不排除使用字元陣列。

空字串結尾錯誤也很難檢測,它們會潛伏在部署好的**中,直至遇到一組特別的輸入而導致發生錯誤。編寫**不能依賴於編譯器如何分配記憶體,因為這在編譯器的下個版本中很可能發生改變。

2.2.4 字串截斷

當目標字元陣列的長度不足以容納字串的內容時,就會發生字串截斷。截斷通常發生於讀取使用者輸入或字串複製時,通常是程式設計師試圖防止緩衝區溢位的結果。儘管沒有緩衝區溢位危害那麼大,但字串截斷會丟失資料,有時也會導致軟體漏洞。

2.2.5 與函式無關的字串錯誤

2.3 字串漏洞及其利用

之前描述的c或c++中操作字串常見的錯誤。當**對源於外部的不受信任的資料(如命令列引數、環境變數、控制台輸入、文字檔案和網路連線)進行操作時,這些錯誤就會變得很嚴重。

2.3.1 被汙染的資料

**exp1:**這個程式檢查使用者密碼(應考慮被汙染的資料),並授權或拒絕訪問。

bool ispasswordok()

intmain

(void

)}

ispasswordok()沒有檢查gets()的返回狀態,這違反了「fio04-c. 檢測和處理輸入和輸出錯誤。」當gets()失敗時,password緩衝區的內容是不確定的,所以後續的strcmp()呼叫的行為是未定義的。在實際的程式中,緩衝區甚至可能包含先前其他使用者輸入的密碼。

2.3.3 緩衝區溢位

c和c++都容易發生緩衝區溢位問題,因為它們具有以下共同之處:

C和C指標讀書筆記

1.c中變數的儲存型別 儲存型別分為靜態儲存 普通記憶體 堆疊,暫存器自動變數即 塊內部的變數儲存於堆疊,其他變數儲存於普通記憶體即靜態儲存,如果頻繁訪問乙個變數,宣告為暫存器型別後儲存於硬體暫存器中。2.c中實體的鏈結屬性 1 屬於internal鏈結屬性的識別符號在同乙個原始檔內的所有宣告中都指...

C 讀書筆記

1.內建函式 inline必須在呼叫前進行完整定義,語 define相同,但是消除了不安全性 例 inline float circle float r 2,函式原型 語法形式 返回型別 函式名 參數列 1 函式原型的參數列中不包含引數的名字,而只包含型別 2 函式定義由函式說明部分和函式體構成 3...

C 讀書筆記

c陷阱和缺陷 1.賦值符優先順序比邏輯比較低,賦值表示式的結果為賦值後的結果 2.編譯器掃瞄符號使用貪心法,盡量選擇最長字元的符號串,如a 理解為a 而不是a 3.為相容老版本編譯器,不同符號之間最好加空格,如a 1可能理解為a 1,應該改為a 1 4.老版本編譯器,8進製數以0開始,後面可以加超過...