-
-
[原创]堆溢出基础
-
发表于:
2021-2-15 11:35
11217
-
“堆”是什么呢?我相信网络当中有大量的关于堆的定义,那么我仅仅谈谈我个人对“堆”的理解。其实本质上来说,它也是一个虚拟的内存空间,它和栈一样是特殊利用的内存空间。所以,堆同样有“读”,“写”,“释放”等操作。那么它相对于我们熟悉的栈又区别在哪里呢?我们知道栈在使用结束后会自动回收且在使用的时候会自动分配空间。“堆”就不一样,它需要程序员自己申请并且在使用结束后由程序员自己的意愿释放“堆”空间。
首先,我们先在大脑中有个基本的对“堆”的理解。这里,我采用一幅图来帮大家将“堆”的逻辑概念拉到类似“物理概念”。如下图:
我们只需要暂时关注中间红色框框标记的部分,由图可知,堆的结构是由“堆表”以及“堆块”构成这,其中“堆表”主要作用是用来索引堆块的位置。其中,堆表主要是有两种:空闲双向链表(Freelist),快速单向链表(Lookaside)(注意:堆表仅仅是用来索引空闲态的堆块,即未被使用的堆块)“堆块”就是用来提供程序员申请堆空间的。
堆块的状态我们可以大致分为“使用状态”和“未使用状态”,即该堆块有没有被申请使用,如果没有则链入堆表中。下面我们来了解一下堆块的结构。
我们同样用一张图来说明一下使用状态下的堆块。主要是“块首”+“块身”组成。详细如下图:
未使用状态下堆结构大体是和使用状态下的堆一样,区别在于“块首”添加了8字节的指针对。该指针对就是用来链路堆表当中的。详细如下图:
空闲双向链表有“段表索引”,“虚表索引”,“空表使用标识”,“空表索引区”组成。我们需要特别了解的是“空表索引区”这个内容。“空表索引区”由128项指针数组组成。这对指针用来将空闲堆组织成双向链表。根据堆块的大小不同存放的指针数组也是不同。索引由“0”编号开始,即128项的标号为free[0]~free[127]。当中free[1](第二项)链接8字节的堆块,free[2](第三项)链接16字节的堆块。即每项链接的堆块大小均比其前一项链接的堆块增大8字节。值得注意的是free[0]链接的是大于等于1024字节的堆块。详细如下图展现:
从上图我们可以看到,每一项“free”后面均链接着一个堆块,每两个空闲堆块均相互链接,直到最后堆块又链接着最初的“free”,形成了一个类似循环的结构。接下来,我们用一个实例来看看,这样一张表是如何存储在物理内存当中的。
1;案例程序
2;实验环境
测试环境
操作系统
Window2000
编译器
Visual C++ 6.0
Build版本
release版本
3;验证流程
3.1;打开运行程序,当代码执行到‘__asm int 3’引发中断,这个时候我们在OD当中打开程序。这个时候,我们发现程序停在断点“CC”位置。
3.2;查看堆表空表索引区
我们来回顾代码,在函数HeapCreate()执行完成之后,即在成功创建堆之后会将堆区的指针存放到寄存器EAX当中,这个时候,我们来查看一下对应寄存器即可找到堆的起始位置。
由上图我们知道了堆的起始位置,我们知道堆的结构是由堆表+堆块组成的,那么地址0X00520000则可以说是堆表的其实位置。接下来,我们在内存当中找到对应位置:
我们主要是需要关注“空表索引区”该区的偏移量为0x178。则空表索引区的地址为:0x00520178。回顾一下空表索引区的结构,我们知道第一项为free[0],因为在代码当中,目前只是创建了堆空间,并没有分配空间。所以,我们发现,出free[0]指向了地址0x00520688,其他free均指向自己本身。为什么是这样呢?仔细想想就不难理解。没有分配的空间算是一个整体,并没有分割,这样的空间自然就链接到free[0]当中了。详细如下图。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-2-15 14:37
被天象独行编辑
,原因: