0

バックグラウンド:

Windows COM オブジェクトに接続しています。

使用される方法は vtable の変更です。instanceという名前のインターフェイス Aのインスタンスがあり、インターフェイスにoldmethodが含まれているとします。これをnewmethodに置き換えました。ただし、私のnewmethodでは、oldmethodのアドレスを知る必要があります。これにより、独自の処理を行った後に oldmethod呼び出すことができます。

インターフェース A の背後に複数の実装がある可能性があるため、oldmethodのアドレスをグローバル変数に格納することは安全ではありません。たとえば、クラス A1クラス A2の 2 つの実装があるとします。したがって、私のnewmethodは A1- >oldmethodA2->oldmethodの両方を格納し、インスタンス タイプに基づいて適切な関数を呼び出す必要があります。

  • これを達成する 1 つの方法は、(vtable のアドレス -> oldmethod) を格納するマップを保持することです。vtable のアドレスは、クラス A1 とクラス A2 の区別として機能するためです。私のnewmethodでは、現在のインスタンスの正しいoldmethodについてマップがチェックされます。ただし、これにより、プログラムは毎回マップをチェックするようになり、コストがかかります。また、マップのスレッド セーフはコストを増加させます。

  • もう 1 つの方法は、クロージャを作成することです。実行可能メモリのチャンクを割り当て、内部にnewmethodのバイナリ コードを書き込みます (最小サイズに縮小できるため、サイズは問題になりません)。各インスタンスのバイナリ コードでoldmethodのアドレスを変更します。この場合、マップ コストでの検索はありません。

質問 1 :

2 番目の方法はこれを行う安全な方法ですか、それとも最初の方法の方が優れていますか? それらのいずれかに潜在的な安全上の問題はありますか?

質問 2 :

2 番目の方法では、作成したクロージャーにクラス固有のデータ (oldmethod ポインター) が含まれています。インスタンス固有のデータをnewmethodに保存する必要がある場合、(this pointer -> data) マップを保持する以外の戦略はありますか? 私は最善を尽くしましたが、方法が見つかりませんでした。

4

2 に答える 2

1

クラス A1 のソースを持っていない可能性がありますが、インスタンス化するタイミングを制御していますか ("new"、CoCreateInstance、またはその他のファクトリ関数によって)。その場合は、インターフェイス A を実装するクラスを実装し、インターフェイス A のすべての呼び出しを実際のオブジェクトに転送し、関心のあるメソッドをインターセプトします。

以下の例では、置換の例を示します

class InterfaceA : public IUnknown
{
public:

    virtual int M1() = 0;
    virtual int M2(int x, int y) = 0;
    virtual int M3() = 0;
};


class CMyWrapperClass : public InterfaceA
{
public:

    int _refcount;
    InterfaceA* _pInner;

    CSomeClass2(InterfaceA* pInner)
    {
        _pInner = pInner;
        _pInner->AddRef();
        _refcount = 1;
    }

    ~CSomeClass2()
    {
        _pInner->Release();
    }

    virtual int M1() {return _pInner->M1();}
    virtual int M2(int x, int y)  {printf("CSomeClass2::M2(x=%d, y=%d)\n", x, y); return _pInner->M2(x,y);  }
    virtual int M3() {return _pInner->M3();}

    // not shown - addRef, release, queryinterface
};


   // example instantiation
   hr = CoCreateInstance(CLSID_A1, NULL, CLXCTX_ALL, IID_InterfaceA, (void**)&pInterfaceA);

   // now do the wrap
   pInterfaceA = new CMyWrapperClass(pInterfaceA);

ホットパッチを適用しようとしているクラスのインスタンス化を制御できない場合は、共有するコードがあります。しかし、明らかにもう少し複雑です。これが機能しない場合は、COM vtable のホットパッチに直接関連する別の回答を投稿します。

于 2013-02-17T11:04:07.140 に答える
0

この問題を理解するのにかなりの時間がかかりました。私は実際に、vtableにパッチを適用してから、ラッパークラスで元のメソッドを呼び出す方法を示すために考えたコードのかなりの部分を書きました。vtableへのパッチ適用は簡単です。

そして、私はあなたが言及している問題を発見しました。そして、パッチが適用されたvtableメソッドが呼び出されると(newmethod)、別のクラス内で定義されている場合でも、「this」は元のオブジェクトであり、どのインスタンスが呼び出されているかについてのコンテキストはありません。したがって、メンバー変数を簡単に参照して、保存した「oldmethod」に戻ることはできません。

ですから、少し考えてみると、これを行うには世界地図が最も安全なアプローチだと思います。マップ内の関数ポインタの挿入、削除、または検索を保護するために必要なのは、ロック(critical_section)だけです。マップから安全に取得した後、古いメソッドを呼び出すときにロックを保持する必要はないでしょう。そのため、この操作を実行する際の実行時のオーバーヘッドはごくわずかです。

于 2013-02-17T23:02:09.730 に答える