深入理解計算機系統——第三章—3.10在機器級程序中將控制與數據結合起來

3.10 在機器級程序中將控制與數據結合起來

3.10.1 理解指針

指針以一種統一方式,對不同數據結構中的元素產生引用。
1) 每個指針都對應一個類型。(指針類型不是機器代碼中的一部分;只是C語言提供的一種抽象,幫助程序員避免尋址錯誤。)
2) 每個指針都有一個值。這個值是某個指定類型的對象的地址。特殊的 NULL(0)值代表該指針沒有指向任何地方。
3) 指針用‘&’運算符創建
4) 操作符是用來間接引用指針
5) 數組與指針緊密聯繫。一個數組的名字可以像指針變量一樣引用(但是不能修改)。如,a[3]等價於*(a+3)
數組引用和指針運算都需要用對象大小對偏移量進行伸縮。
6) 將指針從一種類型強制轉換成另一種類型,只改變它的類型,而不改變它的值。強制類型轉換的一個效果是改變指針運算的伸縮。
例如:

char *p = 'c' ;
(int *)p + 7 的結果爲 p + 28;
(int *)(p + 7) 的結果爲 p + 7。

(強制類型轉換的優先級高於加法)
7) 指針也可以指向函數。這提供了一個很強大的存儲和像代碼傳遞引用的功能,這些引用可以被程序的某個其他部分調用。
例如:

int fun(int x, int *p);
/** 聲明指針 fp ,將它賦值爲這個函數 */
int (*fp)(int, int *);
fp = fun;
/** 函數調用 */
int y = 1;
int result = fp(3, &y);

函數指針的值是該函數機器代碼表示中第一條指令的地址。

3.10.2 應用:使用 GDB 調試器

先運行OBJDUMP 來獲得程序的反彙編版本。如下命令行來啓動 GDB:
linux> gdb prog
通常的方法是在程序感興趣的地方附近設置斷點。

命令 效果
quit 退出GDB
run 運行程序(在此給出命令行參數)
kill 停止程序
break sth 在函數sth入口處設置斷點
break *0x400540 在地址0x400540處設置斷點
delete 1 刪除斷點1
delete 刪除所有斷點
stepi 執行1條指令
stepi 4 執行4條指令
nexti 類似stepi,但以函數調用爲單位
continue 繼續執行
finish 運行到當前函數返回
disas sth 反彙編函數sth
disas 0x400540 反彙編0x400540附近的函數
disas 0x400540.0x40054d 反彙編指定範圍內的代碼
print /x $rip 以十六進制輸出程序計數器(%rip)的值
print $rip 以十進制輸出程序計數器(%rip)的值
print /t $rip 以二進制輸出程序計數器(%rip)的值
print 0x100 輸出0x100的十進制
print /x 555 輸出555的十六進制
print /x ($rsp + 8) 以十六進制輸出%rsp + 8
print *(long *) 0x7fff ffff e818 輸出位於地址0x7fff ffff e818的長整數
print *(long *) (%rsp + 8) 輸出位於地址%rsp + 8的長整數
x/2g 0x7fffffffe818 檢查從地址0x7fffffffe818開始的雙字(8字節)
x/20b sth 檢查函數sth的前20個字節
info frame 有當前棧幀的信息
info registers 所有寄存器的值
help 獲取有關GDB的信息

339  GDBGDB調\blue {圖3-39} \; GDB命令示例。說明了一些GDB支持機器級程序調試的方式

3.10.3 內存越界引用和緩衝區溢出

緩衝區溢出(buffer overflow。在棧中分配某個字符數組來保存字符串,但是字符串的長度超出了爲數組分配的空間。
示例:
這裏寫圖片描述
echo對應的彙編:
這裏寫圖片描述
越界會破壞的信息:
這裏寫圖片描述
這裏寫圖片描述
緩衝區溢出的一個更加致命的使用就是讓程序執行它本來不願意執行的函數。這是一種最常見的通過計算機網絡攻擊系統安全的方法。

3.10.4 對抗緩衝區溢出攻擊

1. 棧隨機化

爲了在系統中插入攻擊代碼,攻擊者既要插入代碼,也要插入指向這段代碼的指針,這個指針也是攻擊字符串的一部分。產生這個指針需要知道這個字符串放置的棧地址。
棧隨機化的思想使得棧的位置在程序每次運行時都有變化。這類技術稱爲地址空間佈局隨機化Address-Space Layout Randomization),簡稱ASLR
通常攻擊者使用空操作雪橇(nop sled,使程序”滑過“目標序列,即在實際攻擊代碼前插入一段很長的nop(讀作“no op”no operation的縮寫)指令。
示例:
nm0,  2mn \geq m \geq 0,\; 2^m 個字節的nop sled能破解2n2^n的棧隨機化需要枚舉 2nm2^{n-m} 次。

2. 棧破壞檢測

計算機的第二道防線是能夠檢測到何時棧已經被破壞。
GCC提供一種棧保護者機制,來檢測緩衝區越界。其思想是在棧幀中任何局部緩衝區與棧狀態之間存儲一個特殊的金絲雀(canary,也稱爲哨兵值(guard value,是在程序每次運行時隨機產生的。
在這裏插入圖片描述
echo函數示例:
這裏寫圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
程序第三行通過段地址%fs:40從內存讀入金絲雀的值,保存在棧中;
程序第十一行取出該值與原地址的值作比較,不相等則棧異常。
tips:
容易越界的參數儘可能放置在棧底,以保護其他參數。
示例:
在這裏插入圖片描述
在這裏插入圖片描述
圖(b)中,參數v比數組參數buf更靠近棧頂,vbuf緩衝越界不會破壞v

3. 限制可執行代碼區域

最後一招是消除攻擊者向系統插入可執行代碼的能力。
隨機化、棧保護和限制哪部分內存可以存儲可執行代碼——是用於最小化程序緩衝區溢出攻擊漏洞三種最常見的機制。

3.10.5 支持變長棧幀

前面所講的各種函數的機器級代碼,都有一個共同點,即編譯器能夠預先確定需要爲棧幀分配多少空間。
下面示例爲局部存儲是變長的。
這裏寫圖片描述
這裏寫圖片描述
%rbp稱爲幀指針(frame pointer)有時稱爲基址幀(base pointer);
leave指令將棧幀指針恢復到它之前的值(第20行)。等價於:

movq %rbp, %rsp    ;Set stack pointer to begining of frame
popq %rbp          ;Restore saved %rbp and set stack ptr to end of caller's frame

重點習題:
在這裏插入圖片描述
答案:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章