c語言中的整型按資料型別主要分三類:短整形(short)、整形(int)、長整形(long)
按符號分類:有符號、無符號
每種資料型別都有自己的大小範圍:
型別位元組
範圍short int
2byte(word)
0~32767(0~0x7fff)
-32768~-1(0x8000~0xffff)
unsigned short int
2byte(word)
0~65535(0~0xffff)
int4byte(word)
0~2147483647(0~0x7fffffff)
-2147483648~-1(0x80000000~0xffffffff)
unsigned int
4byte(word)
0~4294967295(0~0xffffffff)
long
8byte(word)
正: 0~0x7fffffffffffffff
負: 0x8000000000000000~0xffffffffffffffff
unsigned long
8byte(word)
0~0xffffffffffffffff
如果說棧溢位是超過變數的棧空間範圍,那麼當程式中的資料超過其資料型別的範圍,則會造成溢位,整數型別的溢位被稱為整數溢位
上界溢位可以這麼理解:在資料型別範圍臨界點繼續增加
數值,導致溢位
# 偽**
short
int a;
a = a +1;
# 對應的彙編
movzx eax, word ptr [rbp -
0x1c
]/*將rbp - 0x1c位置的值(變數a)賦給eax暫存器*/
add eax,
1mov word ptr [rbp -
0x1c
], ax /*將ax(eax低2byte)中的值賦給rbp - 0x1c(變數a)*/
unsigned
short
int b;
b = b +1;
# assembly code
add word ptr [rbp -
0x1a],
1/*將rbp - 0x1a處的值(變數b)加1*/
我們看上面的例子,變數a是有符號短整形(short)也就是說它的範圍為正0~0x7fff,負0x8000~0xffff。有符號整型的計算主要是在暫存器中執行的,因為short佔2個位元組add eax,1
計算之後,在將ax(eax低2byte)中的值傳給變數a
再看變數b,它是無符號短整形,範圍為0~0xffff。無符號整型的計算主要是在棧中執行,所以add word ptr [rbp - 0x1a], 1
直接在棧中給變數b加上了1
上界溢位主要有兩種情況:
因為計算機底層指令是不區分有符號和無符號的,資料都是以二進位制形式存在 (編譯器的層面才對有符號和無符號進行區分,產生不同的彙編指令)。所以add 0x7fff, 1 == 0x8000
,這種上界溢位對無符號整型就沒有影響,但是在有符號短整型中,0x7fff 表示的是 32767,但是 0x8000 表示的是 -32768,用數學表示式來表示就是在有符號短整型中32767+1 == -32768
這種情況需要考慮的是第乙個運算元,比如上面的有符號型加法的彙編**是add eax, 1
,因為eax=0xffff
,所以add eax, 1 == 0x10000
,但是無符號的彙編**是對記憶體進行加法運算add word ptr [rbp - 0x1a], 1 == 0x0000
在有符號的加法中,雖然eax
的結果為 0x10000,但是只把ax=0x0000
的值儲存到了記憶體中,從結果看和無符號是一樣的
再從數字層面看看這種溢位的結果,在有符號短整型中,0xffff==-1,-1 + 1 == 0
,從有符號看這種計算沒問題。但是在無符號短整型中,0xffff == 65535, 65535 + 1 == 0
和上界溢位一樣,只不過在彙編中把add
替換成sub
。你可以這樣理解:在資料型別範圍臨界點繼續減少
數值,導致溢位
一樣有兩種情況:
第一種是sub 0x0000, 1 == 0xffff
,對於有符號來說0 - 1 == -1
沒問題,但是對於無符號來說就成了0 - 1 == 65535
。
第二種是sub 0x8000, 1 == 0x7fff
,對於無符號來說是32768 - 1 == 32767
是正確的,但是對於有符號來說就變成了-32768 - 1 = 32767
就如同前面的棧溢位,將不限制長度的字串放在定長的變數中。整數溢位就是將不限制長度的資料放在了定長的變數中,就好比你吃的非常的撐,然後媽媽讓你再喝一碗湯?
乙個示例:
$ cat test.c
#include
intmain
(void
)$ gcc test.c
$ ./a.out-1
asdfasfasdfasdfafasfasfasdfasdf
# gdb a.out
► 0x40066d
71> call malloc@plt <
0x400500
>
size:
0xf
我們只申請了0x20大小的堆,但是卻輸入0xffffffff長度的資料,從整形溢位到堆溢位
即使正確的對變數進行約束,也仍然有可能出現整數溢位漏洞,可以概括為錯誤的型別轉換,如果繼續細分下去,可以分為:
範圍大的變數賦值給範圍小的變數
可以這樣理解:你明明只能吃一小碗飯,媽媽問你吃多少,你說一碗就行,然後媽媽給你盛了一海碗
,親媽沒錯了
$ cat test2.c
void
check
(int n)
intmain
(void
)$ gcc test2.c
$ ./a.out
4294967296
vuln
**中講乙個範圍大的變數a(長整形),傳入check函式後變為範圍小的變數n(整型),這樣就造成了溢位
長整形占有8位元組的記憶體空間,但是整型只有4位元組,所以將long放進int之後會造成截斷,只能將long的低4位元組的值傳給整型變數long: 0x100000000 -> int: 0x00000000
,但是反過來將int放在long中時沒有事的
只做了單邊限制
這種情況只針對有符號型別
$ cat test3.c
intmain
(void
)else
printf
("please len < 10");
}$ gcc test3.c
$ ./a.out-1
aaaaaaaaaaaa
aaaaaaaaaaaa
我們雖然對變數len進行了限制,但是len是有符號整型,所以len的長度可以為負數,但是在read函式中,第三個引數的型別是size_t,該型別相當於unsigned long int,屬於無符號長整形
以後做到補上,wiki上沒給程式
鄰居好說話 之 氣泡排序
氣泡排序的主要思想就是 每次比較兩個相鄰的元素,如果它們比較大小之後,左右的順序錯誤,就相互交換位置。下面以從大到小排序為例,來講一下它的思路 第一輪首先處理第1個數和第2個數,使這兩個數從大到小排列,然後在前次處理後的基礎上處理第2個數和第3個數使其從大到小排列,以此類推,直到處理完第n 1個和第...
鄰居好說話 氣泡排序
簡化版的桶排序不僅僅有上一節所遺留的問題,更要命的是 它非常浪費空間!例如需要排序數的範圍是0 2100000000之間,那你則需要申請2100000001個變數,也就是說要寫成int a 2100000001 因為我們需要用2100000001個 桶 來儲存0 2100000000之間每乙個數出現...
鄰居好說話 氣泡排序
氣泡排序的基本思想是 每次比較兩個相鄰的元素,如果它們的順序錯誤就把它們交換過來。如下 include intmain for i 1 i n i 輸出結果 printf d a i return0 現在分別有5個人的名字 和分數 huhu 5分 haha 3分 xixi 5分 hengheng 2...