1. 程式人生 > >C 語言緩衝區溢位的實戰

C 語言緩衝區溢位的實戰

開發十年,就只剩下這套架構體系了! >>>   

該內容原始碼參考了 <深入理解計算機系統>[3.10.3]

學習該章節之前,一直有個疑問,Segmentation fault 這樣的錯誤會具體引發的原因是啥, 可不可以從彙編程式碼的層面進行一個學習和了解. 所有就有了這篇文章, 純屬個人學習研究.

完整的測試原始碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*


*/
char* gets(char* s)
{
        int c;
        char *dest  = s;


        // read c char write to dest memory;
        while((c = getchar()) != '\n' && c != EOF)
        {
                *dest++= c;
        }

        if(c == EOF && dest == s)
        {
                return NULL;
        }

        *dest++ ='\0';
        return s;
}


void echo(){
        char buf[8];
        gets(buf);
        puts(buf);
}

int main()
{

        echo();

        return 0;
}

編譯過程,
shell>gcc -g  demo.c -o demo


程式編譯成功後,隨便輸入幾個字元, 可以正常的回顯, 但是超過某個長度就會出現文章開始的問題, 那麼新的問題來了, 
這個長度是不是就是程式碼中定義的buf的長度呢,  會不會有其他問題發生, 

驗證思路:
1 通過彙編程式碼, 檢視暫存器地址中的內容, 直接明瞭的去觀察, 那麼就存在一個對比的過程, 
  第一遍的時候是正常的輸入, 不會發生錯誤, 
  第二遍會輸入較長的內容,  對比暫存器中的值.

正常輸入時,
在沒有輸入任何字元的時候, 相關的暫存器的值,
這個是在程式呼叫getchar() 函式前暫存器的值.

(gdb) i	r ebp
ebp    	       0xbfffea68	0xbfffea68
(gdb) x/16cb 0xbfffea68
0xbfffea68:	120 'x'	-22 '\352'	-1 '\377'	-65 '\277'	100 'd'	-124 '\204'	4 '\004'
8 '\b'
0xbfffea70:	0 '\000'        -106 '\226'	117 'u'	0 '\000'        -112 '\220'	-22 '\352'	-1 '\377
'	-65 '\277'


(gdb) i r esp
esp    	       0xbfffea50	0xbfffea50
(gdb) x/16cb 0xbfffea50
0xbfffea50:	-12 '\364'	-1 '\377'	-118 '\212'	0 '\000'        4 '\004'        -30 '\342'
-118 '\212'	0 '\000'
0xbfffea58:	-120 '\210'	-22 '\352'	-1 '\377'	-65 '\277'	-87 '\251'	-124 '\204'
4 '\004'        8 '\b'

(gdb) i r eax
eax    	       0xbfffea60	0xbfffea60
(gdb) x/16cb 0xbfffea60
0xbfffea60:	37 '%'	-98 '\236'	121 'y'	0 '\000'        28 '\034'	-21 '\353'	-1 '\377'
-65 '\277'
0xbfffea68:	120 'x'	-22 '\352'	-1 '\377'	-65 '\277'	100 'd'	-124 '\204'	4 '\004'
8 '\b'


開始呼叫getchar() 函式
此時有個隱藏的資訊是, gets() 函式中, 引數s的地址是 0xbfffea60, 這個需要知道, 就是這個引數出現了問題,
gdb> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

在輸入完成後, 
再次檢視暫存器的內容,

(gdb) x/16cb 0xbfffea68
0xbfffea68:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'
0xbfffea70:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'


(gdb) x/32cb 0xbfffea50
0xbfffea50:	96 '`'	-22 '\352'	-1 '\377'	-65 '\277'	4 '\004'        -30 '\342'	-118 '\2
12'	0 '\000'
0xbfffea58:	-120 '\210'	-22 '\352'	-1 '\377'	-65 '\277'	-87 '\251'	-124 '\204'
4 '\004'        8 '\b'
0xbfffea60:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'
0xbfffea68:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'



(gdb) x/16cb 0xbfffea60
0xbfffea60:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'
0xbfffea68:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'

對比發現 0xbfffea68 地址中的內容被改寫了, 這個地址是程式被呼叫時的入棧地址, 在gets()函式內部把值改寫了, 
導致程式返回的時候,出現了問題,


如果使用者輸入了緩衝區大小內的內容後, 則器暫存器中的值為
(gdb) x/16cb 0xbfffea60
0xbfffea60:	97 'a'	97 'a'	97 'a'	97 'a'	97 'a'	0 '\000'        -1 '\377'	-65 '\277'
0xbfffea68:	120 'x'	-22 '\352'	-1 '\377'	-65 '\277'	100 'd'	-124 '\204'	4 '\004'
8 '\b'
可以發現 0xbfffea68地址中的值和原來的值是一致的, 此時程式退出則是正常的,

具體可以使用的空間的大小的問題, 什麼時候,會觸發返回地址被改寫.