記憶體中的資料儲存
複習一下計算機原理的知識,整理一下資料在記憶體中的儲存以及編碼方式,主要是學習一下整數、浮點數的編碼方式,以及由此導致的安全問題。水平有限,歸納不到位的地方還請指正。
知識小貼士
資料在記憶體中都是按照0/1來排列儲存的,在記憶體層面是沒有整數、浮點數之類的區別的;到了語言層面才會談及這些問題:程式按照程式設計師宣告的資料型別,讀取記憶體,並根據預先設定好的編碼方式得到相應資料的值。
儲存方式
首先我們要注意一下大小端儲存方式的區別,儲存方式使用的是大端儲存還是小段儲存和使用的平臺有關。
小端儲存就低位資料儲存在低地址,大端儲存正好相反。
整數
編碼
整數的編碼有原碼、反碼、補碼三類,計算機內對於整數的儲存用補碼錶示。
上面三種編碼方式都是針對signed 整數來說的,對於unsigned自然沒必要折騰這些了,當然無符號整數在記憶體中的編碼方式就是原碼。
定義
三種編碼方式定義如下:
對於正數來說,原碼、反碼以及補碼是其本身;負數的原碼是其本身,反碼是對原碼除符號位之外的各位取反,補碼則是反碼加1。
舉例
原碼: +0:0 000 0000,-0:1 000 0000
反碼: +0:0 000 0000,-0: 1 111 1111
補碼: +0:0 000 0000,-0: 1 111 1111+1=1 0000 0000,因為計算機會進行截斷,只取低8位,所以-0的補碼錶示形式為0000 0000。
表示範圍
可以看到,只有補碼下的+0和-0表示方式是相同的,並且規定補碼下的1 000 0000表示為-128(-2^n-1),表示範圍較其他兩中編碼方式多了一個數字。
補碼:-2^(n-1)~2^(n-1)-1
原碼:-2^(n-1)+1~2^(n-1)-1
反碼:-2^(n-1)+1~2^(n-1)-1
計算
補碼可以直接帶著符號位進行加減運算(不瞭解的可以去查查資料,網上一大堆),也是因為這個優勢加上表示範圍大,所以整數的編碼方式採用補碼的方式。
安全問題
首先先了解一下各種型別的取值範圍:
型別
|
位元組
|
範圍
|
short int |
2byte(word)
|
0~32767(0~0x7fff) -32768~-1(0x8000~0xffff)
|
unsigned short int
|
2byte(word)
|
0~65535(0~0xffff)
|
int
|
4byte(dword) |
0~2147483647(0~0x7fffffff) -2147483648~-1(0x80000000~0xffffffff)
|
unsigned int
|
4byte(dword) |
0~4294967295(0~0xffffffff)
|
long int
|
8byte(qword)
|
正: 0~0x7fffffffffffffff 負: 0x8000000000000000~0xffffffffffffffff
|
unsigned long int
|
8byte(qword)
|
0~0xffffffffffffffff
|
關於整數溢位的東西ctf-wiki講得很清楚,其中需要注意的就是邊界、有無符號的問題,一般都是在數字範圍邊界處加減導致的資料錯誤,以及有無符號數的問題。
比如:
將-1賦值給一個unsigned 數,-1在記憶體中是0xffffffff(假如是int)來表示的,但是如果按照unsigned來讀取的話就會變成一個很大的數;
再有就是資料在邊界數加減的話會導致錯誤,比如無符號0xffffffff(int)+1變成0;
還有就是大範圍賦值給小範圍,小範圍會按照低位截斷來讀取的,比如
long int a = 0x1000000000000000; int b = 0; b =a ;
那麼最後b的值就會按照低位截斷,讀取a的低四個位元組,最終b = 0;
wiki上面還提到了 在彙編層面,有符號是透過暫存器來運算的;而無符號是透過記憶體來計算的。
abs函式的經典漏洞
abs()函式透過man指令查一下用法
RETURN VALUE
Returns the absolute value of the integer argument, of the appropriate integer type for the function.
引數是一個int型別的數,返回值是引數的絕對值;
我們知道int型別的數字範圍在計算機中表示的話,負數是比整數多一個的(也就是0x80000000),那麼0x80000000當做引數穿進去是得不到正確的絕對值的。會出現什麼樣的後果呢,我們來看一下。
測試指令碼:
#include <stdio.h><br>
#include <stdlib.h><br><br>
int main()<br>
{<br>
int a = -0x80000000;<br>
int b = abs(a);<br>
printf("the return value of abs(a) is : %d(10) 0x%x(16) .\n",b,b);<br><br>
return 0;<br>
}
效果:
the return value of abs(a) is : -2147483648(10) 0x80000000(16) .
可以看到,當我們輸入最小的負數時,abs並不能正確處理,這個問題是計算機整數表示本身設定的問題,以後利用這個函式的時候可要記得規避這個問題。
例子
2019-qwb-babycpp其中便包含了這個漏洞的利用。
浮點數
iddm正在抽時間寫o(╥﹏╥)o
reference:
https://www.cnblogs.com/knsbyoo/p/9028056.html
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/integeroverflow/intof-zh/