前言
學習過linux後,知道linux程序內存結構可以分成五個區塊。分別是“代碼段”,“數據段“,”BSS段”,堆(head)和棧(stack)。他們分別的功能是
代碼段: 存放程序編譯後的機器代碼,它是一塊只讀區。
數據段: 數據段用來存放可執行文件中已初始化全局變量,換句話說就是存放程序靜態分配[1]的變量和全局變量。
BSS段[2]: BSS段包含了程序中未初始化的全局變量,在內存中 bss段全部置零。
堆(heap): 堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)
棧: 棧是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味着在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。
如下我們將使用程序的方法,更加感性的認識這些區塊。
程序代碼
代碼大體思路如下,注意,以下打印的地址,不一定就是這個區塊的首地址。
1.代碼段:就是main函數的內存地址
2.數據段:打印某個有初始化全局變量的地址
3.BSS段:打印某個未初始化的全局變量地址
4.堆:打印某個動態創建的變量的地址
5.棧:打印某局部變量的變量地址。
程序代碼如下:
#include<stdio.h>
#include<malloc.h>
#include<unistd.h>
int bss_var;
int data_var0=1;
int main(int argc,char **argv)
{
printf("below are addresses of types of process's mem\n");
printf("Text location:\n");
printf("\tAddress of main(Code Segment):%p\n",main);
printf("____________________________\n");
printf("Data Location:\n");
printf("\tAddress of data_var(Data Segment):%p\n",&data_var0);
static int data_var1=4;
printf("\tNew end of data_var(Data Segment):%p\n",&data_var1);
printf("____________________________\n");
printf("BSS Location:\n");
printf("\tAddress of bss_var:%p\n",&bss_var);
printf("____________________________\n");
int stack_var0=2;
printf("Stack Location:\n");
printf("\tInitial end of stack:%p\n",&stack_var0);
int stack_var1=3;
printf("\tnew end of stack:%p\n",&stack_var1);
printf("____________________________\n");
char *b = sbrk((ptrdiff_t)0);
printf("Heap Location:\n");
printf("\tInitial end of heap:%p\n",b);
brk(b+4);
b=sbrk((ptrdiff_t)0);
printf("\tNew end of heap:%p\n",b);
return 0;
}
程序輸出結果如下
多次運行程序,我們發現代碼段,數據段,BSS段都的地址都是固定好的,但是堆和棧的地址是動態變化的。該怎麼解釋呢?
在linux中,用戶程序使用的是虛擬地址。也就是說,也就是說程序的運行之前,很多地址已經是確定好的。按照道理堆棧段都是固定的,但是因爲系統開啓了安全機制,程序在運行的時候,把堆棧的起始地址隨機分配了,這樣可以增加緩衝區溢出攻擊的難度。
原理
操作系統分配進程棧的起始地址:操作系統給進程棧分配的起始地址爲STACK_TOP,這個值一般也爲TASK_SIZE,即爲用戶空間的最大地址(在32位系統中,該值爲3G),但是如果系統設置PF_RANDOMIZE標誌,則操作系統會在進程啓動後,隨機設置棧的起始位置,即爲:STACK_TOP - randomized_variable,所以每次看到main函數局部變量地址是不一樣的
STACK_TOP的代碼如下
代碼:在內核2.6.32中,對應的方法:
static unsignedlong randomize_stack_top(unsigned long stack_top)
{
unsignedint random_variable = 0;
if((current->flags & PF_RANDOMIZE) &&
!(current->personality & ADDR_NO_RANDOMIZE)) {
random_variable = get_random_int() & STACK_RND_MASK;
random_variable <<= PAGE_SHIFT;
}
#ifdef CONFIG_STACK_GROWSUP
returnPAGE_ALIGN(stack_top) + random_variable;
#else
returnPAGE_ALIGN(stack_top) - random_variable;
#endif
}