WindBG拡張機能のダンプファイルメモリに基づいてオブジェクトを作成するにはどうすればよいですか?
-
24-09-2019 - |
質問
私は大規模なアプリケーションに取り組んでおり、WindBGを使用して顧客からのDMPファイルに基づいて問題を診断します。私は、DMPファイルから大量の情報を引き出すのに非常に役立つWindBGの小さな拡張機能をいくつか書きました。私の拡張コードでは、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ファンキーさを引っ張っていますか?私のコードはこれに似ています:
SomeClass* thisClass = SomeClass::New();
ReadMemory(addressOfObj, &(*thisClass), sizeof(*thisClass), &cb);
フォローアップ:engextcppから極端に伸びた可能性があるように見えます。誰かがこれをうまく使用しましたか?いくつかのサンプルコードをGoogleで検索する必要がありますが、あまり運がありません。
フォローアップ2:私はこれに関する2つの異なる調査ルートを追求しています。
1)ExtremoteTypedを検討していますが、このクラスは本当にReadMemory/GetFieldOffsetコールのヘルパーにすぎないようです。はい、それは物事を大いにスピードアップするのに役立ちますが、DMPファイルからオブジェクトを再現することに関しては本当に役に立ちません。ドキュメントはスリムですが、私は何かを誤解しているかもしれません。 2)ReadMemoryを使用して、DMPファイルのデータを使用して拡張機能で作成されたオブジェクトを上書きしようとしています。ただし、上記のようにsizeof(*thisclass)を使用するのではなく、データ要素のみを選択して、vtablesのままにしておくと思っていました。
解決
興味深いアイデアですが、これは最も単純なオブジェクトのみで作業することを望んでいます。たとえば、オブジェクトに他のオブジェクト(またはvtable)へのポインターまたは参照が含まれている場合、それらは新しいアドレススペースにあまりコピーされません。
ただし、「プロキシ」オブジェクトを取得して、プロキシメソッドを呼び出すときに適切な呼び出しを行うことができるかもしれません。 ReadMemory()
情報を取得します。これはかなりの作業であるように聞こえますが、あなたがプロキシしたいクラスごとに多かれ少なかれカスタムコードセットでなければならないと思います。おそらくこれを進めるためのより良い方法がありますが、それが私の頭の上から来たものです。
他のヒント
私は最初の予感を追って、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ファイルからより多くの情報を取得するために、必要に応じて特定のオーバーライドを追加します。この手法により、これらの新しいリモートオブジェクトを、さまざまなユーティリティ関数で処理するための元のソースコードに戻すことができます。
どういうわけかこれをテンプレート化できるはずです...しかし、各クラスがわずかに異なって実装されているのは何らかの理由が常にあるようです。たとえば、より複雑なオブジェクトにはいくつかのvtableがあります。スキップ。
メモリダンプを取得することは常に診断のための情報を取得する方法であったことを知っていますが、ETWはより簡単に簡単になり、情報システムの呼び出しやユーザーコードを含むコールスタックとともに情報を取得できます。 MSは、Windowsやvs.Netを含むすべての製品に対してこれを行っています。
これは、デバッグの邪魔にならない方法です。私は非常に長い間同じデバッグをしましたが、今ではETWでデバッガー内で多くの時間を費やすことなく、ほとんどの顧客の問題を解決することができます。これらは私の2セントです。
WindBGのGDIリークトレーサー拡張機能をハッキングするとき、私は同様の何かにアプローチしました。クライアントのデータストレージにSTLコンテナを使用し、拡張機能からデータを通過する方法が必要でした。最終的には、極端なタイプを使用して延長側に直接必要なhash_mapの部分を実装することになりましたが、それは満足のいくものでしたが、しばらくかかりました; o)ここ ソースコードです。