经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
堆入门的必备基础知识
来源:cnblogs  作者:i春秋学院  时间:2018/10/15 9:18:55  对本文有异议

i春秋作家:W1ngs

原文来自:堆入门的必备基础知识

前言

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

堆的实现原理

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

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

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

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

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

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

0x00 创建、释放堆的函数

  1. mallocrealloccalloc

malloc 函数:

  1. #include <stdlib.h>
  2. 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 函数:

  1. #include <stdlib.h>
  2. void free(void *ptr);

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

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

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

unlink 的源码:

  1. /* Take a chunk off a bin list */
  2. void unlink(malloc_chunk *P, malloc_chunk *BK, malloc_chunk *FD)
  3. {
  4.     FD = P->fd;
  5.     BK = P->bk;
  6.     FD->bk = BK;
  7.     BK->fd = FD;
  8. }

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

其实这块论坛里有一篇关于 unlink 函数的利用这一块讲的很清楚了,可以参考他的文章:
https://bbs.ichunqiu.com/thread-46614-1-1.html

0x01 内存分配有关的函数

  1. brk
  2. sbrk

对于每个堆,变量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 会发现没有堆空间

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

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

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

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

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

0x02 mmap、munmap函数

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

map函数原型:

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

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

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

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

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

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

  1. 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的链接如下,里面讲到了详细的用法和参数设置。

https://blog.csdn.net/zszcr/article/details/79703642

munmap函数原型:

  1. int munmap(void *start,size_t length)

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

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

分配的过程:

0x03 堆的使用场景

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

0x03 Bin

  1. fast bins
  2. small bins
  3. large bins
  4. unsorted bin

fast bins

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

  1. typedef struct malloc_chunk *mfastbinptr;
  2. /*
  3.     This is in malloc_state.
  4.     /* Fastbins */
  5.     mfastbinptr fastbinsY[ NFASTBINS ];
  6. */

参考文章

Libc堆管理机制及漏洞利用技术 (一)

Heap overflow using unlink

ctf-wiki

浅析Linux堆溢出之fastbin

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号