WinDBG提供了!heap命令帮助我们查找heap,同时我们也可以通过dt和MS SYMBOL来了解memory layout。
假设我们有下面一个小程序。
{
char * pChar = new char[2];
pChar[0] = 'a';
delete [] pChar;
return 0;
}
在WinDbg里面启动这个程序,运行到new操作结束。这时候我们可以得到pChar指针。
我们可以通过!heap -x 来查找对应的Heap Block
!heap -x 00343818
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
00343810 00343818 00340000 00340640 20 38 1e busy extra fill
从上面我们知道,这个heap block地址是00343810,用户内存地址是00343818,整个heap block大小是0x20。Heap Block Meta Data占据了前面8个字节 (这也解释了为什么用户内存地址的起始地址)。未使用的空间有1e(因为我们申请了2个byte) Flags是busy。
有了Heap Block地址,我们就可以运行下面命令
0:000> dt _HEAP_ENTRY 0X00343810
ntdll!_HEAP_ENTRY
+0x000 Size : 4
+0x002 PreviousSize : 7
+0x000 SubSegmentCode : 0x00070004
+0x004 SmallTagIndex : 0x49 'I'
+0x005 Flags : 0x7 ''
+0x006 UnusedBytes : 0x1e ''
+0x007 SegmentIndex : 0 ''
我们可以看到Size 是4.为什么是4了?因为这儿的单位不是byte,而是heap granularity。通常是8.所以我们可以计算:4*8=32=0x20。
我们再看一下内存布局。
0:000> dd 00343810
00343810 00070004 001e0749 abab0198 abababab
00343820 feeeabab feeefeee 00000000 00000000
上图是每个Heap Block的说明。对比我们的memory layout,我们就可以得到类似的信息。
我们也可以下面的命令来得到更详细的信息。
0:000> !heap -i 00343810
Detailed information for block entry 00343810
Assumed heap : 0x00340000 (Use !heap -i NewHeapHandle to change)
Owning segment : 0x00340000 (offset 0)
Block flags : 0x7 (busy extra fill )
Total block size : 0x4 units (0x20 bytes)
Requested size : 0x2 bytes (unused 0x1e bytes)
Previous block size: 0x7 units (0x38 bytes)
Previous block : 0x003437d8
Next block : 0x00343830
运行完pChar[0] = 'a';这条命令,我们再看看内存。
0:000> dc 00343818
00343818 abab0161 abababab feeeabab feeefeee a...............
我们可以看到'a'写进去了。