这篇文章详细的描述了堆,并且会教你如何编写基于堆溢出漏洞的利用。运行下面的程序:#include#include#includeint main(int argc, char *argv[]){char *buf1 = malloc(128);char *buf2 = malloc(256);read(fileno(stdin), buf1, 200);free(buf2);free(buf1);}在第 10 行中,存在一个明显的可利用的溢出漏洞。那么首先我们就来了解下系统是怎样管理堆的 。0*01 基本堆和堆块的布局每个程序分配的内存(这里指的是 malloc 函数)在内部被一个叫做 ” 堆块 ” 的所替代。
一个堆块是由元数据和程序返回的内存组成的(实际上内存是 malloc 的返回值)。所有的这些堆块都是保存在堆上,这块内存区域在申请新的内存时会不断的扩大。同样,当一定数量的内存释放时,堆可以收缩。在 glibc 源码中定义的堆块如下:struct malloc_chunk {INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */struct malloc_chunk* fd; /* double links -- used only if free. */struct malloc_chunk* bk;/* Only used for large blocks: pointer to next larger size. */struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */struct malloc_chunk* bk_nextsize;};假设内存中没有堆块释放,新分配的内存区域紧随之前申请的堆块后。
因此如果一个程序依次调用malloc(256),malloc(512),以及malloc(1024),内存布局如下:Meta-data of chunk created by malloc(256)The 256 bytes of memory return by malloc—————————————–Meta-data of chunk created by malloc(512)The 512 bytes of memory return by malloc—————————————–Meta-data of chunk created by malloc(1024)The 1024 bytes of memory return by malloc—————————————–Meta-data of the top chunk在堆块之间的”—”是虚拟的边界,实际当中他们是彼此相邻的。
你可能会问,为何我要在布局当中包含一个”顶块”元数据。顶级块表示堆中可利用的内存,而且是唯一的可以大小可以生长的堆块。当申请新的内存时,顶块分成两个部分:第一个部分变成所申请的堆块,第二个部分变为新的顶块(因此顶块大小可以收缩)。如果顶块不能够满足申请的内存区域大小,程序就会要求操作系统扩大顶块大侠(让堆继续生长)。