クラスの vtable は、関数へのポインターの単なるリストです。これには、仮想関数ごとに 1 つのポインターが次の順序で含まれます: 非常に、非常に最上位の基本クラス、次の基本クラス、そのサブクラス、. . . 最派生クラス。
例:
struct A {
virtual ~A() {}
virtual void foo() = 0;
}
struct B : public A {
virtual void foo() { // do something }
virtual void bar() { // do something else }
}
B の vtable には、次の順序で含まれます。
(このオブジェクトへの型 A のポインターを持つコードの一部で同じ vtable を使用できるように、A のものが最初に来る必要があります。そのコードは、基になるオブジェクトが実際に B であることを知りません。)
32 ビット ソースを参照している場合、ポインターは 4 バイトなので、24 = 4 * 6 であり、7 番目の仮想関数を参照しています (インデックスは 0 から始まります)。64 ビットを使用している場合、ポインターは 8 バイトなので、24 = 8 * 3 となり、4 番目を探しています。実際、私は IDA の「C++ への変換」機能を使用していないため、24 は実際には表の 24 番目のエントリである可能性があります。
確認する簡単な方法: 独自のプログラムを作成します。タイプ ICLRRuntimeHost の変数を宣言します。疑わしい関数を呼び出します(ヘッダーファイルを見て、ビット数に応じて7または4までカウントするか、例を誤解した場合は24までカウントします)。生成されたアセンブリ コードを見て、インデックスが正しいかどうかを確認します。(私はいつもそのようなことで1つずれているので、これはチェックを提供します。)