一:什么是整數溢出
由於整數在內存里面保存在一個固定長度的空間內,它能存儲的最大值和最小值是固定的,如果我們嘗試去存儲一個數,而這個數又大於這個固定的最大值時,就會導致整數溢出。(x86-32 的數據模型是 ILP32,即整數(Int)、長整數(Long)和指針(Pointer)都是 32 位。)
二、溢出類型及表現
1.溢出
只有符號的數才會發生溢出
對於signed整型的溢出,C的規范定義是“undefined behavior”,也就是說,編譯器愛怎么實現就怎么實現。對於大多數編譯器來說,仍然是回繞。
2.回繞
無符號數會回繞(常繞過一些判斷語句)
對於unsigned整型溢出,C的規范是有定義的——“溢出后的數會以2^(8*sizeof(type))作模運算”,也就是說,如果一個unsigned char(1字符,8bits)溢出了,會把溢出的值與256求模。例如:
|
1
2
|
unsigned
char
x = 0xff;
printf
(
"%d\n"
, ++x);
|
上面的代碼會輸出:0 (因為0xff + 1是256,與2^8求模后就是0)
3.截斷
將一個較大寬度的數存入一個寬度小的操作數中,高位發生截斷
三、簡單了解整數溢出的危害
1. 整數回繞之后,會導致索引越界,取到不確定的數據
2. 或者判斷失效,形成死循環。
3. 回繞之后,導致分配超大內存。
四、Keil將變量加入Watch后,如果溢出顯示的值后會帶個“?”號
觀察到這種情況就要注意了。
五、例子
第一種情況——有符合號溢出舉個例子:
int i;
i = INT_MAX; // 2 147 483 647
i++; printf("i = %d\n", i); // i = -2 147 483 648
第二種情況——無符號回繞舉個列子:
unsigned int ui;
ui = UINT_MAX; // 在 x86-32 上為 4 294 967 295 ui++;
printf("ui = %u\n", ui); // ui = 0
ui = 0;
ui--; printf("ui = %u\n", ui); // 在 x86-32 上,ui = 4 294 967 295
第三種情況——高位截斷截斷舉倆例子:
加法截斷:
0xffffffff + 0x00000001
= 0x0000000100000000 (long long)
= 0x00000000 (long)
乘法截斷:
0x00123456 * 0x00654321
= 0x000007336BF94116 (long long)
= 0x6BF94116 (long)
漏洞多發函數
1.*memcpy(void *dest, const void *src, size_t n)函數
2.*strncpy(char *dest,const char *scr, size_t n)函數
ps說明:其中參數n,是size_t類型,size_t是一個無符號整型的類型
C語言源碼示例:
示例一:
char buf[80];
void vulnerable() {
int len = read_int_from_network();
char *p = read_string_from_network();
if (len > 80) {
error("length too large: bad dog, no cookie for you!");
return;
}
memcpy(buf, p, len);
}
當給len賦值為復數時,可繞過if判斷,因為memcpy()函數中 的len是size_t類型會把復數len轉換為整數,當len被賦值后絕對值很大時就會復制大量的內容到buf中,發生溢出
示例二:
void vulnerable() {
size_t len;
// int len;
char* buf;
len = read_int_from_network();
buf = malloc(len + 5);
read(fd, buf, len);
...
}
這個例子看似避開了緩沖區溢出的問題,但是如果 len 過大,len+5 有可能發生回繞。比如說,在 x86-32 上,如果 len = 0xFFFFFFFF,則 len+5 = 0x00000004,這時 malloc() 只分配了 4 字節的內存區域,然后在里面寫入大量的數據,緩沖區溢出也就發生了。(如果將 len 聲明為有符號 int 類型,len+5 可能發生溢出)
示例三:
void main(int argc, char *argv[]) {
unsigned short int total;
total = strlen(argv[1]) + strlen(argv[2]) + 1;
char *buf = (char *)malloc(total);
strcpy(buf, argv[1]);
strcat(buf, argv[2]);
...
}
這個例子接受兩個字符串類型的參數並計算它們的總長度,程序分配足夠的內存來存儲拼接后的字符串。首先將第一個字符串參數復制到緩沖區中,然后將第二個參數連接到尾部。如果攻擊者提供的兩個字符串總長度無法用 total 表示,則會發生截斷,從而導致后面的緩沖區溢出。
C語言的整型溢出問題
