Windbg扩展指令开发教程
来源:互联网 发布:网络平台建设方案 编辑:程序博客网 时间:2024/05/22 05:01
之前我曾提供了导出stl map和set内容的windbg扩展指令库,并给出了使用方法。授人以鱼不如授人以渔,下面的文章将以开发一个导出CMap容器内容的windbg扩展指令cmap为例,介绍如果开发一个windbg的扩展指令库。
1. windbg扩展指令
Windbg自身提供丰富的扩展指令,使得它的调试功能十分强大。通常,我看到的以!开头的指令就是扩展指令,而这些扩展指令大多由windbg安装目录下winext、winxp等子目录中的DLL导出。
有时候我们也需要开发自己的扩展指令,用以方便地完成特殊的调试任务。例如,我们很可能需要导出一个存储了大量数据CMap容器的内容,开发一个自己的CMap导出指令会很方便地解决问题。
开发一个扩展指令,实质上就是要按照debug engine定义的开发规范,开发一个导出了扩展指令的扩展DLL。扩展DLL可以分为3类,在Windbg目录下sdk\sample中提供了对应的简单示例:
- DbgEng extension DLLs。这种类型是基于dbgeng.h头文件提供的接口所开发的DLL并导出DbgEng 扩展指令。这类指令的实现过程中可以使用Debugger Engine的API并且也能使用WdbgExts API。(示例代码名称:exts)
- EngExtCpp extension DLLs。 这种类型是基于engextcpp.h和dbgeng.h头文件中提供的接口开发的DLL。导出DbgEng 扩展指令。这类指令在实现过程中可以使用Debugger Engine的API,EngExtCpp 扩展框架提供的接口,同时也能使用WdbgExts API。(示例代码名称:extcpp)
- WdbgExts extension DLLs。这种类型基于wdbgexts.h头文件提供的接口开发并导出DbgEng 扩展指令。指令开发中只能使用WdbgExts API。(示例代码名称:simplext)
2. EngExtCpp扩展指令开发
EngExtCpp扩展指令是按照C++的语法进行开发,下面的内容将主要介绍如何开发这种类型的指令。在windbg官方帮助文档中有名为EngExtCpp Extension Design Guide的专题对EngExtCpp扩展指令的开发做了一定的介绍,不一定适用,但是可以参考其中的基本概念。
2.1 创建完整的EngExtCpp工程
按照windbg官方帮助文档提供的开发方案,扩展插件需要在DDK环境编译,显然比较麻烦。而且按照它的方法,我用DDK没有成功编译过它所提供的示例代码。事实上,在VC环境中照样能够开发出扩展指令。下面就以在VS2008中开发一个导出CMap内容的扩展指令cmap为例,说明主要的过程。
1)创建一个空白的win32 DLL工程。
2)向工程添加engextcpp.hpp和engextcpp.cpp两个现有文件,文件所在路径位于Windbg安装目录下sdk\sample\inc。按照Windbg帮助文档的说明,以及官方示例代码的内容显示,原本我们只需要包含头文件engextcpp.hpp,并连接engextcpp.lib静态库就能完成开发,但是事实上直接使用静态库在连接过程中会出现错误,并不能成功编译,所以我选择了直接包含engextcpp.cpp源文件的方法,而不去连接engextcpp.lib。
3)向工程中添加一个新建的cpp文件,命名为mtlkit.cpp。这个文件的内容就是我们实现扩展指令cmap我们需要自己开发的代码,其内容如下:
//----------------------------------------------------------------------------
//
// extcpp.cpp
//
// EngExtCpp-style extension sample.
//
// Copyright (C) Microsoft Corporation, 2005.
//
//----------------------------------------------------------------------------
#include <engextcpp.hpp>
#include <stdio.h>
#include <string>
using std::string;
//----------------------------------------------------------------------------
//
// Base extension class.
// Extensions derive from the provided ExtExtension class.
//
// The standard class name is "Extension". It can be
// overridden by providing an alternate definition of
// EXT_CLASS before including engextcpp.hpp.
//
//----------------------------------------------------------------------------
class EXT_CLASS : public ExtExtension
{
public:
EXT_COMMAND_METHOD(cmap);
//
private:
void OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur);
void DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize);
};
// EXT_DECLARE_GLOBALS must be used to instantiate
// the framework's assumed globals.
EXT_DECLARE_GLOBALS();
void EXT_CLASS::DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize)
{
for (LONG i=0; i<hashTableSize; i++)
{
ExtRemoteTyped tableElement = hashTable[i];
while (tableElement.GetPtr() != 0)
{
ExtRemoteTyped key = tableElement.Field("key");
ExtRemoteTyped value = tableElement.Field("value");
Warn("== key======>\n");
OutUnfold(key, 5, 5);
Warn("\n== value=====>\n");
OutUnfold(value, 5, 5);
Out("\n");
tableElement = tableElement.Field("pNext");
}
}
}
void EXT_CLASS::OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur)
{
CHAR Name[MAX_PATH];
if (nRecur <= 0)
{
return;
}
if (nRecur == nOrgRecur)
{
string strType = node.GetTypeName();
if (strType.find("class") != string::npos &&
strType.find("*") != string::npos)
{
dprintf("%s : %s\n", node.GetTypeName(), node.GetSimpleValue());
}
else
{
dprintf("%s :\n", node.GetTypeName());
}
}
for (int i=0; ;i++)
{
HRESULT Hr;
ULONG Offset=0;
Hr = m_Symbols2->GetFieldName(node.m_Typed.ModBase, node.m_Typed.TypeId, i, Name, MAX_PATH, NULL);
if (Hr == S_OK)
{
m_Symbols2->GetFieldOffset(node.m_Typed.ModBase, node.m_Typed.TypeId, Name, &Offset);
ExtRemoteTyped nodefield;
try
{
nodefield = node.Field(Name);
for(int i=0; i<nOrgRecur-nRecur; i++)
{
dprintf(" ");
}
dprintf("+%03lx %s %10s %s\n", Offset, Name, ":", nodefield.GetSimpleValue());
OutUnfold(nodefield, nRecur-1, nOrgRecur);
}
catch (.../*ExtException e*/)
{
//dprintf("excp :%s\n", e.GetMessage());
//dprintf("Exception OutUnfold \n");
}
}
else if (Hr == E_INVALIDARG)
{
if (i == 0 && nRecur == nOrgRecur)
{
node.OutSimpleValue();
}
// All Fields done
break;
}
else
{
dprintf("GetFieldName Failed %lx\n", Hr);
break;
}
}
}
EXT_COMMAND(cmap,
"\n-Description: \n"
"- Dump the content of a specific CMap (VC9).\n"
"-Usage:"
" Directly use the name of a map variate, eg."
" !cmap NameOfMapVar",
"{;x;arg;argument}")
{
ExtRemoteTyped mtlmap;
//dprintf("raw:%s\n",GetRawArgStr());
string strRawArgs = GetRawArgStr();
mtlmap.Set(strRawArgs.c_str());
mtlmap.OutFullValue();
Out("\n");
ExtRemoteTyped mapsize = mtlmap.Field("m_nCount");
ExtRemoteTyped hashTable = mtlmap.Field("m_pHashTable");
ExtRemoteTyped hashTableSize = mtlmap.Field("m_nHashTableSize");
Out("\n----------------------------------\n");
Warn("Map size: %s", mapsize.GetSimpleValue());
Out("\n----------------------------------\n");
Out("%s , %s",
mtlmap.Field("m_pFreeList.key").GetTypeName(),
mtlmap.Field("m_pFreeList.value").GetTypeName());
Out("\n----------------------------------\n");
//
DumpCmap(hashTable, hashTableSize.GetLongPtr());
}
//
// extcpp.cpp
//
// EngExtCpp-style extension sample.
//
// Copyright (C) Microsoft Corporation, 2005.
//
//----------------------------------------------------------------------------
#include <engextcpp.hpp>
#include <stdio.h>
#include <string>
using std::string;
//----------------------------------------------------------------------------
//
// Base extension class.
// Extensions derive from the provided ExtExtension class.
//
// The standard class name is "Extension". It can be
// overridden by providing an alternate definition of
// EXT_CLASS before including engextcpp.hpp.
//
//----------------------------------------------------------------------------
class EXT_CLASS : public ExtExtension
{
public:
EXT_COMMAND_METHOD(cmap);
//
private:
void OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur);
void DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize);
};
// EXT_DECLARE_GLOBALS must be used to instantiate
// the framework's assumed globals.
EXT_DECLARE_GLOBALS();
void EXT_CLASS::DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize)
{
for (LONG i=0; i<hashTableSize; i++)
{
ExtRemoteTyped tableElement = hashTable[i];
while (tableElement.GetPtr() != 0)
{
ExtRemoteTyped key = tableElement.Field("key");
ExtRemoteTyped value = tableElement.Field("value");
Warn("== key======>\n");
OutUnfold(key, 5, 5);
Warn("\n== value=====>\n");
OutUnfold(value, 5, 5);
Out("\n");
tableElement = tableElement.Field("pNext");
}
}
}
void EXT_CLASS::OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur)
{
CHAR Name[MAX_PATH];
if (nRecur <= 0)
{
return;
}
if (nRecur == nOrgRecur)
{
string strType = node.GetTypeName();
if (strType.find("class") != string::npos &&
strType.find("*") != string::npos)
{
dprintf("%s : %s\n", node.GetTypeName(), node.GetSimpleValue());
}
else
{
dprintf("%s :\n", node.GetTypeName());
}
}
for (int i=0; ;i++)
{
HRESULT Hr;
ULONG Offset=0;
Hr = m_Symbols2->GetFieldName(node.m_Typed.ModBase, node.m_Typed.TypeId, i, Name, MAX_PATH, NULL);
if (Hr == S_OK)
{
m_Symbols2->GetFieldOffset(node.m_Typed.ModBase, node.m_Typed.TypeId, Name, &Offset);
ExtRemoteTyped nodefield;
try
{
nodefield = node.Field(Name);
for(int i=0; i<nOrgRecur-nRecur; i++)
{
dprintf(" ");
}
dprintf("+%03lx %s %10s %s\n", Offset, Name, ":", nodefield.GetSimpleValue());
OutUnfold(nodefield, nRecur-1, nOrgRecur);
}
catch (.../*ExtException e*/)
{
//dprintf("excp :%s\n", e.GetMessage());
//dprintf("Exception OutUnfold \n");
}
}
else if (Hr == E_INVALIDARG)
{
if (i == 0 && nRecur == nOrgRecur)
{
node.OutSimpleValue();
}
// All Fields done
break;
}
else
{
dprintf("GetFieldName Failed %lx\n", Hr);
break;
}
}
}
EXT_COMMAND(cmap,
"\n-Description: \n"
"- Dump the content of a specific CMap (VC9).\n"
"-Usage:"
" Directly use the name of a map variate, eg."
" !cmap NameOfMapVar",
"{;x;arg;argument}")
{
ExtRemoteTyped mtlmap;
//dprintf("raw:%s\n",GetRawArgStr());
string strRawArgs = GetRawArgStr();
mtlmap.Set(strRawArgs.c_str());
mtlmap.OutFullValue();
Out("\n");
ExtRemoteTyped mapsize = mtlmap.Field("m_nCount");
ExtRemoteTyped hashTable = mtlmap.Field("m_pHashTable");
ExtRemoteTyped hashTableSize = mtlmap.Field("m_nHashTableSize");
Out("\n----------------------------------\n");
Warn("Map size: %s", mapsize.GetSimpleValue());
Out("\n----------------------------------\n");
Out("%s , %s",
mtlmap.Field("m_pFreeList.key").GetTypeName(),
mtlmap.Field("m_pFreeList.value").GetTypeName());
Out("\n----------------------------------\n");
//
DumpCmap(hashTable, hashTableSize.GetLongPtr());
}
4)添加一个新建的def文件,命名为mtlkit.def。这个文件定义扩展DLL中需要导出的接口,也就是支持的扩展指令名字。其内容如下:
;--------------------------------------------------------------------
; Copyright (c) 2005 Microsoft Corporation
;
;Module:
; extcpp.def
;--------------------------------------------------------------------
EXPORTS
;--------------------------------------------------------------------
; Core exports provided by the ExtCpp framework.
;--------------------------------------------------------------------
DebugExtensionInitialize
DebugExtensionUninitialize
DebugExtensionNotify
help
;--------------------------------------------------------------------
; Extension commands.
;--------------------------------------------------------------------
cmap
; Copyright (c) 2005 Microsoft Corporation
;
;Module:
; extcpp.def
;--------------------------------------------------------------------
EXPORTS
;--------------------------------------------------------------------
; Core exports provided by the ExtCpp framework.
;--------------------------------------------------------------------
DebugExtensionInitialize
DebugExtensionUninitialize
DebugExtensionNotify
help
;--------------------------------------------------------------------
; Extension commands.
;--------------------------------------------------------------------
cmap
5)按照实际情况配置工程属性。
C++编译配置中,程序文件的包含路径中要添加上Windbg sdk的源码所在目录,也就是engextcpp.hpp和engextcpp.cpp两个文件的所在目录。
连接配置中,添加模块定义文件名称mtlkit.def,以表明需要使用def文件。
6)最后还要注意工程常规属性里面要选择多字节集和静态使用MFC库。当然,根据自己的需要这些设置是可以调整的。
7)至此,一个完整的windbg EngExtCpp扩展DLL工程就建立起来了,只需要只需编译就能得到,一个名为mtlkit.dll的动态库文件。之后只要将mtlkit.dll拷贝到windbg安装目录下的winext子目录中,我们就能在windbg中使用cmap指令了。使用方法可以是!mtlkit.cmap,也可以先用.load mtlkit加载动态库,之后就能直接使用!cmap指令了。
最后需要注意的是,以上编译得到的是32位DLL,只能在32位的windbg上使用,64位windbg需要加载64位的扩展DLL才能正常使用。
2.2 EngExtCpp开发代码规则
下面以mtlkit.cpp的内容为例,介绍EngExtCpp扩展指令开发基本的规则。
包含头文件
#include <engextcpp.hpp>
要使用EngExtCpp框架所提供的接口,显然需要包含它提供的头文件。
类声明的规矩
class EXT_CLASS : public ExtExtension
{
public:
EXT_COMMAND_METHOD(cmap);
//
private:
void OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur);
void DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize);
};
// EXT_DECLARE_GLOBALS must be used to instantiate
// the framework's assumed globals.
EXT_DECLARE_GLOBALS();
{
public:
EXT_COMMAND_METHOD(cmap);
//
private:
void OutUnfold( ExtRemoteTyped& node, int nRecur, int nOrgRecur);
void DumpCmap(ExtRemoteTyped& hashTable, INT_PTR hashTableSize);
};
// EXT_DECLARE_GLOBALS must be used to instantiate
// the framework's assumed globals.
EXT_DECLARE_GLOBALS();
我们自定义的类通常会继承EngExtCpp框架所定义的ExtExtension类,在这个类的内部将实现扩展指令的所有功能。自定义类的类名通常必须是EXT_CLASS,因为EngExtCpp框架在实现时,声明了一些变量,而这些变量固定使用了EXT_CLASS这样一个符号。可以参看EXT_DECLARE_GLOBALS()宏的定义。
类名EXT_CLASS实际上也是一个宏,定义在文件engextcpp.hpp中,具体的定义为:
#ifndef EXT_CLASS
#define EXT_CLASS Extension
#endif
#define EXT_CLASS Extension
#endif
也就是说,我们自定义类的真实名字默认情况下是Extension。如果我们需要改变真实类名,则需要在#include <engextcpp.hpp>之前,对EXT_CLASS进行#define操作,否则可能无效或报错。
自定义类中的函数声明同普通的C++类基本一致。唯一不同的是扩展指令必须用EXT_COMMAND_METHOD宏申明为public方法。
public:
EXT_COMMAND_METHOD(cmap);
EXT_COMMAND_METHOD(cmap);
上面的代码,声明了一个叫做cmap的public方法,cmap方法是用EXT_COMMAND_METHOD宏声明的,因此它就是一个扩展指令。
指令函数实现的规矩
相应的,用EXT_COMMAND_METHOD宏声明的函数,其实现也不同于普通的C++类函数,实现时必须用EXT_COMMAND宏引导:
EXT_COMMAND(cmap,
"\n-Description: \n"
"- Dump the content of a specific CMap (VC9).\n"
"-Usage:"
" Directly use the name of a map variate, eg."
" !cmap NameOfMapVar",
"{;x;arg;argument}")
{
ExtRemoteTyped mtlmap;
//dprintf("raw:%s\n",GetRawArgStr());
string strRawArgs = GetRawArgStr();
mtlmap.Set(strRawArgs.c_str());
mtlmap.OutFullValue();
Out("\n");
ExtRemoteTyped mapsize = mtlmap.Field("m_nCount");
ExtRemoteTyped hashTable = mtlmap.Field("m_pHashTable");
ExtRemoteTyped hashTableSize = mtlmap.Field("m_nHashTableSize");
Out("\n----------------------------------\n");
Warn("Map size: %s", mapsize.GetSimpleValue());
Out("\n----------------------------------\n");
Out("%s , %s",
mtlmap.Field("m_pFreeList.key").GetTypeName(),
mtlmap.Field("m_pFreeList.value").GetTypeName());
Out("\n----------------------------------\n");
//
DumpCmap(hashTable, hashTableSize.GetLongPtr());
}
"\n-Description: \n"
"- Dump the content of a specific CMap (VC9).\n"
"-Usage:"
" Directly use the name of a map variate, eg."
" !cmap NameOfMapVar",
"{;x;arg;argument}")
{
ExtRemoteTyped mtlmap;
//dprintf("raw:%s\n",GetRawArgStr());
string strRawArgs = GetRawArgStr();
mtlmap.Set(strRawArgs.c_str());
mtlmap.OutFullValue();
Out("\n");
ExtRemoteTyped mapsize = mtlmap.Field("m_nCount");
ExtRemoteTyped hashTable = mtlmap.Field("m_pHashTable");
ExtRemoteTyped hashTableSize = mtlmap.Field("m_nHashTableSize");
Out("\n----------------------------------\n");
Warn("Map size: %s", mapsize.GetSimpleValue());
Out("\n----------------------------------\n");
Out("%s , %s",
mtlmap.Field("m_pFreeList.key").GetTypeName(),
mtlmap.Field("m_pFreeList.value").GetTypeName());
Out("\n----------------------------------\n");
//
DumpCmap(hashTable, hashTableSize.GetLongPtr());
}
EXT_COMMAND宏的原型为:
EXT_COMMAND( _Name, _Desc, _Args );_Name:指令的名称_Desc:指令的描述信息_Args:指令参数的描述信息
(指定windbg指令后面参数的属性,如参数是数字还是字符串等等,可以参考windbg帮助文档中的主题
Parsing Extension Arguments)
(指定windbg指令后面参数的属性,如参数是数字还是字符串等等,可以参考windbg帮助文档中的主题
Parsing Extension Arguments)
.def文件说明
def文件最后一行的cmap就是我们扩展指令的名字,同时也是自定义类中导出函数名字。必须把cmap添加到def文件中,声明cmap函数是mtlkit.dll中的一个导出函数才能在windbg中调用cmap指令。另外,def文件中help等4个导出函数的名称是由开发库默认导出的接口。
代码简单说明
OutUnfold函数:由于变量可能不是基本数据类型,而是复杂的类,在导出类的时候就需要按层次展开类中的结构。OutUnfold函数指定展开的层数,对数据内容做输出。
DumpCmap函数:根据CMap的存储结构,导出其中的key以及key对应的value。CMap实际上是一个hash表,具体的实现可以参考它的源码。
ExtRemoteTyped类:一个由EngExtCpp框架提供的很方便的类,在调试器成功加载程序符号表的情况下,ExtRemoteTyped可以根据符号信息对一段给定的内存做解析,使我们可以方便提取内存中数据的内容、数据类型等信息。具体情况可以参考windbg帮助文档的说明,也可以分析它的源码对它做进一步了解。
Out,Warn,dprintf:都是格式化输出函数,Out和Warn是由EngExtCpp对DbgEng输出接口的封装,dprintf是wdbgexts对DbgEng接口的封装。Out输出的信息是提示性的文字,Warn是警告性的文字,通过对Windbg的输出格式配置,可以设置不同类型文本显示不同的颜色。本代码中使用Warn函数仅仅是演示,没有其他更多含义。另外,需要了解更多的格式化输出函数,可以参考windbg的帮助文档,或者直接分析开发库的源码。
以上完整的工程文件可以在此处下载:http://download.csdn.net/detail/yichigo/9672868
为了避免windbg版本差异引起不必要的问题,我已经将一个windbg资源添加到了工程文件中,可在此基础上学习开发,并直接在其中使用开发出的扩展库。
3.参考文档
以下都是windbg官方帮助文档中的主题,对于学习windbg扩展指令开发都有帮助。
(1)Introduction
(2)Parsing Extension Arguments
(3)EngExtCpp Extension Design Guide
(4)Using Debugger Extension Commands
(4)Using Debugger Extension Commands
(5)WdbgExts Extension Design Guide
(6)DbgEng Extension Design Guide
0 0
- Windbg扩展指令开发教程
- windbg指令
- Chrome扩展开发教程
- WinDBG教程
- WinDBG教程
- WinDBG教程
- WinDBG教程
- windbg指令-!drvobj
- WinDBG 指令整理
- WinDBG之SOSEX扩展
- 编写 windbg 扩展
- 编写WinDbg扩展
- windbg扩展功能
- 如何扩展windbg
- windbg扩展python脚本
- WinDbg命令详解--汇编指令
- WINDBG Script简易教程
- [转载]windbg简明教程
- 公司内局域网中的redis缓存和sock线程池,不在局域内运行程序怎么处理
- [BZOJ4521][Cqoi2016]手机号码(数位dp)
- Xilinx & Altera FPGA中的逻辑资源(Slices VS LE)比较
- 业余01-MATLAB之M文件双击没反应修复方法
- Python基础习题(二)
- Windbg扩展指令开发教程
- 一天一条Linux指令-reset
- C/C++笔试必须熟悉掌握的头文件系列(九)——string
- AOP(1)
- 电源地与信号地关系的详细解析
- WebView详解一:打造优雅的WebView
- 01-多线程01
- [BZOJ1179][Apio2009]Atm(tarjan+spfa)
- shiro认证过程Authenticator到realm,然后进行认证的过程