为什么!address和!heap的输出不匹配?

wil*_*rcs 5 memory windbg

我在Windbg中调试以下C程序:

int main()
{
    size_t size = 500*1024*1024;
    void *p = malloc(size);
    memset(p, 'a', size);
    printf("%p", p);
}
Run Code Online (Sandbox Code Playgroud)

我使用:cl/Zi leak.c编译程序,并生成了一个leak.exe.

我在printf行设置了一个断点.我运行以下命令:

0:000> !address -summary

--- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
Free                                     21          5f0f6000 (   1.485 Gb)           74.27%
Heap                                      3          1f501000 ( 501.004 Mb)  95.07%   24.46%
<unknown>                                39           1436000 (  20.211 Mb)   3.84%    0.99%
Image                                    35            300000 (   3.000 Mb)   0.57%    0.15%
MappedFile                                4            182000 (   1.508 Mb)   0.29%    0.07%
Stack                                     3            100000 (   1.000 Mb)   0.19%    0.05%
Other                                     6             3f000 ( 252.000 kb)   0.05%    0.01%
TEB                                       1              1000 (   4.000 kb)   0.00%    0.00%
PEB                                       1              1000 (   4.000 kb)   0.00%    0.00%

--- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_PRIVATE                              21          207f0000 ( 519.938 Mb)  98.66%   25.39%
MEM_IMAGE                                64            54c000 (   5.297 Mb)   1.01%    0.26%
MEM_MAPPED                                7            1be000 (   1.742 Mb)   0.33%    0.09%

--- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
MEM_FREE                                 21          5f0f6000 (   1.485 Gb)           74.27%
MEM_COMMIT                               73          1f9c9000 ( 505.785 Mb)  95.98%   24.70%
MEM_RESERVE                              19           1531000 (  21.191 Mb)   4.02%    1.03%

--- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
PAGE_READWRITE                           27          1f45f000 ( 500.371 Mb)  94.95%   24.43%
PAGE_EXECUTE_READ                         9            376000 (   3.461 Mb)   0.66%    0.17%
PAGE_READONLY                            26            1e4000 (   1.891 Mb)   0.36%    0.09%
PAGE_WRITECOPY                            9              c000 (  48.000 kb)   0.01%    0.00%
PAGE_READWRITE|PAGE_GUARD                 2              4000 (  16.000 kb)   0.00%    0.00%

--- Largest Region by Usage ----------- Base Address -------- Region Size ----------
Free                                        1fb51000          5590f000 (   1.337 Gb)
Heap                                          750000          1f401000 ( 500.004 Mb)
<unknown>                                   7f0e0000            f00000 (  15.000 Mb)
Image                                       77bc0000             d6000 ( 856.000 kb)
MappedFile                                  7efe5000             fb000 (1004.000 kb)
Stack                                         210000             fd000 (1012.000 kb)
Other                                       7efa0000             33000 ( 204.000 kb)
TEB                                         7efdd000              1000 (   4.000 kb)
PEB                                         7efde000              1000 (   4.000 kb)
Run Code Online (Sandbox Code Playgroud)

我可以看到堆大约500MB,这是预期的.

但是!heap命令无法看到此信息:

只有1个堆.

0:000> !heap
Index   Address  Name      Debugging options enabled
  1:   00650000                 tail checking free checking validate parameters

0:000> !heap -a 00650000
Index   Address  Name      Debugging options enabled
  1:   00650000 
    Segment at 00650000 to 00750000 (0000f000 bytes committed) // Why so few memory committed.
    Flags:                40000062
    ForceFlags:           40000060
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      00000517
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     00650138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   006500a0
    Unable to read nt!_HEAP_VIRTUAL_ALLOC_ENTRY structure at 00750000
    Uncommitted ranges:   00650090
            0065f000: 000f1000  (987136 bytes)
    FreeList[ 00 ] at 006500c4: 0065b340 . 0065b340  
        0065b338: 00458 . 028b8 [104] - free

    Segment00 at 00650000:
        Flags:           00000000
        Base:            00650000
        First Entry:     00650588
        Last Entry:      00750000
        Total Pages:     00000100
        Total UnCommit:  000000f1
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)

0:000> dt p
Local var @ 0x30ff04 Type void*
0x00750020 
Void
0:000> !heap -p -a 0x00750020 
    address 00750020 found in
    _HEAP @ 650000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        00750018 3e80200 0000  [00]   00750020    1f400000 - (busy VirtualAlloc)
0:000> !heap -s 
NtGlobalFlag enables following debugging aids for new heaps:
    tail checking
    free checking
    validate parameters
LFH Key                   : 0x343f9ad2
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
Virtual block: 00fa0000 - 00fa0000 (size 00000000)
006d0000 40000062    1024     36   1024      1     1     1    1      0      
-----------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

为什么我看不到上面的信息!heap -s?如何转储堆中的所有条目?

Tho*_*ler 3

首先需要一些解释......

堆与虚拟内存

恕我直言,堆的发明是因为它的粒度VirtualAlloc()(64kB)对于少量数据来说太浪费了。如果我查看 a 的输出!heap <address>,我可以看到

Heap entries for Segment00 in Heap 00100000
     address: psize . size  flags   state (requested size)
    00100000: 00000 . 00588 [101] - busy (587)
Run Code Online (Sandbox Code Playgroud)

查看size有 5 位数字的列。这可能意味着堆条目的最大有用大小是

0:001> ? fffff
Evaluate expression: 1048575 = 000fffff
Run Code Online (Sandbox Code Playgroud)

大约 1 MB。超过这个大小,拥有堆的粒度可能没有意义,可以VirtualAlloc()直接使用。或者,不是,而是malloc()函数为你决定。

输出中出现错误消息

在你的输出中,你注意到这两行了吗

Virtual Alloc List:   006500a0
Unable to read nt!_HEAP_VIRTUAL_ALLOC_ENTRY structure at 00750000
Run Code Online (Sandbox Code Playgroud)

这表明堆中存在实际由 处理的部分VirtualAlloc()。但是,WinDbg 无法找到它的数据类型:

0:001> dt nt!_HEAP_VIRTUAL_ALLOC_ENTRY
Symbol nt!_HEAP_VIRTUAL_ALLOC_ENTRY not found.
Run Code Online (Sandbox Code Playgroud)

的输出!heap -s丢失,所以我在我的机器上创建了它(WinDbg 6.3.9600.16384):

0:001> !heap 00100000 -s
LFH Key                   : 0x62502d13
Termination on corruption : ENABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
Virtual block: 005b0000 - 005b0000 (size 00000000)
Virtual block: 006b0000 - 006b0000 (size 00000000)
...

Virtual block: 1fab0000 - 1fab0000 (size 00000000)
00100000 00000002    1024    204   1024      4     7     1  500      0   LFH
-----------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

在我的演示中,我创建了约 500 个 1 MB 的块。请注意,00000000 的大小似乎已损坏。!address但是,如果我在块上使用,它会得到正确的区域大小(00100000):

0:001> !address 005b0000                                     
...
Usage:                  Heap
Base Address:           005b0000
End Address:            006b0000
Region Size:            00100000
State:                  00001000    MEM_COMMIT
Protect:                00000004    PAGE_READWRITE
Type:                   00020000    MEM_PRIVATE
Allocation Base:        005b0000
Allocation Protect:     00000004    PAGE_READWRITE
Run Code Online (Sandbox Code Playgroud)

丢失的 _HEAP_VIRTUAL_ALLOC_ENTRY 是什么样子的?

0:001> dd 770000 L10
00770000  00870000 00670000 00000000 00000000
00770010  00100000 00100000 c8d50110 04000000
00770020  003df5a8 00870020 00000000 00000000
00770030  000ffc00 00000001 000000c1 fdfdfdfd
Run Code Online (Sandbox Code Playgroud)

偏移量 0 处的值 870000 似乎是到下一个内存块的 FLink,而偏移量 4 处的值 670000 似乎是到前一个内存块的 BLink。

偏移量 10 和 14 都与区域大小匹配。

0:001> ? 0n1023*0n1024
Evaluate expression: 1047552 = 000ffc00
Run Code Online (Sandbox Code Playgroud)

偏移量 +30 处是大小(我在这里分配了 1023 kB 的块)。

FDFDFDFD是一个众所周知的调试魔数,表示无人区,因此这可能是该结构的结尾。

从 XP 转储中提取结构

我有一个来自 Windows XP 的转储。看看nt!_HEAP_VIRTUAL_ALLOC_ENTRY那里是如何解决的:

0: kd> dt -r1 nt!_HEAP_VIRTUAL_ALLOC_ENTRY
   +0x000 Entry            : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x008 ExtraStuff       : _HEAP_ENTRY_EXTRA
      +0x000 AllocatorBackTraceIndex : Uint2B
      +0x002 TagIndex         : Uint2B
      +0x004 Settable         : Uint4B
      +0x000 ZeroInit         : Uint8B
   +0x010 CommitSize       : Uint4B
   +0x014 ReserveSize      : Uint4B
   +0x018 BusyBlock        : _HEAP_ENTRY
      +0x000 Size             : Uint2B
      +0x002 PreviousSize     : Uint2B
      +0x000 SubSegmentCode   : Ptr32 Void
      +0x004 SmallTagIndex    : UChar
      +0x005 Flags            : UChar
      +0x006 UnusedBytes      : UChar
      +0x007 SegmentIndex     : UChar
Run Code Online (Sandbox Code Playgroud)

结论

!heapmalloc()在这种情况下实际使用的大块似乎存在问题VirtualAlloc()。WinDbg 找不到它期望将内存内容映射到的数据类型。微软或许应该修复这个错误。

堆的输出统计数据!address -summary似乎是合理的。