Windows下的HEAP溢出及其利用

Windows下的HEAP溢出及其利用

一、概述
前一段时间ASP的溢出闹的沸沸扬扬,这个漏洞并不是普通的堆栈溢出,而是发生在HEAP中的溢出,这使大家重新认识到了Windows下的HEAP溢出的可利用性。其实WIN下的HEAP溢出比LinuxSOLARIS下面的还要简单得多,大家肯定已经都搞明白了,我来做是一个总结,以免自己将来忘了。由于缺乏这方面的资料及源代码,所有的分析结果都来自于反汇编和调试的分析,所以错误之处在所难免,敬请各位指正。

以下所有程序的测试环境为:
中文版Windows 2000 + SP2
VC++ 6.0

二、WindowsHEAP管理机制简述
LINUX一样,WindowsHEAP区是程序动态分配一块内存区域。程序员一般调用C函数malloc/free或者C++new/delete或者WIN32 API函数HeapAlloc/HeapFree来动态分配内存,这些函数最终都将调用ntdll.dll中的RtlAllocateHeap/RtlFreeHeap来进行实际的内存分配工作,所以我们只需要分析RtlAllocateHeap/RtlFreeHeap就行了。

对于一个进程来说可以有多个HEAP区,每一个HEAP的首地址以句柄来表示:hHeap,这也就是RtlAllocateHeap的第一个参数。每个HEAP区的整体结构如下:

+-------------------------------------------------------------------+
| HEAP
总体管理结构区 | 双指针区 | 用户分配内存区 |
+-------------------------------------------------------------------+
^ ^
|_hHeap |_hHeap+0x178

heap
总体管理结构区存放着一些用于HEAP总体管理的结构,这不是我们所关心的。双指针区存放着一些成对出现的指针,用于定位分配内存以及释放内存的位置,这可能是某种树结构,我还没完全搞清楚。用户分配内存区是用户动态分配内存时实际用到区域,也这是HEAP的主体。

当我们调用RtlAllocateHeap(HANDLE hHeap,DWORD dwFlags,SIZE_T dwBytes)来分配内存时将进行以下操作:
对参数进行检查,如果dwBytes过大或小于0都按照出错处理,根据dwFlags来设置一些管理结构;
检查是否为DEBUG程序,对于DEBUG的程序与实际运行的程序每个内存块之间的结构是不同的,所以我们下面说到的都是以RELEASE版编译的实际运行的程序(不是在MSDEV中调试的程序);
根据要分配的内存的大小(dwBytes)决定不同的内存分配算法,我们只分析小于1024 bytes的情况;
从双指针区找到用户内存区的末尾位置,如果有足够的空间分配所需的内存,就在末尾+dwBytes+8的位置放置一对指针来指向双指针区的指向用户内存区末尾位置的地方;
在后面同时设置双指针区的指向用户内存区末尾位置的指针指向进行完分配之后的用户内存区末尾位置。这么说可能有点绕,不过这跟HEAP溢出没有太大的关系,所以我们就不细究了。

两块连续分配的内存块之间并不是紧挨着的,而是有8字节的管理结构,最末尾的一块内存后面还另外多了8字节的指针指向双指针区,就是上面提到过的。

假设有以下程序:
buf1 = HeapAlloc(hHeap, 0, 16);
buf2 = HeapAlloc(hHeap, 0, 16);
连续分配了两块16字节内存,实际在内存中(用户分配区)的情况是这样的:

第一次分配后:
+-----------------------------------------------+
| buf1 | 8 byte |4 byte|4 byte|
+-----------------------------------------------+
|
用户内存 | 管理结构 | 两个指针 |

第二次分配后:
+---------------------------------------------------------------------------------+
| buf1 | 8 byte | buf2 | 8 byte |4 byte|4 byte|
+---------------------------------------------------------------------------------+
|
用户内存 | 管理结构 | 用户内存 | 管理结构 | 两个指针 |

在第二次分配内存的时候会利用第一块内存管理结构后面那两个指针进行一些操作,其中会有一次写内存的操作:

77FCB397 mov [ecx], eax
77FCB399 mov [eax+4], ecx

这时的eaxecx分别指向:
+-----------------------------------------------+
| buf1 | 8 byte |4 byte|4 byte|
+---------------------------------^------^------+
|
用户内存 | 管理结构 |_eax |_ecx |

写到这里大家一定就明白HEAP溢出如何利用了吧?假设我们分配完buf1之后向其中拷贝内容,拷贝的内容大小超过buf1的大小,即16字节,就会发生溢出,当如果我们覆盖掉了那两个4字节的指针,而下一次分配buf2之前又没有把buf1释放掉的话,那么就会把一个4字节的内容写入一个地址当中,而这个内容和地址都是我们能够控制的,这样我们就可以控制函数的流程转向我们的shellcode了。

三、HEAP溢出的利用
上面就是这种溢出可以被利用的基本原理,下面我们就来看看具体是怎么回事。有这么一个程序:

/*
* Windows Heap overrun test - vul.c
* by isno
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
HANDLE hHeap;
char *buf1, *buf2;
char mybuf[] = "AAAAAAAAAAAAAAAABBBBBBBBXXXXYYYY";

//
在进程的默认HEAP当中分配内存
hHeap=GetProcessHeap();

//
先分配一块16字节内存buf1
buf1 = HeapAlloc(hHeap, 0, 16);

//
32字节的mybuf拷贝到16字节的buf1里面,发生溢出!
strcpy(buf1,mybuf);

//
再次分配一块16字节的内存buf2,此时buf1还没有被释放
//
由于buf1溢出了,所以当写内存的时候就会出错
buf2 = HeapAlloc(hHeap, 0, 16);

//
释放这两块内存
HeapFree(hHeap, 0, buf1);
HeapFree(hHeap, 0, buf2);

return 0;
}

我们把这个程序用VC按照RELEASE方式编译,并在命令行下运行它(不要在MSDEV中调试运行)。如果你没有装SOFTICE的话就会弹出一个错误对话框显示:"0x77fcb397"指令引用的"0x59595959"内存。该内存不能为"written"

可以注意到0x59595959就是YYYY,这就证明了程序在向YYYY指向的内存地址进行写操作,写的内容是什么呢?如果你启动了SOFTICE的话,运行这个程序的时候SOFTICE就会自动跳出来,并停在下面的指令处:

77FCB397 mov [ecx], eax

此时eax=0x58585858,ecx=0x59595959,因为0x59595959这个地址没有映射内存页面,所以执行这个指令的时候出错了。

0x58585858
0x59595959正是我们覆盖buf1所用的XXXXYYYY,实际进行的内存分配操作就是上面我们说过的那样:

第一次分配后:
+-----------------------------------------------+
| buf1 | 8 byte |4 byte|4 byte|
+-----------------------------------------------+
|
用户内存 | 管理结构 | 两个指针 |

溢出后:
+-----------------------------------------------+
| buf1 | 8 byte |4 byte|4 byte|
+-----------------------------------------------+
| AAAAAAAAAAAAAAAA | BBBBBBBB | XXXX | YYYY |

这样当第二次分配buf2的时候就会把XXXX写入到YYYY所指向的地址当中去,由于XXXXYYYY都是我们所能够控制的,所以我们就可以把shellcode地址写入到堆栈中保存的函数返回地址去,这样当函数返回的时候就会跳到我们的shellcode去执行。

当然这是比较理想的情况,实际上利用这个漏洞还有很多问题,下面我们以一个实际的例子来看看具体利用这个漏洞的情况。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值