CTF 竞赛入门指南(CTF All In One)学习(九)

内存分布

       32 位系统有 4GB 的地址空间,其中 0x08048000 ~ 0xbfffffff 是用户空间(3GB),0xc0000000 ~ 0xffffffff 是内核空间(1GB)。(0x00000000 ~ 0x08048000 有 128M。为什么是0x00000000而不是0x00000000为起始?

在这里插入图片描述

栈帧

       栈帧(Stack Frame,也称堆栈帧)是在堆栈中为未运行完的函数分配的内存区域,用于保存调用这些函数所需维护的信息。
       在 x86 体系中,寄存器ebp指向堆栈帧的底部,esp指向堆栈帧的顶部。栈由高地址向低地址生长。
       在 Linux 环境下,ELF 文件的入口其实是_start而非main()

  • 堆是除了存放栈中东西之外的所有其他内存区域,由动态内存分配器负责维护。
  • 堆由低地址向高地址生长(这是由于系统用链表来存储的空闲内存地址,而链表的遍历方向是由低地址向高地址。),是不连续的内存区域。分配器将堆视为一组大小不同的块(block)的集合来维护,每个块就是一个连续的虚拟内存片(chunk)。堆的大小受限于计算机系统中有效的虚拟内存。因此,堆获得的空间比较灵活,也比较大。
  • 如果每次申请内存时都直接使用系统调用,会严重影响程序的性能。通常情况下,运行库先向操作系统“批发”一块较大的堆空间,然后 “零售” 给程序使用。当全部 “售完” 之后或者剩余空间不能满足程序的需求时,再根据情况向操作系统“进货”。当使用malloc()free()时就是在操作堆中的内存。对于堆来说,由于释放工作由程序员控制,容易产生内存泄露。

在这里插入图片描述

进程堆管理

       Linux 提供了 2 种堆空间分配的方式,一个是系统调用brk(),另一个是系统调用mmap()。可以使用 man brkman mmap查看。

       brk()的声明如下:

    #include <unistd.h>
    int brk(void *addr);
    void *sbrk(intptr_t increment);

       参数*addr是进程数据段的结束地址,brk()通过改变该地址来改变数据段的大小,当结束地址向高地址移动,进程内存空间增大,当结束地址向低地址移动,进程内存空间减小。brk()调用成功时返回0,失败时返回-1sbrk()brk()类似,但是参数increment表示增量,即增加或减少的空间大小,调用成功时返回增加后减小前数据段的结束地址,失败时返回-1
       在上图中可以看到brk指示堆结束地址,start_brk指示堆开始地址。BSS segmentheap之间有一段Random brk offset,这是由于ASLR的作用,如果关闭了ASLR,则Random brk offset0,堆起始地址和数据段结束地址重合。

       mmap()的声明如下:

    #include <sys/mman.h>
    void *mmap(void *addr, size_t len, int prot, int flags,
        int fildes, off_t off);

       mmap()函数用于创建新的虚拟内存区域,并将对象映射到这些区域中,当它不将地址空间映射到某个文件时,我们称这块空间为匿名(Anonymous)空间,匿名空间可以用来作为堆空间。mmap()函数要求内核创建一个从地址addr开始的新虚拟内存区域,并将文件描述符fildes指定的对象的一个连续的片(chunk)映射到这个新区域。连续的对象片大小为len字节,从距文件开始处偏移量为off字节的地方开始。prot描述虚拟内存区域的访问权限位,flags描述被映射对象类型的位组成。

       munmap()则用于删除虚拟内存区域,其声明如下:

    #include <sys/mman.h>
    int munmap(void *addr, size_t len);

       通常情况下,我们不会直接使用brk()mmap()来分配堆空间,C 标准库提供了一个叫做malloc的分配器,程序通过调用malloc()函数来从堆中分配块。

猜你喜欢

转载自blog.csdn.net/m_pNext/article/details/107382855
ctf