一些概念

前言

在刷ROP题的时候涉及到将字符串写入内存的问题,学习过程中需要更加细致的了解一些概念,简单的记录一下自己的理解。

内存区域

简单的了解一下内存中的一些区域:

  • 栈(stack):由编译器自动分配及释放,存放着函数的参数值、局部变量的值等。

  • 堆(heap):一般是由程序员通过相应的函数分配和释放,若程序员不释放,程序结束可能由OS回收。

  • 全局区(静态区,static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(.data段,data segment),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bss段,bss segment)。程序结束后由系统释放。

  • 文字常量区:存放文字等不可修改的常量,由系统分配和释放。

  • 程序代码区:存放程序编译连接后生成的二进制机器码指令。

下面是一个例子了解这些区域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string.h>
using namespace std;

static int a=1; //全局初始化区
int b=2; //全局初始化区
char *p; //全局未初始化区
char *p2; //全局未初始化区
int *p3; //全局未初始化区
int *p4; //全局未初始化区
char *p5={"555555555"}; //全局初始化区

int main(){
static int c=3;
int d=4; //内存栈
int e=7; //内存栈

char *p6={"555555555"};
p=(char*)malloc(sizeof(char)*10); //内存堆
p2=(char*)malloc(sizeof(char)*10); //内存堆
p3=(int*)malloc(sizeof(int)); //内存堆
p4=(int*)malloc(sizeof(int)*10); //内存堆
for(int i=0;i<=9;i++)p4[i]=0x1;


*p3=0x123;
strcpy(p,"123456789"); //文字常量区
strcpy(p2,"987654321");
strcpy(p2,"123456789");
}

关于这些内存区域的一些说明:

  • 全局初始化区与全局(静态)初始化区是在同一段内存连续分配的,按内存地址增长方向分配

  • 全局初始化区与全局未初始化区不在同一段内存区

  • 栈空间也是同一段内存连续分配的,按内存地址减小方向分配

  • 堆空间也是同一段内存连续分配的,按内存地址增大方向分配

  • 文字常量区在自己特有的内存段内,且有机制控制字符常量不被修改(当字符串相同的时候,系统有时还会将两个指针指向同一处)

  • 在文字常量区的字符串不可以被修改,而在内存堆空间的字符串可以被修改

bss段和data段

bss段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时bss 段部分将会清零(bss段属于静态内存分配,即程序一开始就将其清零了)。比如,在C程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss段中。

text(二进制机器代码段)和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中(不占用exe的空间,只标记空间大小),由系统初始化。

看一下这样两个程序:

1
2
3
4
5
6
7
8
9
10
11
#程序1 
#include <stdio.h>

int arr[1024*1024]={0};
//int arr[1024*1024];

int main()
{
printf("hello!");
return 0;
}
1
2
3
4
5
6
7
8
9
10
#程序2
#include <stdio.h>

int arr[1024*1024]={1};

int main()
{
printf("hello!");
return 0;
}

当我们编译完这两个程序之后查看编译好的文件大小会发现程序2比程序1要大。原因在于程序1的变量位于.bss段,而程序2的变量位于.data段。

全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。

.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化,因此造成了上述情况。

还有一些点需要注意:

  • .bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小

  • .data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中

  • .data段包含经过初始化的全局变量以及它们的值

  • .bss段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含DATA和BSS段的整个区段此时通常称为数据区

总结

下面的图可以很好的理解和总结这些概念之间的关系:

文章作者: ColdSnap
文章链接: https://coldwave96.github.io/2020/06/01/concepts/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ColdSnap の Blog