美文网首页
堆——继续分析

堆——继续分析

作者: 萍水间人 | 来源:发表于2019-06-05 23:16 被阅读0次

Windows下的堆机制

参考0day安全那本书

0x01 堆块分配

堆块分配可以分成三类,块表分配,普通空表分配,和零号空表分配

从快表中分配
寻找到大小匹配的空闲堆块,将其状态修改为占用态,把它从堆表中卸下,最后返回一个指向堆块块身的指针给程序用

普通空表分配
首先寻找到最优的空闲块分配,若失败,则寻找次优的空闲块分配,即最小能满足的空闲快

零号空表分配
零号空表按照升序大小链着大小不同的空闲块,所以在分配的时候先从free[0]反向查找最后一个块,也就是先找到表中最大的一个块,如果这个块都不能满足,那就不用找了,如果能满足要求,再正向搜索最小能够满足要求的空闲堆块进行分配(所以零号空表中要按照升序排列)

特殊的找零钱现象:
当空表中无法满足要求时,一个稍大的块会被用于分配,这种次优分配发生时,会先从大块中按请求的大小精确地分割出一个块进行分配,然后将剩余的部分重新标注块首,链如空表。

0x2 认识数据结构

1. 空表
空闲堆块的大小 = 索引项*8字节

如下是空表的数据结构:


这里面只有free[0]最特殊,其余的都还行

2. 快表

快表的数据结构

使用单链表来实现的
这是Windows为了加速堆分配而采用的一种数据结构

因为其中的堆块是不会合并的

并且每条快表中最多只有4个节点,因此快表很容易被填满

0x3 堆块合并

暂空


几个要点

  1. 快表被设置为占用态,所以其内的堆块是不会发生合并的
  2. 快表只有精确匹配,不存在找零钱
  3. 快表是单链表,操作更简单,堆安全中也不太常见
  4. 快表很快,所以在分配和释放的时候都是优先使用快表,失败是才使用空表
  5. 快表只有四项,所以很容易被填满,因此空表也是被频繁使用的

尝试调试:

代码如下:

#include <windows.h>

int main(int argc, char const *argv[])
{
    HLOCAL h1,h2,h3,h4,h5,h6;
    HANDLE hp;
    hp = HeapCreate(0,0x1000,0x10000);
    __asm int 3; 
    h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);
    h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,5);
    h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,6);
    h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,19);
    h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);
    HeapFree(hp,0,h1);
    HeapFree(hp,0,h3); 
    HeapFree(hp,0,h5); 
    HeapFree(hp,0,h4); 

    return 0;
}

命中断点
此时


此时我们看到


只有hp有一个值,0x003a0000
这是在调用了函数HeapCreate之后得到的一个值,尝试在内存中去查看(因为我不确定是不是指针)

当HeapCreate()函数成功地创建了堆区之后,会把整个堆区的起始地址都返回给EAX,在这里是0x003a0000

内存布局

单步运行一次之后

h1此时的值为:0x003a0688
和我们课堂上分析的一样!

此时的内存:
VC++很人性化的标记出了被修改的值


为什么是0x003A0028这一处的值被修改了呢?

先看下0x003a0688处的值:

重点是这两个值:


偏移0x178处是空白索引区

当一个堆刚刚被初始化的时候,它的堆块状况是比较简单的

  1. 只有一个空闲态的大块,这个块被称作“尾块”
  2. 位于堆偏移0x0688处(启用快表之后这个位置将是快表)
  3. Free[0]指向尾块
  4. 除0号空白哦外,其余各项索引都指向自己

而这个时候,h1指向的就是偏移0x0178处的空白索引区

我们在这里可以看到好多指针,都指向它自己
同时也看到了指向0x003a06a8的指针

继续单步

h2得到值


此时0x003a0178
两个值被修改了


因为空白索引区此时要指向新开的区域

此时0x003a06a8

这时候挺神奇的,分配了一个h2,h2的两个指针指向了空白索引区
但是h1的两个指针就没了

还有一个问题,这里堆块分配的大小似乎不一样?
每一个堆块是占用了32个字节。。

再单步

空白索引区的指针继续被修改

空白索引区 此时h3已经被分配了内存

依旧可以看到一对指向空白索引区的指针


h3指向的内存 h4被分配内存 h3指向的内存区域发生了很大的变化 h5被分配内存 依旧是h3指向的区域发生了很大变化,还有EAX保存了返回的值 h6被分配内存 连续分配6次内存之后的空白索引区

释放掉h1


h1被释放,但是h1的内容不变 空白索引区的指针发生变化

此时查看0x003a0688处的内存

再释放掉h3之后发生的变化


释放了h3

我们看到0x003a0688也就是原来的h1现在指向了0x003a06c8也就是h3
0x003a06c8指向了0x003a01980x003a0688
也就是说
h3的flink指向了0x003a0198,blink指向了h1
h1的flink指向了h3,blink指向了0x003a0198

此时0x003a0198处的指针也正好指向了h1和h3

0x003a0198处的内存

再释放掉h5

此时h1,h3之前的关系都没有变

查看h5的内存发现其指向了0x003a01a8

查看h5的内存

此时就可以猜到了


0x003a01a8处的内存

0x003a01a8应该是free[8]了

接下来应该是重点了

释放掉h4


0x003a01a8处的内存

不太会分析
看一下0x003a06c8此时由 0x003a01e8指向
0x003a01e8应该是free[13]了

原来的h1此时指向了0x003a0198

h1指向了0x003a0198

原来的h3也指向了0x003a01e8


h3指向了0x003a01e8

所以这个时候发生了堆块的合并
但是是怎么合并的呢?

因为h3,h4.h5都相邻,所以才会发生堆块合并
但是我还没想清楚每个堆块的大小。。

相关文章

  • 堆——继续分析

    Windows下的堆机制 参考0day安全那本书 0x01 堆块分配 堆块分配可以分成三类,块表分配,普通空表分配...

  • 如何确定JVM堆中哪些对象是可以被回收的

    垃圾回收器在对堆进行回收之前,需要确定堆中哪些对象是可以继续存活的,哪些是可以被回收的,Java采用可达性分析算法...

  • 使用MAT解析OOM问题

    对于排查OOM问题、分析程序堆内存使用情况,最好的方式就是分析堆转储。 堆转储,包含了堆现场全貌和线程栈信息(Ja...

  • 堆糖产品分析

    一、产品概况 1.1产品体验环境 体验机型:vivo x7 APP版本:v.7.0.1 1.2产品简介 堆糖定...

  • Windows堆的分析

    又把0day安全翻了一下收获了很多,还是关于堆的知识源代码: 堆的起始地址为3a0000偏移3a0178处为fre...

  • 堆,栈内存分析

    这是一个数据流,将李白这个对象写入到文件中。 整个抄写下来也是懵的,不知道是啥意思,为什么是这样写的。然后也是找了...

  • 7. heaptrack

    linux的堆内堆内存分析器 1. github heaptrack

  • 【性能优化】Java 堆外内存的实战分析

    本文在 Kubernetes 部署场景下,分析容器内 Java进程的堆外内存 1.堆外内存的分析思路 采集 JVM...

  • 2018.7.20 继续分析

  • 【Spark】Spark 存储原理--存储层分析

    本篇结构: 缓存分析 存储级别 如何选择存储级别 堆内和堆外内存规划 内存空间分配 参考博文 一、缓存分析 RDD...

网友评论

      本文标题:堆——继续分析

      本文链接:https://www.haomeiwen.com/subject/mlqexctx.html