8.1 數組和指針類型的分配和訪問
1. 數組的分配和訪問
(1)數組元素在內存的存放和訪問
- 定義一個具有4個元素的
靜態存儲型
short 數據類型數組A,可以寫成“static short A[4];” - 第i(0≤i≤3)個元素的地址計算公式爲**
&A[0]+2*i
**; - 假定數組A的首地址存放在EDX中,i 存放在ECX中, 現要將A[i]取到AX中,則所用的彙編指令是:
movw (%edx, %ecx, 2), %ax
(比例因子是2)其中,ECX爲變址(索引)寄存器,在循環體中增量 - 數組元素的地址
數組定義 | 數組名 | 數組元素類型 | 數組元素 大小(B) |
數組大小(B) | 起始地址 | 元素i的地址 |
---|---|---|---|---|---|---|
char S[10] | S | char | 1 | 10 | ||
char SA[10] | SA | char* | 4 | 40 | ||
double D[10] | D | double | 8 | 180 | ||
double SA[10] | SA | double* | 4 | 40 |
(2)數組的初始化和訪問
- 分配在
靜態區
的數組的初始化和訪問
auto型
數組的初始化和訪問
2. 數組與指針
(1)數組與指針的類型
- 假定數組A的首址SA在ECX中,i 在EDX中,表達式結果在EAX中
表達式 | 類型 | 值的計算方式 | 彙編代碼 |
---|---|---|---|
A | int * | SA | leal (%ecx) , %eax |
A[0] | int | M[ SA ] | movl (%ecx) , %eax |
A[i] | int | M[ SA + 4 * i ] | movl (%ecx , %edx , 4) , %eax |
&A[3] | int * | SA + 12 | leal 12 (%ecx) , %eax |
&A[i] - A | int | (SA + 4 * i - SA) \ 4=i | movl %edx , %eax |
*(A + i) | int | M[ SA + 4 * i ] | movl (%ecx , %edx,4) , %eax |
&A[0] + i - 1 | int | M[ SA + 4 * i - 4 ] | movl -4 (%ecx , %dex , 4) , %eax |
A + i | int * | SA + 4 * i | leal (%ecx , %edx , 4) , %eax |
(2)指針數組和多維數組
- 由若干指向同類目標的指針變量組成的數組稱爲指針數組。
- 其定義的一般形式如下: 存儲類型數據類型*指針數組名[元素個數];
–例如,“int *a[10];”定義了一個指針數組a,它有10個元 素,每個元素都是一個指向int型數據的指針。 一個指針數組可以實現一個二維數組。
8.2 結構和聯合數據類型的分配和訪問
1. 結構體的分配與訪問
(1)結構體成員在內存的存放和訪問
(2)結構體數據作爲入口參數
- 當結構體變量需要作爲一個函數的形參時,形參和調用函數中的實參應具有相同結構
- 有按值傳遞和按地址傳遞兩種方式
- 若採用按值傳遞,則結構成員都要複製到棧中參數區,這既增加時間開銷又增加空間開銷,且更新後的數據無法在調用過程使用
- 通常應按地址傳遞,即:在執行CALL指令前,僅需傳遞指向結構體的指針而不需複製每個成員到棧中
2. 聯合體數據的分配和訪問
聯合體各成員共享存儲空間,按
最大長度成員
所需空間大小爲目標
- 通常用於特殊場合,如,當事先知道某種數據結構中的不同 字段的使用時間是互斥的,就可將這些字段聲明爲聯合,以減少空間。
- 但有時會得不償失,可能只會減少少量空間卻大大增加處理複雜性。
- 還可實現對相同位序列進行不同數據類型的解釋
(1)對相同位序列進行不同數據類型的解釋
(2)IA-32寄存器組織的模擬
typedef struct{
union{
struct {
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
};
union{
uint32_t _32;
uint16_t _16;
uint8_t _8[2];
} gpr[8];
};
} CPU_state;
extern CPU_state cpu;
enum { R_EAX, R_ECX, R_EDX, R_EBX, R_ESP, R_EBP, R_ESI, R_EDI };
enum { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI };
enum { R_AL, R_CL, R_DL, R_BL, R_AH, R_CH, R_DH, R_BH };
#define reg_l(index) (cpu.gpr[index]._32)
#define reg_w(index) (cpu.gpr[index]._16)
#define reg_b(index) (cpu.gpr[index & 0x3]._8[index >> 2])
(3)利用嵌套可定義鏈表結構
8.3 數據的對齊存放
Alignment: 要求數據的地址是相應的邊界地址
- 目前機器字長爲32位或64位,主存按一個
傳送單位(32/64/128位)
進行存取,而按字節編址,例如:若傳送單位爲64位,則每次最多讀寫64位 ,即:第0~ 7字節同時讀寫,第8~ 15字節同時讀寫,……,以此類推。按邊界對齊,可使讀寫數據位於8i~8i+7(i=0,1,2,…) 單元- 指令系統支持對字節、半字、字及雙字的運算
- 各種不同長度的數據存放時,有兩種處理方式:
– 按邊界對齊(若一個字爲32位)
•字地址:4的倍數(低兩位爲0)
•半字地址:2的倍數(低位爲0)
•字節地址:任意
– 不按邊界對齊 壞處:可能會增加訪存次數!
1. 對齊(Alignment)
(1)數據的對齊
(2)對齊策略
(3)對齊(Alignment)舉例
2. 對齊方式
(1)對齊方式的設定
缺省或#pragma pack() | 按自然邊界對齊 |
|
---|---|---|
#pragma pack(n) | 結構體、類內部的成員變量 | 當自然邊界(如int型按4字節、short型按2字節、float按4字節 )比n大時,按n字節對齊 。 |
attribute((aligned(m))) | 結構體、類、聯合體、一個單獨的變量(對象) | 按m字節對齊 (m必須是2的冪次方),其佔用空間大小也是m的整數倍 ,以保證在申請連續存儲空間時各元素也按m字節對齊。 |
attribute((packed)) | 不按邊界對齊,稱爲緊湊方式 。 |
(2)對齊方式舉例
6.4 越界訪問和緩衝區溢出攻擊
C語言程序中對數組的訪問可能會有意或無意地超越數組存儲區範圍而無法發現。
- 數組存儲區可看成是一個緩衝區,超越數組存儲區範圍的寫入操作 稱爲緩衝區溢出。
– 例如,對於一個有10個元素的char型數組,其定義的緩衝區有10個字節。若寫一個字符串到這個緩衝區,那麼只要寫入的字符串多於9個字符(結束符‘\0’佔一個字節),就會發生“寫溢出”.- 緩衝區溢出是一種非常普遍、非常危險的漏洞,在各種操作系統、 應用軟件中廣泛存在。
- 緩衝區溢出攻擊是利用緩衝區溢出漏洞所進行的攻擊。利用緩衝區溢出攻擊,可導致程序運行失敗、系統關機、重新啓動等後果。
1. 越界訪問和緩衝區溢出
2. 程序的加載和運行
UNIX/Linux系統中,可通過調用execve()函數來加載並執行程序。
(1)execve()函數的用法
int execve(char *filename, char *argv[], *envp[]);
filename: 加載並運行的可執行文件名(如./hello),
argv: 可帶參數列表
envp: 環境變量列表
若錯誤(如找不到指定文件filename),則返回-1,並將控制權交給調用程序;
若函數執行成功,則不返回 ,最終將控制權傳遞到可執行目標中的主函數main
(2) 主函數main()的原型形式如下:
int main(int argc, char **argv, char **envp);
//或者:
int main(int argc, char *argv[], char *envp[]);
&emsp**;argc:** 指定參數列表長度,參數列表中開始是**命令名(可執行文件名)
** ,最後**以NULL結尾
** 。
3. 緩衝區溢出攻擊
code中的“0123456789ABCDEF"字符覆蓋棧中的buffer;
"XXXX"字符串破壞EBP的舊址;
"\x11\x84\x04\x08"爲預設的hacker()函數首地址,覆蓋原有的返回值地址,使得執行完outputs後返回到hacker中執行。