信息安全系統設計基礎第三週學習總結 算法
第四周:學習任務教材第二章 編程
第四周(9.28-10.04): 數組
學習計時:共10小時 安全 讀書:2 網絡 代碼:2 數據結構 做業:3 函數 博客:3學習 |
學習目標測試 |
1. 理解二進制在計算機中的重要地位 編碼 2. 掌握布爾運算在C語言中的應用 3. 理解有符號整數、無符號整數、浮點數的表示 4. 理解補碼的重要性 5. 能避免C語言中溢出,數據類型轉換中的陷阱和可能會致使的漏洞 |
學習任務:
公式能夠不看,習題不能不作,考覈題目和課後習題相似,重點題目:
2.四、2.六、2.八、2.十一、2.1三、2.1四、2.1八、2.1九、2.2一、2.2三、
2.2四、2.2五、2.2七、2.2九、2.3三、2.3四、2.3九、2.40、2.4二、2.4三、
2.4四、2.4五、2.4七、2.50、2.5二、2.54
p20: 三種數字:無符號數、有符號數(2進制補碼)、浮點數,信息安全系同窗從逆向角度考慮爲何會產生漏洞
p22: 進制轉換,注意拿二進制做中間結果就好轉了
p25: gcc -m32 能夠在64位機上(好比實驗樓的環境)生成32位的代碼
p26: 字節順序是網絡編程的基礎,記住小端是"高對高、低對低",大端與之相反就能夠了。
p28: 代碼執行一下
p32: 能區分邏輯運算(結果是1或0)和位運算(結果是位向量),全部邏輯運算均可以用與、或、非表達(最大式、最小式),而與或非能夠用"與非"或"或非"表達,因此,只要一個與非門,就能夠完成全部的邏輯運算。
p33: 掩碼是位運算的重要應用,對特定位能夠置一,能夠清零
p38: 要用C99中的"long long"類型,編譯是要用 gcc -std=c99
p39: 補碼的利用寄存器的長度是固定的特性簡化數學運算。想一想鐘錶,12-1 等價於 12 + 11,利用補碼能夠把數學運算統一成加法,只要一個加法器就能夠實現全部的數學運算。
p44: 注意C語言中有符號數和無符號數的轉換規則,位向量不變。想一想第一章說的 信息就是"位+上下文"
p48: 怎麼樣讓負數等於正數? 信息安全的逆向思惟
p49: 0擴展和符號擴展
p52: 深刻思考一下代碼和結果
p54: 如何讓整數運算溢出?如何避免?
p62例子看看
p67: 關於整數運算的最後思考
p67: 浮點數有科學計數法的基礎就不難理解,IEEE標準754
p68: 浮點數運算的不精確性與舍入
p70: IEEE浮點標準,float/double類型
p74: 整數與浮點數表示同一個數字的關係
p78: 整數與浮點數轉換規則
p80:家庭做業能夠選作,協調好每題最多兩人一組作,一星題目一人加一分,二星加二分,三星加三分,四星加四分
筆記:
1.三種數字:無符號數、有符號數(2進制補碼)、浮點數,信息安全系同窗從逆向角度考慮爲何會產生漏洞
無符號數:基於傳統的二進制表示法,表示大於或者等於零的數字。
補碼:表示有符號整數最多見的方法,能夠爲正能夠爲負。
浮點數:表示實數的科學計數法。
結果太大可能會使得某些運算溢出,大量的計算機安全漏洞是因爲計算機算術運算的微妙細節引起的。
2. 進制轉換,注意拿二進制做中間結果就好轉了
四位二進制等於一位十六進制,2的n次冪換算十六進制,
練習題2.4:
0x503c+0x8=0x5044
0x503c-0x40=0x4ffc
0x503c+64=0x507c
0x50ea-0x503c=0xae
3. gcc -m32 能夠在64位機上生成32位的代碼
4. 字節順序是網絡編程的基礎,記住小端是"高對高、低對低",大端與之相反就能夠了。
小端法:最低有效字節在最前面的方式
大端法:最高有效字節在最前面的方式
也有雙端法。
已知,Linux 3二、 Windows、Linux 64是小端法機器。Sun是大端法機器。
5. p28的代碼,使用強制類型轉換來訪問和打印不一樣程序對象的字節表示。
代碼以下:
執行一下,達到以下結果:
練習2.6:
A:0000 0000 0011 0101 1001 0001 0000 0001
0100 1010 0101 0110 0100 0101 0000 0100
B:右移兩位可最大又21位匹配
C:除了最高有效位1,整數的全部位都嵌在浮點數中,而浮點數有一些非零的高位不與整數中的高位相匹配
6. 能區分邏輯運算(結果是1或0)和位運算(結果是位向量),全部邏輯運算均可以用與、或、非表達(最大式、最小式),而與或非能夠用"與非"或"或非"表達,因此,只要一個與非門,就能夠完成全部的邏輯運算。使用掩碼,利用位向量來對集合編碼。掩碼是位運算的重要應用,對特定位能夠置一,能夠清零。
練習2.8:
a 01101001
b 01010101
~a 10010110
~b 10101010
a&b 01000001
a|b 01111101
a^b 00111100
7. 要用C99中的"long long"類型,編譯是要用 gcc -std=c99,C語言定義了每種數據類型必須可以表示的最小的取值範圍。C和C++都支持有符號和無符號數,JAVA只支持有符號數。
練習2.11:
練習2.13:
bis(x,y)
bis(bic(x,y),bic(y,x))
bis(x,y)實際上是保留x的1,加入了y的1,也就是"或"。
練習2.14:
x&y 0x20 x&&y 0x01
x|y 0x7F x||y 0x01
~x|~y 0xDF !x||!y 0x00
X&!y 0x00 x&&~y 0x01
8. 補碼的利用寄存器的長度是固定的特性簡化數學運算。相似於鐘錶,12-1 等價於 12 + 11,利用補碼能夠把數學運算統一成加法,只要一個加法器就能夠實現全部的數學運算。對於某些程序來講,用某個肯定大小的表示來編碼數據類型很是重要,好比Java標準很是明確,採用補碼錶示,單字節數據類型成爲byte而不是char且沒有long long數據類型。所以保證了Java程序在不管什麼機器上都能表現得同樣。
9. 有符號數還有兩種標準的表示方法:反碼和原碼。C語言容許在各類不一樣的數字數據類型之間作強制類型轉換,且支持全部整型數據類型的有符號和無符號運算,一般大多數數字都默認爲有符號的。注意C語言中有符號數和無符號數的轉換規則,位向量不變。信息就是"位+上下文"。
練習2.18:
A:440 B:20 C:-424 D:-396 E:68 F:-312 G:16 H:12 I:-276 J:32
練習2.19:
x |
hex(x) |
T2U4(x) |
-8 |
0x8 |
8 |
-3 |
0xD |
13 |
-2 |
0xE |
14 |
-1 |
0xF |
15 |
0 |
0x0 |
0 |
5 |
0x5 |
5 |
10.怎麼樣讓負數等於正數? 信息安全的逆向思惟
利用printf首先將一個字當作一個無符號數輸出,而後再把它當作一個有符號數輸出。轉換的原則是底層的位表示保持不變。就是應用函數U2TW,將無符號數轉換爲有符號數。
練習2.21:
類型 |
求值 |
無符號數 |
1 |
有符號數 |
1 |
無符號數 |
0 |
有符號數 |
1 |
無符號數 |
1 |
11. 0擴展和符號擴展
0擴展:將一個無符號數轉換爲一個更大的數據類型,在表示的開頭添加0.
符號擴展:將一個補碼數字轉換爲一個更大的數據類型。
練習2.23:
A:fun1是無符號移位,即邏輯移位,fun2則是算數移位
W |
fun1(w) |
fun2(w) |
0x00000076 |
0x00000076 |
0x00000076 |
0x87654321 |
0x00000021 |
0x00000021 |
0x000000C9 |
0x000000C9 |
0xFFFFFFFC9 |
0xEDCBA987 |
0x00000087 |
0xFFFFFFF87 |
B:fun1從參數的低8位中提取獲得範圍0-255之間的整數,fun2也是從低8位提取,可是進行了符號擴展,因此是介於-128~127
12. 深刻思考一下代碼和結果
當參數length爲0時,程序代碼會出錯,說明從有符號數到無符號數的隱式強制類型轉換很容易引起錯誤。因爲length在代碼中是無符號的,0-1將會進行無符號計算,就會等價於MOD算法,至關於獲得一個MAX數值,因此全部數都是小於等於MAX值的,因而老是爲真。因此代碼嘗試訪問數組a的非法元素。
練習2.24:
無符號截斷值 |
補碼截斷值 |
0 |
0 |
2 |
2 |
1 |
1 |
3 |
3 |
7 |
-1 |
練習2.25:
出錯緣由:當參數length爲0時,程序代碼會出錯,說明從有符號數到無符號數的隱式強制類型轉換很容易引起錯誤。因爲length在代碼中是無符號的,0-1將會進行無符號計算,就會等價於MOD算法,至關於獲得一個MAX數值,因此全部數都是小於等於MAX值的,因而老是爲真。因此代碼嘗試訪問數組a的非法元素。
修改辦法:將length聲明爲int類型進行有符號運算,或者將for循環的測試條件改成i<length防止溢出
13. p54: 如何讓整數運算溢出?如何避免?
首先,無符號數運算:全部無符號數運算都是以2的n次方爲模,(n是結果中的位數)。因此它不存在運算時的沒有那種所謂的"溢出",當它超過範圍時,從零開始從新計數!當一個無符號數和有符號數相加的時候,有符號數會自動轉化爲無符號數參與運算!
其次,有符號數運算: 是可能發生"溢出"的,並且溢出的結果是未定義的。當一個運算的結果發生溢出時,作出任何假設都是不安全的。
避免方法:檢查 cpu 的狀態標誌寄存器中的溢出標誌位,驗算運算是否超出了溢出標誌位。
練習2.27:
int fun(unsigned x,unsigned y){
unsigned sum=x+y;
return sum>=x;
}
練習2.29:
x |
y |
x+y |
x+(t/5)y |
狀況 |
[10100] |
[10001] |
100101 |
00101 |
1 |
[11000] |
[11000] |
110000 |
10000 |
2 |
[10111] |
[01000] |
111111 |
11111 |
2 |
[00010] |
[00101] |
000111 |
00111 |
3 |
[01100] |
[00100] |
010000 |
10000 |
4 |
練習2.33:
x |
-(t/4)x |
||
十六進制 |
十進制 |
十進制 |
十六進制 |
0 |
0 |
0 |
0 |
5 |
5 |
-5 |
B |
8 |
-8 |
-8 |
8 |
D |
-3 |
3 |
3 |
F |
-1 |
1 |
1 |
練習2.34:
x·y |
截斷的x·y |
20[010100] 12[001100] |
4[100] -4[100] |
14[001110] -2[111110] |
6[110] -2[110] |
36[100100] 4[000100] |
4[100] -4[100] |
14. p62 XDR庫中的安全漏洞
參數過大,第10行上的乘法運算會溢出,從第16行開始的循環會試圖複製全部字節,超出已分配的緩衝區的界限,從而破壞其餘的數據結構,致使程序崩潰或者行爲異常。可見malloc使用了一個32位無符號數做爲參數,所以不可能分配一個大於232個字節的塊,因此不必,應該將其放棄,返回一個NULL。
練習2.39:
將表達式變爲-(x<<m),設字長w,n=w-1。計算(x<<w)-(x<<m),而將x向左移動w位會獲得0。
練習2.40:
表達式 |
(x<<2)+(x<<1) |
(x<<5)-x |
(x<<1)-(x<<3) |
(x<<6)-(x<<3)-x |
練習2.42:
int div16(int x){
int bias=(x>>31)&0xF;
return (x+bias)>>4;
}
練習2.43:
M=31,利用(x<<5)-x求x*M
N=8,y是負數時加上偏置量7,且右移3位
15.關於整數運算的最後思考
計算機執行的"整數"運算其實是一種MOD運算形式,表示數字的有限字長限制了可能的值的取值範圍,致使運算結果溢出。補碼提供了一種既能表示負數又能表示整數的靈活方法。C語言中某些規定可能會產生出乎意料的結果,並且難以察覺,unsigned
數據類型概念上簡單,卻可能致使不少意想不到的行爲。
練習2.44:
A:假。令x=TMin32,x-1=TMax32
B:真。前式爲0則有位x2等於1,左移29位後,x2將會成爲符號位
C:假。令x爲0xFFFF,x*x爲0xFFFE0001.
D:真。X非負數,-x是非正。
E:假。令x=TMin32,那麼x與-x都是負數
F:真。
G:真。~y=-y-1.uy*ux=x*y,因此等價。
16. 浮點數有科學計數法的基礎就不難理解,IEEE標準754
17.浮點數運算的不精確性與舍入
二進制表示法只能表示那些可以被寫成x*2y的數,其它的值只能被近似表示。增長二進制的表示長度能夠提升表示的精度。
練習2.45:
小數值 |
二進制表示 |
十進制表示 |
1/8 |
0.001 |
0.125 |
3/4 |
0.11 |
0.75 |
25/16 |
1.1001 |
1.5625 |
43/16 |
10.1011 |
2.6875 |
9/8 |
1.001 |
1.125 |
47/8 |
101.111 |
5.875 |
51/16 |
11.0011 |
3.1875 |
18. IEEE浮點標準,float/double類型
IEEE浮點標準用V=(-1)S*M*2E的形式來表示一個數:符號s決定正負,對於數值0的符號位解釋做爲特殊狀況處理。尾數M是一個二進制小數。階碼E做用是對浮點數加權,權重是2的E次冪。
浮點數的位劃分三個字段,分別對這些值進行編碼:
一個單獨的符號位s直接編碼符號s
K位的階碼字段編碼階段E
N位小數字段編碼尾數M
兩種最多見的格式就是float 和double,區分一下單精度和雙精度的具體狀況。
練習2.47:
見書本。
19.整數與浮點數表示同一個數字的關係:相關的區域對應整數的低位,恰好在等於1的最高有效位以前中止,也就是隱含的開頭的位1,與浮點數表示的小數部分的高位時相匹配的。
練習2.50:
原始值 |
舍入後的值 |
10.0102 2+(1/4) |
10.0 2 |
10.0112 2+(3/8) |
10.1 2+(1/2) |
10.1102 2+(3/4) |
11.0 3 |
11.0012 3+(1/8) |
11.0 3 |
練習2.52:
1011110 |
15/2 |
1001111 |
15/2 |
0101001 |
25/32 |
0110100 |
3/4 |
1101111 |
31/2 |
1011000 |
16 |
0000001 |
1/64 |
0001000 |
1/64 |
20.整數與浮點數轉換規則
練習2.54:
A:真,double比int具備更大的精度和範圍
B:假,令x=TMax即爲假
C:假,令d=le40,右邊可獲得正無窮
D:真,double比float具備更大的精度和範圍
E:真,浮點數取非就是對他的符號位取反
F:真,這是浮點數的除法
G:真
H:假,令f爲1.0e20而d爲1.0,f+d會舍入到1.0e20,左式所以求得0.0而右式爲1.0。
家庭做業:
2.65 寫出代碼實現以下函數:
/*Return 1 when x contains an even number of 1s; 0 otherwise. Assume w=32*/
int even_ones(unsigned x);
函數應該遵循位級整數編碼規則,不過你能夠假設數據類型int有w=32位,你的代碼最多隻能包含12個算術運算、位運算和邏輯運算。
解讀題目:當無符號數x包含偶數個1時,返回值爲1,不然爲返回值爲0,假設x的數據類型是int 有w=32位。
解題思路:要求x所包含的1的個數,能夠對x的每一個位進行異或運算。若是獲得的結果是0,那麼就說明x包含偶數個1,則返回值爲1,;若是獲得的結果是1,那麼說明x包含奇數個1,則返回值爲0。
代碼編寫過程:因爲x是個32位int類型數,因此①首先採用折半縮小規模的方法進行逐位異或。②最後獲得的x值再與1進行與運算就會獲得一個32位中前31均爲0,尾數是0或者是1(用於判斷是原x包含奇數仍是偶數個1),③返回這個值就完成了題目需求。
代碼編寫:
int even_ones(unsigned x){
x ^= (x >> 16);//等同於x=x^(x>>16)
x ^= (x >> 8); //等同於x=x^(x>>8)
x ^= (x >> 4); //等同於x=x^(x>>4)
x ^= (x >> 2); //等同於x=x^(x>>2)
x ^= (x >> 1); //等同於x=x^(x>>1)
return !(x&1);
}
遇到的問題及解決:
解決:在類似的重複的練習中摸清楚了算數移位是須要補1而邏輯移位須要補0
解決:我在草稿紙上根據bis和bic的功能假設了兩個8位二進制數,經過bis和bic
的運算來摸清他們之間的規律。
解決:只好經過作幾道練習題來稍稍瞭解。
解決:通過搭檔符運錦同窗給個人題目解析和解題思路,我根據逐位異或的算法把這道家庭做業的代碼寫了出來。截圖:
結果:
即當a爲10e5+0時,返回值爲0
若a爲10e5+1時,返回值爲1