好好說話之整數溢位

2021-10-09 03:39:02 字數 3911 閱讀 9036

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...