堆溢出在0day安全这本书里面应该是 基础章的最后一段了 其实也算是比较难的一段
在 CTF的pwn中 堆就是一个难点 并且 在那些重大的CTF比赛 上来就直接 来个 经典的 菜单 堆块题
不过 这个在windows下 却是比较难利用的
linux 是开源的 就相当于 代码审计 但是 windows 却是 闭源的,,,
然后 windows 2000的 堆块策略 基本了解了
看着这本书 然后 开始研究
实验环境 就是 windows2000 sp4 VC6.0 OD
代码如下
/*****************************************************************************
To be the apostrophe which changed "Impossible" into "I'm possible"!
POC code of chapter 6.2 in book "Vulnerability Exploit and Analysis Technique"
file name : heap_debug.c
author : failwest
date : 2007.04.04
description : demo show of how heap works
Noticed : 1 only run on windows 2000
2 complied with VC 6.0
3 build into release version
4 only used for run time debugging
version : 1.0
E-mail : failwest@gmail.com
Only for educational purposes enjoy the fun from exploiting :)
******************************************************************************/
#include <windows.h>
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;
}
这里直接 用的堆就是空表 并没有用到 快表 (为了更好的观察 空表)
而且还不能直接 用od 调试 要不然的话 会有所改变 主要改变的地方 体现在
然后我们我们直接 用od 来看
这里的eax 就是 我们申请到的地方 就是 HeapCreate 申请到的地方
ok 现在 还没有开始想我们申请到的地方申请堆块 可以看出这里面已经有了很多数据
这里我们看一下 0day 书上描述的内容
这里 的 0x520000 在我们这里是 0x0036000
然后 根据 书上描述的内容 我们来看一下 堆块里面的内容 首先 观察 空表索引区
这里面的值可以好好的 琢磨一下,(看的头晕眼花)
0x00360178处的值指向的是0x00360688,而接下来索引区其它的索引都是指向自己本身 这里对应了 上面的第三条 第四条
可以认为 这里的 0x00360688 就是那个 “尾块”
然后我们 到 0x00360688看看,可以看到这个位置的指针是指向0x00360178
然后空闲堆块的 结构
占用堆块的结构
也就是说我们的占用堆 会 把fd bk 给填充成数据
所以我们 直接越过 8字节 直接看 data 块
然后我们 观察一下 0x00360680
发现了 0x360680 的值是 0x00080130
这里的 0x0130表示这个堆块大小为0x0130 计算单位是8字节 也就是 0x980 字节0xx0008表示Previous chunk size
然后我们继续往下走
第一次申请堆块
h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);
看一下堆块的变化
可以看出变化还是很大的
这里具体分析一下
内存0x00360028处的0x00000130变成了0x0000012E,减小了2个堆单位,一个堆单位是8字节,也就是说减小了16个字节,而这个数字的意思就是尾块还有多少可以分配的空间,因为分配的时候会带上块首,所以第一次申请了3个字节加上块首8个字节,最终一共申请的是11个字节,所以实际就分配了16个字节也就是两个堆单位
然后会发现 0x360178这里指向的地方也发生了变化 变成了 0x0360698 然后 看一下 0x00360680
前面8字节是块首,0x0002表示两个堆单位(可以理解 0x8是上一个相邻的堆单位 0x2是所在堆块的堆单位)
前面提到过,0x00360178处是freelist[0],指向的是尾块,所以现在0x00360178处的指针变成了0x00360698,也就是说现在尾块的起始位置是0x00360690
块首的前两个字节是代表堆块的大小,所以0x012E的确是现在未分配堆块的大小,和前面的数据是匹配的
以上就是第一次分配内存里的部分变化,也是比较直观可见的变化,接下来看看其余五次的申请,过程和第一次是一样的,只是数据会有所不同
第二次申请堆块
h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,5);
尾块可分配的大小变为0x0000012C个堆单位
0x00360020 00 02 00 00 00 20 00 00 2C 01 00 00 FF EF FD 7F
尾块数据区的起始位置变为0x003606A8
0x00360170 00 00 00 00 00 00 00 00 A8 06 36 00 A8 06 36 00
第三次申请堆块
h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,6);
尾块可分配的空间变为0x0000012A个堆单位
尾块数据区的起始位置变为0x003606B8
第四次申请堆块
h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
尾块可分配的空间变为0x00000128个堆单位
尾块数据区的起始位置变为0x003606C8
第五次申请堆块
h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,19);
可分配尾块空间为0x00000124个堆单位
尾块数据区的起始位置为0x003606E8
第六次申请堆块
h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);
尾块可分配空间变为0x00000120个堆单位
尾块数据区的起始位置变为0x00360708
这里就没有什么好说的了 就是 不断地变化 freelist[0] 然后 把 堆的单位改一下
其实就是不断地 分割 尾块的过程,
然后 看一下 free的过程。
我们这里直接把 h1 h3 h5 全部free 掉 直接观察结果
Freelist 前四位我都标注好
可以看到 list[2] 和 list[4] 发生了变化 list[2] 指向了 fd指向了 h1 bk指向了 h3 list[4]直接指向了 h5
然后我们看一下 h1 和 h3的空间
发现两者data 化成了fd指针 fd 已经用上
然后 继续往下看 free掉 h4
可以看到Freelist[4]原来串着的h5没了,0x00360198处的指针已经指向了自身
而Freelist[8]发生了变化,值为0x003606A8,那就过去看看
可以看到这里出现了一个大小为8个堆单位的堆块
而这里原本 是h3的空间,,
因为当h4被释放的时候,h3,h4,h5发生了合并,大小为8个堆单位,所以串在了Freelist[8]上
也可以看到 freelist[2] 上面就剩下 h1块
ok 终于还是艰难的把分析了一下
(怀念在 ubuntu 里面用 gdb调试堆的时光,,,这OD调试的眼花。。。。)
参考资料