DLLをサードパーティの実行可能ファイル(Qtフレームワークを使用して構築されたもの)に挿入するC#アプリケーションを作成しました。このDLLは、EasyHookを使用して、いくつかの特定の関数への呼び出しをインターセプトします。挿入されたコードが呼び出されると、これらの関数のパラメーターであるオブジェクトのいくつかを検査しようとします。
たとえば、XMLを解析するために行われた呼び出しをインターセプトしました。
virtual bool __thiscall QXmlSimpleReader::parse(class QXmlInputSource const &)
私のC#コードには、これに一致するPInvoke署名があります。
static extern bool XmlParse(IntPtr Reader, IntPtr Source)
「Source」クラスのメンバーである「data()」関数を呼び出したいと思います。つまり、QXmlSimpleReaderはQXmlInputSourceから生のXMLを解析しますが、その前に、この「data()」関数を介して生のXMLを検査しようとしています。
ここの専門家の1人のアドバイスで、私はC ++ / CLIを使用してオブジェクトにネイティブにアクセスしようとしました(サードパーティのDLLのメソッドの呼び出しを参照)。C#コードからIntPtrを受け入れるC++のラッパーオブジェクトを作成しました。
ヘッダ:
public ref class QXmlInputSource
{
public:
// Constructor must be called with C# IntPtr
QXmlInputSource(IntPtr Ptr);
bool LoadQt(void);
bool UnloadQt(void);
String^ Data();
private:
// Pointer to the native Qt object
void * Native;
HINSTANCE DllHandle;
// SIGNATURE: virtual QString QXmlInputSource::data() const
typedef void * (__thiscall *QXmlInputSource_Data)(void *);
QXmlInputSource_Data fpData;
};
CPPファイル:
QXmlInputSource::QXmlInputSource(IntPtr Ptr)
{
LoadQt();
Native = Ptr.ToPointer();
}
bool QXmlInputSource::LoadQt(void)
{
FARPROC Addr;
/* get handle to dll */
std::wstring LibName = QtPath + QtXml;
DllHandle = LoadLibrary(LibName.c_str());
/* get pointer to the function in the dll*/
Addr = GetProcAddress(HMODULE (DllHandle), "?data@QXmlInputSource@@UBE?AVQString@@XZ");
fpData = QXmlInputSource_Data(Addr);
return true;
}
bool QXmlInputSource::UnloadQt()
{
/* Release the Dll */
FreeLibrary(DllHandle);
return true;
}
String^ QXmlInputSource::Data()
{
void* Ptr = fpData(Native);
return "EPIC FAIL";
}
fpData()関数ポインタを呼び出そうとすると、Qtベースのアプリケーションがクラッシュします。ヘルプ:P
役立つ場合と役に立たない場合があるいくつかの追加情報:
同じ方法を使用して、QString.count()やQString.data()などの「より単純な」オブジェクトの関数を正常に呼び出しました。(QStringは、標準のUnicode文字列の単なる軽量ラッパーのようです)。
私が興味を持っているXML関数を含むQtXml4.dllファイルには、実際には2つのparse()メソッドがあります。1つはSourceがconst&であり、もう1つはSourceがconst*です。どちらを使うべきかわかりません。いずれにしても私の署名は変わらないと思います。
遊んでいる間、C#コードでIntPtrを逆参照して、C++に渡してみました。
IntPtr DerefSrc =(IntPtr)Marshal.PtrToStructure(Source、typeof(IntPtr));
これら2つのIntPtrの値を出力すると、Sourceの値は約3.5Mbですが、DerefSrcの値は1.6Gbです。これは、メモリ内のQtXml4.dllのアドレスとほぼ一致します。私の推測では、3.5Mbは相対オフセットですが、DerefSrcは明らかに絶対参照です。DerefSrcを相対アドレスに変換し、代わりにC ++に渡すことは一撃の価値があります...?