如何使用 Windbg 命令查看 CMap 条目

How to use Windbg commands for viewing CMap entries

在调查转储时,我偶然发现了一个 CMap 对象。这在Visual Studio中很容易调试,如下:

pThread->p<Class>->m_mapParameters.m_pHashTable,2741

其中 2741 是 m_mapParameters CMap 对象的 m_nHashTableSize。

现在我尝试在 Windbg 中做同样的事情,但这不起作用。我唯一能做的就是:

dt pThread

然后,我开始点击,结果如下(只是自动生成的命令):

dx -r1 ((<application>!<Class> *)0x7f8e820)
dx -r1 (*((<application>!<Class> *)0x7f8e820)),nd
dx -r1 -n (*((<application>!<Class> *)0x7f8e820)),nd

但后来,我卡住了:无法(我发现)使用元素数量来获取 CMap 条目的完整列表。

有人知道是否有办法在 Windbg(或 Windbg 预览)会话中获取 CMap 条目的完整列表吗?

提前致谢
多米尼克

当你说 m_mapParameters 是 CMap 对象时,你为什么要查看 pThread?

你可以使用 dx m_mapParameters

下面是一个小示例代码,它在 vs2017 社区开发提示中编译的 MFC 最低限度应用程序中实现 CMap,并查看 windbg 中的 CMap

#define WINVER 0x501  // compiler warning for _WINNT_WINVER so adding winxp
#define _CRT_SECURE_NO_WARNINGS  // using sprintf which is deprecated
#include <afxwin.h>  
class CMainFrame : public CFrameWnd {
public: CMainFrame();
protected:
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    DECLARE_MESSAGE_MAP()
};
CMainFrame::CMainFrame() {
    Create(NULL, "MFC Test", WS_OVERLAPPEDWINDOW, CRect(120, 100, 700, 480), NULL);
}
class CMessagesApp : public CWinApp {
public: BOOL InitInstance();
};
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_KEYDOWN()
END_MESSAGE_MAP()
BOOL CMessagesApp::InitInstance() {
    m_pMainWnd = new CMainFrame; 
    m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();
    return TRUE;
}
CMessagesApp theApp;
CMap<UINT, UINT, CStringA, CStringA> myMap;  // global CMap Declaration
void CMainFrame::OnKeyDown(UINT nChar, UINT, UINT) {
    char buff[0x100] = { 0 }; char buffy[0x100] = { 0 };
    sprintf(buff, "you pressed %c \n", nChar);
    myMap[nChar] = CStringA(buff);  
    sprintf(buffy, "total number of keys is %d\n", myMap.GetSize());
    MessageBox(buffy);
}

编译执行按下几个键盘键并将其附加到 windbg

:\>cl /Zi /W4  /Ox /nologo /EHsc mfctest.cpp /link /nologo /subsystem:windows
mfctest.cpp

:\>mfctest.exe

:\>windbg -pn mfctest.exe

:\>

结果

0:001> dx mfctest!myMap
mfctest!myMap                 [Type: CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >]
    [=0x5c79bc] classCObject     : {"CObject"} [Type: CRuntimeClass]
    [+0x004] m_pHashTable     : 0x8d32b8 [Type: CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >::CAssoc * *]
    [+0x008] m_nHashTableSize : 0x11 [Type: unsigned int]
    [+0x00c] m_nCount         : 9 [Type: int]
    [+0x010] m_pFreeList      : 0x8dd958 [Type: CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >::CAssoc *]
    [+0x014] m_pBlocks        : 0x8dd8c0 [Type: CPlex *]
    [+0x018] m_nBlockSize     : 10 [Type: int]
0:001> dx mfctest!myMap.m_pHashTable[0]->value.m_pszData
mfctest!myMap.m_pHashTable[0]->value.m_pszData                 : 0x8cf808 : "you pressed U ." [Type: char *]

0:001> dx mfctest!myMap.m_nCount
mfctest!myMap.m_nCount : 9 [Type: int]

编辑 这是转储 CMap->pHashTable.values

的方法

这张地图有 26 个键值对 (a,b..z)
hashtablesize 默认为 0x11(推荐素数)

0:001> dt myMap
mfctest!myMap
   +0x000 __VFN_table : 0x0161b96c 
   =0161bba8 classCObject     : CRuntimeClass
   +0x004 m_pHashTable     : 0x002e0858  -> 0x002e0d30 CMap<cut>::CAssoc
   +0x008 m_nHashTableSize : 0x11
   +0x00c m_nCount         : 0n26
   +0x010 m_pFreeList      : 0x002e0d90 CMap<cut>::CAssoc
   +0x014 m_pBlocks        : 0x002e0d28 CPlex
   +0x018 m_nBlockSize     : 0n10

转储 pHashTables 的默认数组
下面的每个 0x11 DWORD 都是指向 CMap::CAssoc
类型的 pHashTable 的指针 每个都可以有一个有效的 pNext 成员或 null 表示没有更多条目

0:001> dd @@c++(myMap.m_pHashTable) L @@c++(myMap.m_nHashTableSize)
002e0858  002e0d30 002e0600 002e05d0 002e05a0
002e0868  002e0d80 002e0d50 002e0620 002e05f0
002e0878  002e05c0 002e0590 002e0d70 002e0d40
002e0888  002e0610 002e05e0 002e05b0 002e0950
002e0898  002e0d60

准备使用 C++ 表达式求值器进行转储

0:001> $$ each of 0x11 dword are pointers to pHashTable and can have a valid pNext 
0:001> $$ lets c++ expressify the first array member
0:001> r? @$t0 = myMap.m_pHashTable[0]
0:001> ? @$t0
Evaluate expression: 3018032 = 002e0d30 <<<<< see the dump above
0:001> r? @$t0 = myMap.m_pHashTable[1]
0:001> ? @$t0
Evaluate expression: 3016192 = 002e0600 <<<<< see the dump above

0:001> $$ @$t0 represents the pHashTables and if you have sufficiently 
0:001> $$ big data m_nHashTableSize would be modified 

0:001> ?? myMap.m_nHashTableSize
unsigned int 0x11

0:001> $$ now that we have an expression that points to the first pHashTable 
0:001> we can iterate over the pNext and dump the keys

0:001> ? @$t0
Evaluate expression: 3016192 = 002e0600

转储实际条目

0:001> $$ we are going to iterate the second HashTable entries
0:001> .printf "%ma\n" , @@c++(@$t0->value.m_pszData)
you pressed R 

0:001> $$ choose the pNext if it exists
0:001> .printf "%ma\n" , @@c++(@$t0->pNext->value.m_pszData)
you pressed A 

0:001> .printf "%ma\n" , @@c++(@$t0->pNext->pNext->value.m_pszData)
Memory access error at 'm_pszData)'
0:001> ? @@c++(@$t0->pNext->pNext)
Evaluate expression: 0 = 00000000
0:001> $$ we reached the end so choose the next pHashTable and repeat the process
0:001> r? @$t0 = myMap.m_pHashTable[0]
0:001> ? @$t0
Evaluate expression: 3018032 = 002e0d30
0:001> $$ we are going to iterate the first HashTable entries
0:001> .printf "%ma\n" , @@c++(@$t0->value.m_pszData)
you pressed U 

0:001> .printf "%ma\n" , @@c++(@$t0->pNext->value.m_pszData)
you pressed D 

0:001> .printf "%ma\n" , @@c++(@$t0->pNext->pNext->value.m_pszData)
Memory access error at 'm_pszData)'

示例 javascript 代码转储 CMap 条目以实例化 CMap(int,int.CString,CString) 使用 class 来自 dt /v /t mfctest!myMap[ 的描述=20=]

"use strict";
// usage !dumpcmap address
// typeDescription is copied from windbg dt /v /t myMap for this instantiation
// of CMap (int,int,CString,Cstring) myMap and pointerised 
function initializeScript() {
    return [new host.functionAlias(dump_CMap, "dumpcmap")];
}

function log(instr) {
    host.diagnostics.debugLog(instr + "\n")
}

function dump_CMap(input ) {
    var typeDescription = "(CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > > *)"
    var foo = host.evaluateExpression( typeDescription + input.toString() )
    for(var i =0; i< foo.m_nHashTableSize ; i ++) {
        log (foo.m_pHashTable[i].value)
        if(foo.m_pHashTable[i].pNext != 0) {
            log (foo.m_pHashTable[i].pNext.value)
        } 
    }
}

js执行结果

0:001> ? myMap
Evaluate expression: 17889164 = 0110f78c
0:001> ?? myMap
class CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >
   +0x000 __VFN_table : 0x0108b96c 
   =0108bba8 classCObject     : CRuntimeClass
   +0x004 m_pHashTable     : 0x00325660  -> 0x00331460 CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >::CAssoc
   +0x008 m_nHashTableSize : 0x11
   +0x00c m_nCount         : 0n26
   +0x010 m_pFreeList      : 0x003314c0 CMap<unsigned int,unsigned int,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > >,ATL::CStringT<char,StrTraitMFC<char,ATL::ChTraitsCRT<char> > > >::CAssoc
   +0x014 m_pBlocks        : 0x00331458 CPlex
   +0x018 m_nBlockSize     : 0n10
0:001> .scriptload c:\wdscr\dump_cmap.js
JavaScript script successfully loaded from 'c:\wdscr\dump_cmap.js'
0:001> !dumpcmap 0x0110f78c
"you pressed U"
"you pressed D"
"you pressed R"
"you pressed A"
"you pressed O"
[object Object]
"you pressed L"
[object Object]
"you pressed Z"
"you pressed I"
"you pressed W"
"you pressed F"
"you pressed T"
"you pressed C"
"you pressed Q"
[object Object]
"you pressed N"
[object Object]
"you pressed K"
[object Object]
"you pressed Y"
"you pressed H"
"you pressed V"
"you pressed E"
"you pressed S"
"you pressed B"
"you pressed P"
[object Object]
"you pressed M"
[object Object]
"you pressed J"
[object Object]
"you pressed X"
"you pressed G"
@$dumpcmap(0x0110f78c)

糟糕,这似乎是 Windbg dt 命令的标准功能:

Option           Description
======           ===========
-a[quantity]     Show each array element on a new line, with its index.
                 A total of quantity elements will be displayed.
                 There must be no space between the a and the quantity.
                 If -a is not followed by a digit, all items in the array are shown.
                 The -a[quantity] switch should appear immediately before each type name or
                 field name that you want displayed in this manner.

不过,虽然这个解释中提到了不需要元素的数量,但是我也看到过不放数量导致显示的元素太少的情况。

因此:

dt -a2741 pThread->p<Class>->m_mapParameters.m_pHashTable