c- 陣列越界問題
為了加深大家對陣列越界的瞭解,下面通過一段完整的陣列越界示例來演示程式設計中陣列越界將會導致哪些問題。
- #define PASSWORD "123456"
- int Test(char *str)
- {
- int flag;
- char buffer[7];
- flag=strcmp(str,PASSWORD);
- strcpy(buffer,str);
- return flag;
- }
- int main(void)
- {
- int flag=0;
- char str[1024];
- while(1)
- {
- printf("請輸入密碼: ");
- scanf("%s",str);
- flag = Test(str);
- if(flag)
- {
- printf("密碼錯誤!\n");
- }
- else
- {
- printf("密碼正確!\n");
- }
- }
- return 0;
- }
上面的示例程式碼模擬了一個密碼驗證的例子,它將使用者輸入的密碼與巨集定義中的密碼“123456”進行比較。很顯然,本示例中最大的設計漏洞就在於 Test() 函式中的 strcpy(buffer,str) 呼叫。
由於程式將使用者輸入的字串原封不動地複製到 Test() 函式的陣列 char buffer[7] 中。因此,當用戶的輸入大於 7 個字元的緩衝區尺寸時,就會發生陣列越界錯誤,這也就是大家所謂的緩衝區溢位(Buffer overflow)漏洞。但是要注意,如果這個時候我們根據緩衝區溢位發生的具體情況填充緩衝區,不但可以避免程式崩潰,還會影響到程式的執行流程,甚至會讓程式去執行緩衝區裡的程式碼。示例執行結果為:
請輸入密碼:12345
密碼錯誤!
請輸入密碼:123456
密碼正確!
請輸入密碼:1234567
密碼正確!
請輸入密碼:aaaaaaa
密碼正確!
請輸入密碼:0123456
密碼錯誤!
請輸入密碼:
在示例程式碼中,flag 變數實際上是一個標誌變數,其值將決定著程式是進入“密碼錯誤”的流程(非 0)還是“密碼正確”的流程(0)。當我們輸入錯誤的字串“1234567”或者“aaaaaaa”,程式也都會輸出“密碼正確”。但在輸入“0123456”的時候,程式卻輸出“密碼錯誤”,這究竟是為什麼呢?
其實,原因很簡單。當呼叫 Test() 函式時,系統將會給它分配一片連續的記憶體空間,而變數 char buffer[7] 與 int flag 將會緊挨著進行儲存,使用者輸入的字串將會被複制進 buffer[7] 中。如果這個時候,我們輸入的字串數量超過 6 個(注意,有字串截斷符也算一個),那麼超出的部分將破壞掉與它緊鄰著的 flag 變數的內容。
當輸入的密碼不是巨集定義的“123456”時,字串比較將返回 1 或 -1。我們都知道,記憶體中的資料按照 4 位元組(DWORD)逆序儲存,所以當 flag 為 1 時,在記憶體中儲存的是 0x01000000
而對於“0123456”,因為在進行字串的大小比較時,它小於“123456”,flag的值是 -1,在記憶體中將按照補碼存放負數,所以實際儲存的不是 0x01000000 而是 0xffffffff。那麼字串截斷後符 0x00 淹沒後,變成 0x00ffffff,還是非 0,所以沒有進入正確分支。
其實,本示例只是用一個位元組淹沒了鄰接變數,導致程式進入密碼正確的處理流程,使設計的驗證功能失效。