2017-2018-1 20155229 《信息安全系統設計基礎》第十四周學習總結
對“第三章 程序機器級表示”的深入學習
- 我選擇這章的理由是第一次學的時候還是不太理解,老師也有說這章建議在認真學習,所以本周的學習任務是認真再次學習這一章
c語言、匯編代碼以及機器代碼
這三者的關系大概順序是:
- [1]C預處理器擴展源代碼,展開所以的#include命名的指定文件;
- [2]編譯器產生匯編代碼(.s);
- [3]匯編器將匯編代碼轉化成二進制目標文件(.o).
- 匯編起著高級語言和底層二進制代碼的橋梁作用.
1.機器語言
機器語言是用二進制代碼表示的計算機能直接識別和執行的一種機器指令的集合。它是計算機的設計者通過計算機的硬件結構賦予計算機的操作功能。機器語言具有靈活、直接執行和速度快等特點。
2.匯編語言
匯編語言是用符號代替了機器指令代碼,而且助記符與指令代碼一一對應,基本保留了機器語言的靈活性。使用匯編語言能面向機器并較好地發揮機器的特性,得到質量較高的程序。匯編語言用來編制系統軟件和過程控制軟件,其目標程序占用內存空間少,運行速度快,有著高級語言不可替代的用途。
3.C語言
C語言是一門通用計算機編程語言,應用廣泛。C語言的設計目標是提供一種能以簡易的方式編譯、處理低級存儲器、產生少量的機器碼以及不需要任何運行環境支持便能運行的編程語言
匯編代碼起到了承上啟下的作用,理解匯編代碼以及與它的源C代碼之間的聯系,是理解程序如何執行的關鍵一步,因為編譯器隱藏了太多的細節如:程序計數器、寄存器(整數、條件碼、浮點)等。
調用函數
調用一個函數,實現將數據和控制代碼從一個部分到另一個部分的跳轉。
幀指針與棧指針的不公之處: ebp放與參數與返回地址的最下方,方便計算參數的偏移位置;而esp一直在棧頂,可以通過push將數據壓入,通過pop取出,增加指針來釋放空間。
①轉移控制
call指令,同跳轉一樣,可以是直接的,也可以是間接的。
call指令的效果:1、將返回地址入棧2、跳轉到被調用過程的起始處。
當調用過程返回時,執行會從此處繼續。ret指令從棧中彈出地址,并跳轉到這個位置(表示程序計數器被賦值為這個返回地址,并跳轉到這個地址,執行這個地址對應的指令)。
其中call先將返回地址入棧,然后跳轉到函數開始的地方執行。(返回地址是開始調用者執行call的后面那條指令的地址)當遇到ret指令的時候,彈出返回地址,并跳轉到該處繼續執行調用者剩余部分。
leave
等價于:
movl %ebp, %esp
popl %ebp
程序寄存器組被所有過程共享。寄存器 %eax 、 %edx 、 %ecx 為調用者保存寄存器,由P保存,Q可以覆蓋這些寄存器而不破壞P的數據。寄存器 %ebx 、 %esi 、 %edi 為被調用者保存寄存器,Q需要在覆蓋它們之前將值保存到棧中,并在返回前恢復它們。
int swap(int *x, int *y)
{int t = *x;*x = *y;*y = t;return *x + *y;
}int func()
{int a = 1234;int b = 4321;int s = swap(&a, &b);return s;
}.p2align 4,,15
.globl swap.type swap, @function
swap:pushl %ebp 壓入幀指針movl %esp, %ebp 將幀指針設為棧指針指向位置movl 8(%ebp), %edx 讀取xmovl 12(%ebp), %ecx 讀取ypushl %ebx 壓入%ebx(被調用者保存寄存器)movl (%edx), %eax 讀取*xmovl (%ecx), %ebx 讀取*ymovl %ebx, (%edx) 將*y寫入x指向存儲器位置movl %eax, (%ecx) 將*x寫入y指向存儲器位置addl (%edx), %eax *x+*ypopl %ebx 恢復%ebxpopl %ebp 彈出幀指針(%esp等于原值)ret 返回.size swap, .-swap.p2align 4,,15
.globl func.type func, @function
func:pushl %ebp 壓入幀指針movl $5555, %eax 保存計算結果(編譯器-O2優化)movl %esp, %ebp 將幀指針設為棧指針指向位置popl %ebp 彈出幀指針(%esp未變)ret 返回.size func, .-func
②數據傳送
當調用一個過程的時候,除了要把控制傳遞給調用過程,調用還需要把數據作為參數傳遞過去,調用過程可能返回一個值。
如果一個函數有大于6個整型參數,超出6個的部分就通過保存在調用者的棧幀來傳遞。
上面的程序代碼,前六個參數可以通過寄存器傳遞,后面的兩個通過棧傳遞。
③棧上的局部存儲
目前為止我們看到的大多數程序示例都不需要超過寄存器大小的本地存儲。不過以下情況局部數據必須要放入內存中。
1.寄存器不足以存放所有的本地數據。
2.對一個局部變量使用運算符“&”。
3.某些局部變量是數組或者是結構體,必須能夠通過數據的引用訪問到。
eg.#include<stdio.h>int swap(int* a,int* b) {int temp = *a;*a = *b;*b = temp;return *a+*b;}int main(){int a=5,b=3;int tot = swap(&a,&b);printf("%d",tot);return 0;}
上面的匯編代碼是一個交換兩個int數據,并得到兩個數之和的程序。函數中,首先在棧上分配了24個字節,其中可以看到的是棧頂的前四個字節用來保存變量‘a’,之后的四個用來保存變量‘b’,將寄存器%rax的值保存在0x8-0x18(新分配的字節在返回地址的頂部)。
④遞歸的過程
因為寄存器和棧幀的存在是的x86-64過程能夠遞歸的調用自身,每個過程調用在棧中都有自己的私有空間,因此多個未完成的調用的局部空間不會相互影響,棧的原則也提供了適當的策略,當過程被調用時分配局部存儲,返回時釋放局部存儲。
x86-64:將IA32擴展到64位
數據類型的比較:
訪問信息:
- 算術指令:當大小不同的操作數混在一起的時候,必須進行正確的擴展
過程
由于寄存器翻了一倍,64位中不需要棧幀來存儲參數,而是直接使用寄存器
EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP 寄存器詳解
4個數據寄存器(EAX、EBX、ECX和EDX)
2個變址和指針寄存器(ESI和EDI)
2個指針寄存器(ESP和EBP)
eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 匯編語言中CPU上的通用寄存器的名稱,是32位的寄存器。如果用C語言來解釋,可以把這些寄存器當作變量看待。
4個數據寄存器(EAX、EBX、ECX和EDX):
32位CPU有4個32位的通用寄存器EAX、EBX、ECX和EDX。對低16位數據的存取,不會影響高16位的數據。這些低16位寄存器分別命名為:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。
4個16位寄存器又可分割成8個獨立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每個寄存器都有自己的名稱,可獨立存取。程序員可利用數據寄存器的這種“可分可合”的特性,靈活地處理字/字節的信息。
EAX
是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。
EBX
是"基地址"(base)寄存器, 在內存尋址時存放基地址。
ECX
是計數器(counter), 是重復(REP)前綴指令和LOOP指令的內定計數器。
EDX
則總是被用來放整數除法產生的余數。
ESI/EDI
分別叫做"源/目標索引寄存器"(source/destination index),因為在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目標串.
EBP
是"基址指針"(BASE POINTER), 它最經常被用作高級語言函數調用的"框架指針"(frame pointer).
eax,ax,al(ah)之間的關系
- eax是32位寄存器,ax是16位寄存器,al(ah)是八位寄存器。那么eax存儲的數據就是ax的兩倍,ax是al(ah)的兩倍。
- eax可以存儲的數字是DWORD(雙字)ax存儲的是WORD(字)AL(AH)存儲的是BYTE(字節)
- 32位寄存器EAX、EBX、ECX和EDX不僅可傳送數據、暫存數據保存算術邏輯運算結果,而且也可作為指針寄存器
緩沖區溢出
緩沖區溢出是指當計算機程序向緩沖區內填充的數據位數超過了緩沖區本身的容量。
eg.#include <stdio.h> #include <string.h> #include <iostream> using namespace std; int main(int argc, char *argv[]) { char buf[10]; strcpy(buf, argv[1]); cout<<buf; return 0; }
連續輸入一些字符就產生了溢出。
C語言常用的strcpy、sprintf、strcat 等函數都非常容易導致緩沖區溢出問題。
程序運行時,其內存里面一般都包含這些部分:(1)程序參數和程序環境;(2)程序堆棧(堆棧則比較特殊,主要是在調用函數時來保存現場,以便函數返回之后能繼續運行),它通常在程序執行時增長,一般情況下,它向下朝堆增長。(3)堆,它也在程序執行時增長,相反,它向上朝堆棧增長;(4)BSS 段,它包含未初始化的全局可用的數據(例如,全局變量);(5)數據段,它包含初始化的全局可用的數據(通常是全局變量);(6)文本段,它包含只讀程序代碼。
BSS、數據和文本段組成靜態內存:在程序運行之前這些段的大小已經固定。程序運行時雖然可以更改個別變量,但不能將數據分配到這些段中。
在棧中分配某個字節數組來保存一個字符串,但是字符串的長度超出了為數組分配的空間。C對于數組引用不進行任何邊界檢查,而且局部變量和狀態信息,都存在棧中。這樣,對越界的數組元素的寫操作會破壞存儲在棧中的狀態信息。當程序使用這個被破壞的狀態,試圖重新加載寄存器或執行ret指令時,就會出現很嚴重的錯誤。
eg.
void echo()
{char buf[8] ;gets(buf) ;puts(buf) ;
}
棧是向地地址增長的,數組緩沖區是向高地址增長的。故,長一些的字符串會導致gets覆蓋棧上存儲的某些信息。
隨著字符串變長,下面的信息會被破壞:
輸入的字符數量 被破壞的狀態
0---7 無
8---11 保存的%ebx的值
12---15 保存的%ebp的值
16---19 返回地址
20+ caller中保存的狀態
如果破壞了存儲%ebp的值,那么基址寄存器就不能正確地恢復,因此調用者就不能正確地引用它的局部變量或參數。
如果破壞了存儲的返回地址,那么ret指令會使程序跳轉到完全意想不到的地方。
緩沖區溢出的一個更加致命的使用就是讓程序執行它本來不愿意執行的函數。
輸入給程序一個字符串,這個字符串包含一些可執行代碼的字節編碼,稱為攻擊代碼,另外還有一些字節會用一個指向攻擊代碼的指針覆蓋返回地址。這是一種最常見的通過計算機網絡攻擊系統安全的方法。執行ret指令的效果就是跳轉到攻擊代碼。
為buf分配的空間只有8個字節元素,上面地址保存的是%ebx, %ebp和返回地址的值,如果buf的長度超過分配的長度,就會把多出來的元素寫入到保存這些寄存器地址的值,破壞%ebx,%ebp和返回地址的值,這樣程序就不能正確的返回%ebx的值,不能正確的返回上以棧幀的地址,不能正確返回下一條要執行的命令地址。如果這些輸入的元素包含一些可執行代碼的字節編碼,稱為攻擊代碼(exploit code), 比如串改返回地址的值,使程序返回到惡意程序的代碼地址進行執行,就會成為我們所說的蠕蟲和病毒。
對抗緩沖區溢出攻擊
1、棧隨機化
為了在系統中插入攻擊代碼,攻擊者不但要插入代碼,還要插入指向這段代碼的指針,這個指針也是攻擊字符串的一部分。產生這個指針需要知道這個字符串放置的棧地址。在過去,程序的棧地址非常容易預測,在不同的機器之間,棧的位置是相當固定的。
棧隨機化的思想使得棧的位置在程序每次運行時都有變化。因此,即使許多機器都運行相同的代碼。它們的棧地址都是不同的。
實現的方式是:程序開始時,在棧上分配一段0--n字節之間的隨機大小空間。程序不使用這段空間,但是它會導致程序每次執行時后續的棧位置發生了變化。
在Linux系統中,棧隨機化已經變成了標準行為。(在linux上每次運行相同的程序,其同一局部變量的地址都不相同)
2、棧破壞檢測
在C語言中,沒有可靠的方法來防止對數組的越界寫,但是,我們能夠在發生了越界寫的時候,在沒有造成任何有害結果之前,嘗試檢測到它。
3、限制可執行代碼區域
限制那些能夠存放可執行代碼的存儲器區域。在典型的程序中,只有保存編譯器產生的代碼的那部分存儲器才需要是可執行的,其他部分可以被限制為只允許讀和寫。
一般的系統允許三種訪問的形式:讀(從存儲器讀數據)、寫(存儲數據到存儲器)和執行(將存儲器的內容看作是機器級代碼)
浮點數的機器級表示
為什么浮點數的表示是不精確的?(參考鏈接見最后)
- IEEE標準754規定了三種浮點數格式:單精度、雙精度、擴展精度。
- 浮點數的表示格式:n是浮點數,s是符號位,m是尾數,e是階數。
eg.單精度的20000.4
20000.4轉換為單精度的2進制是多少?
此單精度浮點數是正數,那么尾數符號s=0,指數(階數)e是8位,30到23位,尾數m(科學計數法的小數部分)23位長,22位到0位,共32位,如圖
先看整數部分,20000先化為16進制(4e20)16,則二進制是(100 1110 0010 0000)2,一共15位。
再看小數部分,0.4化為二進制數,這里使用乘權值取整的計算方法,使用0.X循環乘2,每次取整數部分,但是我們發現,無論如何x2,都很難使得0.X為0.0,就相當于十進制的無限循環小數0.33333……一樣,10進制數,無法精確的表達三分之一。也就是人們說的所謂的浮點數精度問題。因單精度浮點數的尾數規定長23位,那現在乘下去,湊夠24位為止,即再續9位是(1.011001100)2
習題
3.59
假設
多個2 ^ 128溢出
# void store_prod(int128_t* dest, int64_t x, int64_t y)
# dest in %rdi, x in %rsi, y in %rdx
store_prod:movq %rdx, %rax # %rax = ycqto # (int128_t)y, %rdx = (-1)y_63movq %rsi, %rcx # %rcx = x# x >> 63, if x == 1, %rcx = -1; if x_63 == 0, %rcx = 0# %rcx = (-1)x_63sarq $63, %rcx# pay attention, imulq behaves differently according to param number(1/2)imulq %rax, %rcx # %rcx = y * -x_63imulq %rsi, %rdx # %rdx = x * -y_63addq %rdx, %rcx # %rcx = x * -y_63 + y * -x_63mulq %rsi # %rdx:%rax <= ux * uy# lower 64 bits are same for x * y and ux * uy. ref (2.18) on book# %rdx = ux * uy(high 64 bits) - (x_{63}y + y_{63}x)2^{64}addq %rcx, %rdxmovq %rax, (%rdi) # set lower 64bitsmovq %rdx, 8(%rdi) # set higher 64bitsret
3.60
答:
A:%rdi
中保存著x
%esi
中保存著n
%rax
中保存著result
%rdx
中保存著mask
B:result和mask的初始值是:
result = 0mask = 1
C:mask的測試條件是:mask != 0
D:mask = mask << n
F:
long loop2(long x, int n) {long result = 0;long mask;for (mask = 1; mask != 0; mask <<= n) {result |= (x & mask);}return result;
}
代碼托管
上周考試錯題總結
14.( 多選題 | 1 分)
The following table gives the parameters for a number of different caches. For
each cache, determine the number of cache sets (S), tag bits (t), set index bits (s),
and block offset bits (b)
A .
第三行S為1
B .
第一行t為24
C .
第二行s為5
D .
第三行b的值為5
正確答案: A C D
18.( 多選題 | 1 分)
有關RAM的說法,正確的是()
A .
SRAM和DRAM掉電后均無法保存里面的內容。
B .
DRAM將一個bit存在一個雙穩態的存儲單元中
C .
一般來說,SRAM比DRAM快
D .
SRAM常用來作高速緩存
E .
DRAM將每一個bit存儲為對一個電容充電
F .
SRAM需要不斷刷新
G .
DRAM被組織為二維數組而不是線性數組
正確答案:A C D E G
解析:SRAM用來作為高速緩存存儲器;DRAM用來作為主存以及圖形系統的幀緩沖區。SRAM的存取比DRAM快。SRAM對諸如光和電噪聲這樣的干擾不敏感。代價是SRAM 單元比DRAM單元使用更多的晶體管。
結對及互評
點評模板:
- 博客中值得學習的或問題:
-
- 代碼中值得學習的或問題:
-
本周結對學習情況
- [20155225]()
- 結對照片
- 結對學習內容- 相互講解學習的章節
其他(感悟、思考等,可選)
學習進度條
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 15篇 | 400小時 | |
第一周 | 20/20 | 1/ | 12/12 | |
第二周 | 42/62 | 1/2 | 8/20 | |
第三周 | 62/124 | 1/3 | 14/34 | |
第四周 | 61/185 | 1/4 | 10/44 | |
第五周 | / | 2/6 | 13/57 | |
第六周 | / | 2/8 | 17/74 | |
第七周 | / | 2/10 | 15/89 | |
第八周 | / | 2/12 | 12/101 | |
第九周 | / | 2/14 | 10/111 | |
第十一周 | / | 1/16 | 12/123 | |
第十三周 | / | 2/18 | 17/140 | 學習認為最重要的一章 |
第十四周 | / | 1/19 | 18/158 | 學習認為學的不好的一章 |
嘗試一下記錄「計劃學習時間」和「實際學習時間」,到期末看看能不能改進自己的計劃能力。這個工作學習中很重要,也很有用。
耗時估計的公式
:Y=X+X/N ,Y=X-X/N,訓練次數多了,X、Y就接近了。
參考:軟件工程軟件的估計為什么這么難,軟件工程 估計方法
計劃學習時間:18小時
實際學習時間:18小時
改進情況:深入學習第三章
(有空多看看現代軟件工程 課件
軟件工程師能力自我評價表)
參考資料
- 《深入理解計算機系統V3》學習指導
- 從如何判斷浮點數是否等于0說起——浮點數的機器級表示