0x01 空表的学习
实验环境:winxpsp3中文版。vc++6.0 release版本。调试器:OD
示例代码:
#include <windows.h>
int main()
{
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);
// free block and prevent coaleses
HeapFree(hp, 0, h1); // free to freelist[2]
HeapFree(hp, 0, h3); // free to freelist[2]
HeapFree(hp, 0, h5); // free to freelist[4]
HeapFree(hp, 0, h4); // coalese h3, h4, h5, link the large block to freelist[8]
return 0;
}
调试过程
一、堆分配:
采用heapcreate创建一个干净的堆,对堆进行学习。
hp = HeapCreate(0, 0x1000, 0x10000)
堆创建后,返回的堆地址存放在EAX中。
位于堆偏移0x0688处开始进行真正的堆分配。(如果一开始选择创建可扩展堆,即HeapCreate(0,0,0)创建时,此处为块表所在地址。指向块表的指针位于0x584字节处。本实验里,块表指针为0.)
未进行分配前,freelist[0]指向了尾块,其他的freelist指向自己。
尾块的块首指向freelist[0] 。尾块大小为0130,计算单位为8个字节,也就是0130(16进制)->304(10进制)。304*8 = 2432(10进制) -> 980(16进制)
分配h1时
分配完h6时
二、堆释放
释放h1,h3,h5时
三、堆块的合并
释放h4时,由于h4和h3、h5相邻,故会发生堆块合并,合并后的堆块进行重新计算,链入对应的空表。
0x02 块表的学习
实验环境:winxpsp3简体中文版,vc++6.0 release版
示例代码
#include <stdio.h>
#include <windows.h>
void main()
{
HLOCAL h1, h2, h3, h4;
HANDLE hp;
hp = HeapCreate(0, 0, 0);
__asm int 3
h1 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 8);
h2 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 8);
h3 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 16);
H4 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 24);
HeapFree(hp, 0, h1);
HeapFree(hp, 0, h2);
HeapFree(hp, 0, h3);
HeapFree(hp, 0, h4);
h2 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 16);
HeapFree(hp, 0, h2);
}
调试跟踪
1.查看HeapCreate创建的堆
2.进行h1、 h2、h3、h4分配后的情况
3.对h1进行释放
块表的情况
4.对h2释放的情况
5.对h3释放的情况
6.对h4释放的情况
小结:
7.对 h2进行分配,由下图可以看出,从块表上进行分配了
8.对h2再进行释放
总结
从上面的实验可以看出,链入块表中的堆块,其标识位为0x01,即busy状态,这也是块表中的堆块不进行合并的原因。且块首只存指向下一堆块的指针,不存指向前一堆块的指针。