博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
堆入门的必备基础知识
阅读量:7010 次
发布时间:2019-06-28

本文共 3446 字,大约阅读时间需要 11 分钟。

i春秋作家:W1ngs

原文来自:

前言

堆的利用相对于栈溢出和格式化字符串会复杂很多,这里对堆的一些基本知识点和实现原理进行了一些小小的总结,写的如有不当恳请大佬们斧正。

堆的实现原理

对堆操作的是由堆管理器来实现的,而不是操作系统内核。因为程序每次申请或者释放堆时都需要进行系统调用,系统调用的开销巨大,当频繁进行堆操作时,就会严重影响程序的性能

例如 glibc 中使用了 ptmalloc2 作为堆管理器:

目前 Linux 标准发行版中使用的堆分配器是 glibc 中的堆分配器:ptmalloc2。ptmalloc2 主要是通过 malloc/free 函数来分配和释放内存块。

程序向系统申请堆空间的时候相当于一种 "批发" 和 "零售" 的关系:

堆管理器就像一个中间商,将向内核申请到空间根据分配算法来把空间真正的分配给程序。

这里为了理解简单画了一张图,如果有错误的话敬请指正。

0x00 创建、释放堆的函数

malloc、realloc、calloc

malloc 函数:

#include 
void *malloc(size_t size);
  • 在使用malloc的时候要进行强制类型转换为指针类型

malloc函数申请地址成功后返回一个指针,指向大小为至少size字节的内存块

  • 当size = 0 时,返回当前系统允许的堆的最小内存块
    即malloc(0) 在32位系统下会分配 8 个字节的空间,在 64 位系统下会分配 16 字节的空间

malloc会使用 mmap 来创建独立的匿名映射段,malloc 的背后是用 brk 函数来实现内存地址申请的。

查看方法:cat /proc/PID/maps

使用 malloc() 申请的内存,释放后,仍然归还回原处,再次申请同样大小的内存区时,还是从第 1 次那里获得

每次申请会获取比申请到更大的值,这样的话,就避免了多次内核态与用户态的切换,提高了程序的效率

分配器视堆为一组不同大小的块(chunk)的集合。每个块就是一个连续的虚拟内存片。

free 函数:

#include 
void free(void *ptr);

free 函数会释放由 p 所指向的内存块,这个内存块可以是通过malloc或者readlloc函数分配的块。

  • 当 p 为空指针时,函数不执行任何操作,当释放过 p 内存块再次释放后,会产生错误(double free)。

当一个堆块释放了(通过调用free函数),它会检查之前的堆块是否被释放了。如果之前的堆块没有在使用,那么就会和当前的堆块合并。

unlink 的源码:

/* Take a chunk off a bin list */void unlink(malloc_chunk *P, malloc_chunk *BK, malloc_chunk *FD){    FD = P->fd;    BK = P->bk; FD->bk = BK; BK->fd = FD; }

chunk 合并的过程与双向链表删除节点的过程相同:

其实这块论坛里有一篇关于 unlink 函数的利用这一块讲的很清楚了,可以参考他的文章:

0x01 内存分配有关的函数

brksbrk

对于每个堆,变量brk指向堆的顶部,不过有下面的两个前提

  • 不开启 ASLR 保护时,brk 会指向 data/bss 段的结尾。
  • 开启 ASLR 保护时,brk 也会指向同一位置,只是这个位置是在 data/bss 段结尾后的随机偏移处。

brk和sbrk主要的工作是实现虚拟内存到内存的映射

  • 当 sbrk() 中的参数为 0 时,我们可以找到 program break 的位置。 也就是 sbrk(0) 时,指针指向的就是program break,也就是堆顶

  • brk 函数和 sbrk 函数通常都配合使用,如下示例:

示例:

1.sbrk(0) -- >  初始化堆,将 start_brk 以及堆的当前末尾 brk 指向同一地址

在执行下面的两条语句之后,使用 cat /proc/PID/maps 会发现没有堆空间

tmp_brk = curr_brk = sbrk(0);printf("Program Break Location1:%p\n", curr_brk);

2.brk(curr_brk+4096) -- > 重新定义堆顶的指针

brk(curr_brk+4096);curr_brk = sbrk(0); printf("Program break Location2:%p\n", curr_brk);

3.此时再调用 sbrk(0),堆顶指针就会变化

brk(tmp_brk);curr_brk = sbrk(0); printf("Program Break Location3:%p\n", curr_brk);

0x02 mmap、munmap函数

mmap函数要求内核创建一个新的虚拟内存区域

map函数原型:

void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offset)

prot参数指定虚拟内存区域的访问权限,有以下几个

PROT_EXEC    //这个区域由可以被CPU执行的指令组成PROT_READ    //可读PROT_WRITE   //可写PROT_NONE    //不可访问

flags参数描述被映射对象类型的位组成,有以下几个

MAP_ANON或者MAP_ANONYMOUS    //表示被映射的对象是一个匿名对象,相应的虚拟页面是请求二进制零的MAP_PRIVATE                 //对象属性为私有、写时复制的MAP_SHARED                  //表示共享对象
  • 对于大于 128 KB 的堆申请请求来说,根据分配算法会使用 mmap 函数为她分配一块匿名空间,在这个匿名空间里为用户分配空间。

eg.申请132KB的虚拟内存区域

addr = mmap(NULL, (size_t)132*1024, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

mmap 函数与 brk 函数的区别

对于小于 128 KB 的请求来说,会在现有的空间中按照堆分配算法(brk、sbrk)为它分配一个堆空间,大于 128 kB 时,就使用 mmap 函数分配一个匿名空间给用户使用。

简单来说就是两个区别:

1.一个是在现有的堆空间中分配,一个是在设置了 MAP_ANONYMOUS 属性的匿名空间中分配。

2.一个是用于申请小空间时使用,一个是在用于申请大空间时使用


mmap 函数的另一种用法

在栈溢出的利用时,若 system 和 execve 函数都被禁用的时候,我们可以使用 mmap 或者 mprotect 函数将 bss 段的内存权限设置为可执行,这样我们再把 shellcode 写入到里面,接着将 eip 执行他,就可以达到直接执行 shellcode 的效果。

例如,jarvisoj 的 level5:

WriteUp的链接如下,里面讲到了详细的用法和参数设置。

munmap函数原型:

int munmap(void *start,size_t length)

eg.删除已经创建的虚拟内存区域

ret = munmap(addr, (size_t)132*1024);

分配的过程:

0x03 堆的使用场景

  1. new 一个新对象
  2. 传参为数组时

0x03 Bin

fast binssmall binslarge bins unsorted bin

fast bins

用于一些较小的 chunk 释放之后发现存在与之相邻的空闲的 chunk 并将它们进行合并

typedef struct malloc_chunk *mfastbinptr;/*    This is in malloc_state.    /* Fastbins */    mfastbinptr fastbinsY[ NFASTBINS ];*/

参考文章

大家有任何问题可以提问,更多文章可到阅读哟~

转载地址:http://rsttl.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
利用clonezilla克隆、还原CentOS整个系统
查看>>
解决127.0.0.1 localhost 劫持问题
查看>>
关于硬盘的一切!
查看>>
人工智能落地之路:从概念验证到产品
查看>>
winscp连接虚拟机Linux被拒绝的问题解决方案
查看>>
教程-Delphi设置功能表
查看>>
node常用库或中间件
查看>>
关系运算图模板
查看>>
Java中的多线程,线程池
查看>>
发布系统背景和saltstack的基本操作
查看>>
软件下载站
查看>>
appium - 连接设备
查看>>
C#获取一个文件的相关信息
查看>>
linux驱动系列之文件压缩解压小节(转)
查看>>
POJ 1180 斜率优化DP(单调队列)
查看>>
Zend Studio 12 生成 WSDL
查看>>
重新学struct,边界对齐,声明……与Union的区别
查看>>
Centos6.8防火墙配置
查看>>
JAVA学习心得
查看>>