我处理大型应用程序,并经常使用WinDbg根据客户的DMP文件诊断问题。我已经为WinDbg编写了一些小扩展,这些扩展已被证明对于从DMP文件中提取信息非常有用。在我的扩展代码中,我发现自己一遍又一遍地手动取消引用c ++类对象。例如:
Address = GetExpression("somemodule!somesymbol");
ReadMemory(Address, &addressOfPtr, sizeof(addressOfPtr), &cb);
// get the actual address
ReadMemory(addressOfObj, &addressOfObj, sizeof(addressOfObj), &cb);
ULONG offset;
ULONG addressOfField;
GetFieldOffset("somemodule!somesymbolclass", "somefield", &offset);
ReadMemory(addressOfObj+offset, &addressOfField, sizeof(addressOfField), &cb);
这很好用,但是由于我已经编写了更多扩展,具有更强大的功能(以及在我们的应用程序DMP文件中访问更复杂的对象),我渴望得到更好的解决方案。我当然可以访问我们自己的应用程序的源代码,所以我认为应该有一种方法可以从DMP文件中复制一个对象并使用该内存在调试器扩展中创建一个我可以调用函数的实际对象(通过我们的应用程序中的dll链接)。这样可以省去手工拉出DMP的麻烦。
这甚至可能吗?我尝试了一些显而易见的事情,比如在扩展中创建一个新对象,然后直接从DMP文件用一个大的ReadMemory覆盖它。这似乎把数据放在了正确的字段中,但当我试图调用函数时吓坏了。我想我错过了一些东西...也许c ++拉出一些我不知道的vtable funky-ness?我的代码看起来与此类似:
SomeClass* thisClass = SomeClass::New();
ReadMemory(addressOfObj, &(*thisClass), sizeof(*thisClass), &cb);
FOLLOWUP:看起来像EngExtCpp的POSSIBLY ExtRemoteTyped是我想要的吗?有没有人成功使用过这个?我需要谷歌一些示例代码,但没有太多运气。
关注2:我正在研究两种不同的调查途径 1)我正在研究ExtRemoteTyped,但看起来这个类实际上只是ReadMemory / GetFieldOffset调用的帮助器。是的,这将有助于加速ALOT,但在从DMP文件重新创建对象时并没有真正帮助。虽然文档很小,但我可能会误解一些东西。 2)我也在尝试使用ReadMemory用来自DMP文件的数据覆盖在我的扩展中创建的对象。但是,我没有像上面那样使用sizeof(* thisClass),而是认为我只会选择数据元素,并保持vtable不变。
答案 0 :(得分:1)
有趣的想法,但这只能用于最简单的对象。例如,如果对象包含指向其他对象(或vtable)的指针或引用,那么这些对象将无法很好地复制到新的地址空间。
但是,您可以获得一个“代理”对象,当您调用代理方法时,它们会对ReadMemory()
进行适当的调用以获取信息。这听起来是相当有意义的工作,我认为它必须或多或少是您想要代理的每个类的自定义代码集。可能有更好的方法来解决这个问题,但这就是我头脑中的问题。
答案 1 :(得分:1)
我最后只是跟随我的初步预感,并将数据从dmp文件复制到一个新对象中。我通过制作像这样的远程包装器对象来做得更好:
class SomeClassRemote : public SomeClass
{
protected:
SomeClassRemote (void);
SomeClassRemote (ULONG inRemoteAddress);
public:
static SomeClassRemote * New(ULONG inRemoteAddress);
virtual ~SomeClassRemote (void);
private:
ULONG m_Address;
};
在实施中:
SomeClassRemote::SomeClassRemote (ULONG inRemoteAddress)
{
ULONG cb;
m_Address = inRemoteAddress;
// copy in all the data to the new object, skipping the virtual function tables
ReadMemory(inRemoteAddress + 0x4, (PVOID) ((ULONG)&(*this) +0x4), sizeof(SomeClass) - 4, &cb);
}
SomeClassRemote::SomeClassRemote(void)
{
}
SomeClassRemote::~SomeClassRemote(void)
{
}
SomeClassRemote* SomeClassRemote::New(ULONG inRemoteAddress)
{
SomeClassRemote*x = new SomeClassRemote(inRemoteAddress);
return (x);
}
这是基础知识,但随后我会根据需要添加特定的覆盖来从dmp文件中获取更多信息。这种技术允许我将这些新的远程对象传递回原始源代码,以便在各种实用程序函数中进行处理,因为它们是从原始类派生的。
确定SEEMS就像我应该能够以某种方式模板化......但似乎总有一些原因,每个类都以不同的方式实现,例如我们的一些更复杂的对象有几个vtable,两者都是必须被跳过。
答案 2 :(得分:0)
我知道获取内存转储一直是获取诊断信息的方法,但是使用ETW会更容易,并且您可以获得包含信息系统调用和用户代码的调用堆栈的信息。 MS一直在为包括Windows和VS.NET在内的所有产品执行此操作。
这是一种非侵入式的调试方式。我已经完成了相同的调试很长时间,现在使用ETW,我能够解决大多数客户问题,而无需在调试器中花费大量时间。这是我的两分钱。
答案 3 :(得分:0)
在为windbg攻击gdi泄漏示踪剂扩展时,我找到了类似的东西。我在客户端使用stl容器进行数据存储,并且需要一种遍历扩展数据的方法。我最后使用ExtRemoteTyped直接在扩展端实现了我需要的hash_map部分,这是令人满意的,但我花了一些时间才弄明白; o) Here是源代码。