0%

初探windows堆

初探windows堆

环境配置好后便开始学习windows堆

堆块和堆表

摘抄自0day安全

堆块:出于性能的考虑,堆区的内存按不同大小组织成块,以堆块为单位进行标识,而不 是传统的按字节标识。一个堆块包括两个部分:块首和块身。块首是一个堆块头部的几个字节, 用来标识这个堆块自身的信息,例如,本块的大小、本块空闲还是占用等信息;块身是紧跟在 块首后面的部分,也是最终分配给用户使用的数据区。

堆表:堆表一般位于堆区的起始位置,用于索引堆区中所有堆块的重要信息,包括堆块的 位置、堆块的大小、空闲还是占用等。堆表的数据结构决定了整个堆区的组织方式,是快速检 索空闲块、保证堆分配效率的关键。堆表在设计时可能会考虑采用平衡二叉树等高级数据结构 用于优化查找效率。现代操作系统的堆表往往不止一种数据结构。

空表和快表

自己看0day漏洞吧

调试

第一个调试的程序源代码为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

int main ( )
{
HANDLE hHeap;
char *heap;
char str[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x1000, 0xffff);
getchar(); // 用于暂停程序,便于调试器加载

heap = (char*)HeapAlloc(hHeap, 0, 0x10);
printf("heap addr:0x%08x\n",heap);

strcpy(heap,str); // 导致堆溢出
HeapFree(hHeap, 0, heap); // 触发崩溃

HeapDestroy(hHeap);
return 0;
}

复制第一次的时候,edi跟esi,这里我是运行后截的图

从控制台得到heap地址,查看该地址处内容

1
!heap -p -a 0x00030590

!heap可以查看有多少个大堆块

1
!heap

查看第三个大堆块里内容,可以看到我们堆块下面还有个空闲堆块

1
!heap -a 0x00030000

复制前空闲堆块

1
dt _HEAP_FREE_ENTRY 0x305a0

复制后空闲堆块

可以看到,这个堆块的数据结构整体都被破坏了

FreeList中的前后指向的指针被改为0x41414141了

调试技巧

添加堆尾检查和页堆,去掉堆标志

1
2
3
4
5
6
7
htc 堆尾检查
hfc 堆释放检查
hpc 堆参数检查
ust 用户态栈回溯
htg 堆标志
hvc 调用时验证
hpa 启用页堆

堆尾检查

这里是用windbg直接打开exe然后输入命令

1
2
!gflag +htc +hpc
g

这就是检测信息

1
Heap block at 00390588 modified at 003905A0 past requested size of 10

页堆

启用页堆

1
!gflag +hpa

这里调试方法个人而异吧,我是绿色版的windbg,这里我直接打开windbg目录下的gflag

然后勾选如图,一开头是默认都不能选,输入目录后,按tab刷新就可以勾选了,然后点应用,launch,发送,然后确定,关闭窗口

接下来需要管理员启动windbg,因为gflag是管理员权限,这时候附加heap1程序,这时候查看

这里已经开启了页堆,接下来正常运行,g然后控制台回车,最后在此处断下

他断下的地方刚好是我们原来报异常的地方,而且此时堆没有被破坏,也就是环境被破坏前的现场,基于栈回溯,追踪到0x401000

uf可以查看整个函数汇编

总结

windbg总结

1
2
3
4
5
6
!heap 查看堆情况
!heap -a 地址 查看指定堆表里有多少个堆块
!heap -p -a 地址 指定堆表,指定堆块,详细情况
dt _HEAP_FREE_ENTRY 查看堆结构,以结构体形式
uf 地址 查看指定地址整个函数汇编
!gflag 查看开启了什么检查

heap检查参数,+是开启 -是不开启

1
2
3
4
5
6
7
htc 堆尾检查
hfc 堆释放检查
hpc 堆参数检查
ust 用户态栈回溯
htg 堆标志
hvc 调用时验证
hpa 启用页堆

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/1da29925/
版权声明:转载请注明出处!