-
-
[原创]windows堆内存管理及调试技巧
-
发表于: 2022-4-21 13:02 15055
-
进程堆是在应用层上的概念,对于内核内核层进程的内存全部有平衡二叉树管理,存放在EPROCESS结构的VadRoot变量,该变量维护了该进程所有内存,以下只讨论应用层的堆内存,堆内存由堆管理器管理,堆管理器实现代码在ntdll.dll模块中,最终ntdll会使用系统调用virtualalloc进入内核申请内存分配,在C/C++时我们常用new和malloc系列函数,windows本身也提供了堆的申请,调用heapcreate和heapalloc,new的底层是调用malloc,malloc的底层是调用heapalloc。
heapcreate每次调用会以页为单位分配一块内存,返回一个句柄,该句柄也是堆的首地址,heapcreate也会初始化这个堆头,用于管理这个堆,堆头结构就是_HEAP,可以使用windbg命令dt _HEAP查看
以下代码是使用heapcreate创建的堆
!heap指令是查看当前进程的所有堆
windbg中多出来的01360000堆是系统堆,是进程创建时就已经创建好的。
可以使用!peb指令查看
使用!heap -a 01A70000查看这个堆中的每一个堆块
可以看出有五个堆块+一个未提交的堆块,所有的堆块都有一个堆块头结构是_HEAP_ENTRY
第一个堆块是01a70000-01a704a8,就是上面说到了堆头_HEAP结构,同时_HEAP结构的前八个字节也是一个_HEAP_ENTRY
第二个堆块是01a704a8-01a704c8,使用windbg命令db查看db 01a704a8+8,8是堆块头结构_HEAP_ENTRY
就是源代码中第一次使用heapalloc分配的内存
第三个堆块是01a704c8-01a704e8,使用windbg命令db查看db 01a704c8+8,8是堆块头结构_HEAP_ENTRY
第四块堆是01a704e8-01a70fe0是一块free内存
第五块堆是01a70fe0-01a71000是堆的结尾
01a71000- 是没有commit空间
当堆溢出时可以使用页堆进行排查,使用gflag.exe程序
使用页堆时,当程序分配内存时,堆管理器会额外分配一个页,将数据存放在页的末尾,当访问的数据超过分配的大小,会触发一个缺页异常,开发人员可以捕获到异常
当发生内存泄漏时,可以使用umdh.exe程序分别生成两个log文件,对比log文件排查出申请堆的堆栈
赞赏
- [原创]windows堆内存管理及调试技巧 15056
- [原创]cpu任务段任务门 15447
- [原创]关于X64程序中RUNTIME_FUNCTION,UNWIND_INFO,UNWIND_CODE结构理解 6628
- 调试器原理分析 3669