如何在Windows默认进程堆创建时触发断点并获取调用栈?
如何在Windows默认进程堆创建时触发断点?
我希望观察Windows默认进程堆的创建时机,设置断点并获取其创建时的调用栈,仅用于学习Windows内部机制及验证《Windows Internals》书中内容。
我使用如下简单HelloWorld程序进行调试:
#include <iostream> int main() { std::cout << "Hello World!\n"; }
我的调试步骤如下:
- 准备尽早启动的调试会话
- 启动WinDbg
- 用WinDbg启动HelloWorld程序
- 在初始断点执行命令:
sxe cpr sxe ld * - 执行
.restart启动早期调试流程,此时仅加载了可执行文件
- 确认处于早期调试会话,此时ntdll尚未加载:
0:000> lm start end module name 00100000 00122000 HelloWorld (deferred) - 设置未解析断点(因kernelbase尚未加载):
bu kernelbase!heapcreate - 确认当前无堆存在:
- 初始时执行
!heap提示无效类型信息 - 执行
g等待ntdll加载完成:0:000> g ModLoad: 77940000 77ae4000 ntdll.dll [...] - 再次执行
!heap确认无堆:0:000> !heap Heap Address NT/Segment Heap
- 初始时执行
但在加载下一个模块KERNEL32.DLL后,堆已存在,而设置的断点并未触发(此时kernelbase尚未加载,断点未解析):
0:000> g ModLoad: 76540000 76630000 C:\Windows\SysWOW64\KERNEL32.DLL [...] 0:000> !heap Heap Address NT/Segment Heap 012d0000 NT Heap
解决方法
问题出在断点目标选择错误:Windows默认进程堆并非通过kernelbase!HeapCreate创建,而是由ntdll在进程初始化阶段通过内部函数调用创建的。需针对ntdll中的堆创建相关函数设置未解析断点,具体步骤如下:
- 在仅加载HelloWorld.exe的早期调试会话中,执行以下命令设置未解析断点:
若想捕获更底层的内核态调用,也可设置:bu ntdll!RtlCreateHeapbu ntdll!NtCreateHeap - 执行
g继续执行,当ntdll加载完成后,断点会自动解析并触发,此时即可捕获默认进程堆创建时的完整调用栈。
补充说明:进程默认堆的创建逻辑位于ntdll!LdrpInitializeProcess流程中,RtlCreateHeap是用户态封装函数,最终会调用NtCreateHeap进入内核态完成堆的实际创建。前者的调用栈更贴近用户态初始化流程,后者则能看到内核态调用的起点。
内容的提问来源于stack exchange,提问作者Thomas W.
相关产品推荐
相关产品推荐

