堆溢出利用

1. 堆的数据结构

堆表索引空闲态堆块,重要的堆表有两类:空表、快表。我们通过下面代码练习识别堆表、堆块:

#include <windows.h>
main()
{
	HLOCAL h1,h2,h3,h4,h5,h6;
	HANDLE hp;
	hp = HeapCreate(0,0x1000,0x10000);//InitialSize:0x1000, MaxSize:0x10000, 成功创建堆区后,会把整个堆区的起始地址返回给eax
	__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;
}

Release版运行,跳入OD调试界面:

堆区起始地址0x3A0000.
当一个堆刚被初始化时,它只有一个空闲的大块(“尾块”),而它被记录在了空表的第0项。除了第0项外,其余各项空表索引均指向自己。
空表索引区位于偏移0x178处

可知尾块地址为0x3A0688(注意这个地址是尾块数据区地址;如果启用快表,这个地址将是快表。想要启动快表,可将代码hp = HeapCreate(0,0x1000,0x10000);替换为hp = HeapCreate(0,0,0);):

尾块现大小为0x130(单位为8字节),即0x980字节,加上0x688,等于0x1008
堆块首数据结构如图:

2. 堆的管理

2.1 分配

有几个前提:
1)若请求32个字节,实分配40个字节,因为有8个字节做块首
2)堆块分配最小单位为8字节
3)初始状态下,快表和空表都为空,不存在精确分配,将发生次优块分配
4)由于次优分配,分配函数会陆续从尾块中切走一些小块,修改尾块块首中的size信息,把freelist[0]指向新的尾块位置

接下来把int3改为nop

继续执行到0x401061后(对应代码h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);),可发现:

h1实请求3字节,但分配了16字节(8字节块首 + 8字节最小堆分配单位)
继续执行,最终可知实际堆块分配情况:h1~h4均为2个堆单位,h5、h6均为4个堆单位,所以最终尾块大小为0x130-2*4-4*2=0x120:

2.2 释放

前三次释放不会发生合并(因为不连续),按照空表组织规则,h1、h3所指堆块会被链入freelist[2],h5被链入freelist[4]。
此时堆块们如图,h1、h3、h5的data区的前8个字节存储着前向、后向链表指针:

查看0x3A178处的空表索引:

此时空表结构如图:

2.3 合并

释放h4后,h3、h4、h5发生合并。
首先这三个空闲块都从空表中摘下,合并后链入空表freelist[8]。合并只会修改空表索引和块首数据,原块块身基本没有变化
此时空表索引如图:

注意:空表的第一个块不会向前合并,最后一个块不会向后合并。

3. DWORD SHOOT

利用堆溢出淹没掉下一个堆块块首,从而改写块首中的前向、后向指针。一旦此堆块被回收,将发生:

void remove(ListNode* node){
    node->blink->flink=node->flink;
    node->flink->blink=node->blink;
}

而恰恰现在的flink、blink都是伪造的,所以将导致攻击者能够向任意内存写入任意数据。当然不仅仅是回收堆块,任何涉及链表的操作(分配、合并等)都可成为攻击点。

3.1 常用攻击目标

  • 内存变量:如标志位
  • 代码逻辑:如将调用指令替换为nop
  • 函数返回地址
  • 异常处理机制:堆溢出容易引起异常,从而转入异常处理机制,所以异常处理涉及的数据结构往往成为DWORD SHOOT攻击目标
  • 函数指针
  • P.E.B中线程同步函数的入口地址:在进程退出时会被ExitProcess()调用
posted @ 2017-10-08 12:30  T_1  阅读(735)  评论(0编辑  收藏  举报