2012-05-08 25 views
0

私は最近、迂回路とそれに付随するすべてのものでプログラミングしてきました。私は多くの異なる機能を迂回してきました。 thiscall、stdcall、cdecl、仮想関数などです。しかし、私が管理していない(可能ではないかもしれない)ことがあり、それは基本クラスの仮想関数をフックすることです。例えば;仮想関数(空)を宣言するCarクラスがありますDrive。その後、車を継承し、Driveを実装する他の3つのクラスがあります。仮想関数と迂回

私は、彼らがないコール基本機能をすれば、彼らは、Driveトリガーとき、それはCarの子孫によってトリガーされるだろう(簡単な「JMP」フックを使用して)Drive機能を車の(基本クラス)をフックする場合は?

さらに徹底的に説明すると:

class Car 
{ 
    virtual void Drive(void) { } // Empty virtual function 
} 

class Lamborghini : public Car 
{ 
    void Drive(void) { // does lots of stuff, but does NOT call base function } 
} 

をだから私は基本メソッドは、それが何らかの形でフックすることができるかどうかは、またはと呼ばれる取得するかどうかを思ったんだけど?関数の実行がに直接からLamborghini::Driveにジャンプするか、どういうわけかCarクラスを通過するので、子孫がDriveを呼び出すたびに検出可能ですか?

EDIT:基本クラスの関数が空の場合、5バイトのスペースが必要なので、フックすることも可能ですか?

+0

http://en.wikipedia.org/wiki/Virtual_method_table、それはあなたが記述しているものもないので、これは役立つかもしれない:http://code.google.com/p/ gmodmodules/source/browse/trunk/gm_slog/gm_slog/vfnhook.h?spec = svn74&r = 74 –

答えて

5

いいえ、基本メソッドではが自動的に呼び出されません。が呼び出されます。動的ディスパッチ・メカニズムは、がそれを呼び出す必要があるかどうかを検出し、呼び出される関数になります。これは通常、クラス内の各仮想関数に対して最終オーバーライドへのポインタを格納する仮想テーブル(vtable)によって実装されます。動的ディスパッチを使用すると、コンパイラはそのテーブルを介して間接呼び出しを挿入し、適切な関数にジャンプします。 vtableのは、実際に通話を転送する前に、潜在的にthis(暗黙の第一引数)を変更することができサンクまたはトランポリンへのポインタを保持している

注意。この方法を使用する利点は、thisを更新する必要がない場合、コンパイラは最終的なオーバーライダに直接ジャンプできます。いずれにしても、この機能を利用して、独自のコードを指すようにvtableを変更することができます(つまり、各vtableのポインタをタイプごとに更新することができます)。サンクまたは関数

+0

Nice :)完全な答え! – besworland

+0

誤解を避けるため。それは可能です、私がする必要があるのは仮想テーブルを変更することだけです?その場合、知識の不足のために質問したいのですが、vtableは4バイトのポインタのみで構成されていますか?または、私は今必要なことが他にあります(つまり、vtableとオフセットを見つけて、そのポインタを私の 'hook'を指すように置き換えるだけです)? –

+0

@ElliottDarfinkうん、あなたはvtableを見つけて、ポインタを交換する必要があります。また、それは4バイトかもしれませんが、あなたのコンピュータのアーキテクチャに依存しないかもしれません。 –

0

質問が正しく表示された場合、ランボルギーニクラスのDriveメソッドは仮想関数のテーブルに基づいて呼び出されます。基本クラスのDriveメソッドを呼び出す場合は、Car::Drive;のように記述する必要があります。基本クラスはVTBLのためにスペースが必要です。私はあなたの質問に答えていないことを願っています。

0

正しく理解すれば、Lamborghini::Driveが呼び出されるたびにCar::Driveが呼び出されます。Lamborghini:Driveは、直接ベース関数を呼び出さなくてもかまいませんか?

このため、最も簡単な方法は、仮想(保護されている)である「内部」関数を使用することです。初期のメソッドは非仮想であり、呼び出しをルーティングします。 ここでは例です:

class Car 
{ 
    void Drive(void) 
    { 
     // ... 
     Car::innerDrive(); // Base function call 
     // ... 
     this->innerDrive(); // 'Derived' function call 
     // ... 
    } 
protected: 
    virtual void innerDrive(void) { } // Empty virtual function 
} 

class Lamborghini : public Car 
{ 
protected: 
    void innerDrive(void) { // does lots of stuff, but does NOT call base function } 
} 
+0

問題は、あらかじめコンパイルされたコードで作業していることです。そのため、私は迂回路を使用しています:) –