You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#include<windows.h>main()
{
HLOCALh1,h2,h3,h4,h5,h6;
HANDLEhp;
hp=HeapCreate(0,0x1000,0x10000);
__asmint3h1=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 coalesesHeapFree(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]return0;
}
堆溢出
堆结构
堆和栈的结构差异很大,堆的分配以块为单位,分为块首和块身,对堆操作使用的指针一般指向块身起始位置。堆块和堆表组成一个堆,不同类型的堆表将堆在逻辑上分为不同的部分,重要的堆表(只索引空闲堆块)有两种:空表(freelist)和快表(lookaside)。
占用态堆块结构
空闲态堆块
可以看出空闲状态下是有前后块指针的,占用态没有
空表
由空闲堆块组成的双向链表,空表总共有128条,具体结构如下图
空表连接的都是空闲堆块,某个块被分配使用时,空表就会将该块从表中摘除,当被释放后,又会再次连接到空表上
通过0day中给出的代码进行一段测试,测试代码:
按照0day书上的环境,并设置好后,od成功拦截
分配的堆的首地址
0x00310000
,其中位移0x178
处指向分配的空表的尾块0x0688
,再看0x688
处的数据其中
0x688
指的是其数据部分,它的块首是向前8个字节0x680
,根据其c源码,每个HeapAlloc
分配的空间如下空表分配
根据空表的分配规则,以及上节中得出的每个块应该分配的大小,可以得出如下的分配地址列表
再通过od进行验证
H1
H2
H3
H4
H5
H6
可以发现每一步跟我们根据规则画出来的分配都是相同的
空表释放与合并
第一步,释放H1,空闲空间是8bytes,所以其释放完后应该挂在free[1]上
未释放前
free[1]
应该是这样的验证一下
Free,根据规则应该是这样的
free1的后指针指向0x688这个堆块,堆块的前后指针均指向0188,验证一下
释放H3
free[1]后挂了两块不连续的空闲块!验证一下
释放H5
根据规则,我们可以得出
但是结果跟猜想的不一样
按照0day书中所说,
但是这里却不是
01A8
,而是0198
,没有弄明白是为什么。。。。。释放H4
因为H3,H4,H5是连在一起的,所以需要合并,合并的总空间是32个空闲字节,所以应该挂在
01B8
即free[4]上验证一下
空表的分配,释放和合并分析完毕!
快表
根据0day书中的描述,开始时快表是空的,堆管理器首先从空表上分配给
HeapAlloc
,等到HeapFree
释放空间再将其链入快表中。利用如下代码进行测试查看一下偏移位置
0x178
可以看到已经不是
0x00310688
了,变为了0x00311E90
,再来看看0x688
处的快表,根据0day书中所说lookaside从0x6B8
开始,每48个字节算一个lookaside
首项,现在lookaside[1]
等都是0往下依次类推。等到第一次
HeapFree
之后再看已经得到了分配后释放的堆块,并且链入了快表,再看释放4次之后
可以看到
0x6E8
处的值,经过4次释放变为00311EA0
,再看一下00311EA0
其指向了第一次释放的位置,由此可知后释放的空间会插入先前插入的位置的前面。再次分配会优先分配快表
分配
16
字节之后,lookaside[2]
不再有空间。快表的分配,释放分析结束DWORD SHOOT
空表由双向链表构成,双向链表在拆卸的过程中会发生如下的操作
具体的操作可以参照0day中的图,如下
其实总结起来就是[blink]=flink数据
测试地址写入
测试代码
根据
DWORD SHOOT
的定义,我们人为的构造一下,将8888888
写入地址00000022
,产生错误
可以看出是前向指针中的数据写入了后向指针中所表示的地址。
测试MessageBOx弹窗
这里主要依据的就是windows为了同步进程中的多个线程,使用了一些同步措施,如锁机制,信号量等,当进程退出时,
ExitProcess
函数需要做很多的工作,其中就会用到RtlEnterCriticalSection
和RtlLeaveCriticalSection
,指向前一个函数的指针存放在0x7FFDF020
,即进程退出时会到这个地址取出RtlEnterCriticalSection
函数的指针,并执行该函数,所以我们需要利用堆溢出中的DWORD SHOOT
技术将shellcode
的地址写入0x7FFDF020
中。找到
RtlEnterCriticalSection
函数地址地址为
0x77F82060
,再确定shellcode
的地址,具体测试代码如下但是测试过程中始终失败,具体也找不到什么原因。
The text was updated successfully, but these errors were encountered: