FreeList原理
双向链表管理系统,指针数组的每一位没有上限
Lookside原理
单向链表管理系统,数组的每一位最多存储四个堆快
调试
FreeList
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <windows.h> main() { HLOCAL h1,h2,h3,h4,h5,h6; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); __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); HeapFree(hp,0,h1); HeapFree(hp,0,h3); HeapFree(hp,0,h5); HeapFree(hp,0,h4); return 0; }
|
地址0x178代表FreeList[0],初始化后是指向地址0x688,在进行6次分配后,指向0x708处。为什么是指向0x708处,这里做一个简单的计算,分配了6次,分配的堆size大小分别为2,2,2,2,4,4
,一共就是0x10个堆快,一个堆快大小是8byte,也就是一共使用的堆大小为0x10*0x8 =0x80

查看内存0x688信息,可以看到h1的块首大小为两个堆块,1个堆快大小为8byte,h1堆快一共占用16byte。一定要注意堆头部中的size大小是计算了堆头部,也就是一定会有8个固定字节来保存堆头部

在使用三次HeapFree后查看内存,观察双向链表,FreeList数组中前4byte是双向链表的表头,后4Byte是链表表尾

在经过第四次HeapFree后,会进行堆合并,将h3,h4,h5合并为一个堆,大小为这3个堆快size大小总和8,所以FreeList数组中FreeList[8]发生改变,FreeList[2]和FreeList[4]还原。


LookSide
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <windows.h> void main() { HLOCAL h1,h2,h3,h4; HANDLE hp; hp = HeapCreate(0,0,0); __asm int 3 h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24); HeapFree(hp,0,h1); HeapFree(hp,0,h2); HeapFree(hp,0,h3); HeapFree(hp,0,h4); h3 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 16); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); HeapFree(hp,0,h2); }
|
在0x360178位置存放的堆尾地址发生了变化,从0x688变成了0x361E90

查看0x688(在初始化后堆开始的地方)

在进行三次HeapAlloc后Free内存在次查看块表内容,8字节堆快在Array[1]中,16字节堆快在Array[2]中

在内存中查看堆块的结构,每一个堆快在释放后依旧状态依旧为Busy

通过块表原理知道这是一个单向链表,再次申请一个16字节的堆快,Array[2]中存放的指针会清零。再次申请一个8Byte的堆快0x361EA0处地址也会清零

DWORD shooting
原理
通过操作堆快的Flink和Blink可以达到任意地址写入四字节
1 2 3 4 5 6
| int unlink (ListNode * node) { node -> blink -> flink = node -> flink; node -> flink -> blink = node -> blink; return 0; }
|
当在node这个块节点写入数据时,如果造成堆溢出覆盖了node节点后的堆快的头部指针,当进行unlink时产生安全问题,可以往任意地址写入四个字节,这也是DWORD shooting名称的由来
调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <windows.h> main() { HLOCAL h1, h2,h3,h4,h5,h6; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); _asm int 3 HeapFree(hp,0,h1); HeapFree(hp,0,h3); HeapFree(hp,0,h5); h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); return 0; }
|
利用方法
这个东西感觉将再多用处也不大,原理是双向链表在进行脱链时造成指针的重放,这里引用书上的一副图和unlink的伪代码
如果可以修改一个精心挑选的函数指针,将其修改为shellcode的地址,那么在调用该函数时,就会执行shellcode,从而造成任意代码执行