前言
学习过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
}