- 実行時に vtable を変更したり、直接アクセスしたりできますか?
移植性はありませんが、汚いトリックを気にしないのであれば、もちろんです!
警告: このテクニックは、子供、 969歳未満の大人、アルファ ケンタウリの小さな毛むくじゃらの生き物にはお勧めできません。副作用として、悪魔が鼻から飛び出したり、その後のすべてのコード レビューで必要な承認者としてYog-Sothothが突然出現したり、既存のすべてのインスタンスに が遡及的に追加されたりすることが含まれる場合がありIHuman::PlayPiano()
ます]
私が見たほとんどのコンパイラでは、vtbl * はオブジェクトの最初の 4 バイトであり、vtbl の内容は単にそこにあるメンバー ポインターの配列です (通常、宣言された順序で、基本クラスが最初になります)。もちろん、他の可能なレイアウトもありますが、それは私が一般的に観察したものです.
class A {
public:
virtual int f1() = 0;
};
class B : public A {
public:
virtual int f1() { return 1; }
virtual int f2() { return 2; }
};
class C : public A {
public:
virtual int f1() { return -1; }
virtual int f2() { return -2; }
};
A *x = new B;
A *y = new C;
A *z = new C;
今、いくつかの悪ふざけを引っ張るために...
実行時のクラスの変更:
std::swap(*(void **)x, *(void **)y);
// Now x is a C, and y is a B! Hope they used the same layout of members!
すべてのインスタンスのメソッドを置き換える (クラスのモンキーパッチ)
vtbl 自体はおそらく読み取り専用メモリにあるため、これは少しトリッキーです。
int f3(A*) { return 0; }
mprotect(*(void **)x,8,PROT_READ|PROT_WRITE|PROT_EXEC);
// Or VirtualProtect on win32; this part's very OS-specific
(*(int (***)(A *)x)[0] = f3;
// Now C::f1() returns 0 (remember we made x into a C above)
// so x->f1() and z->f1() both return 0
後者は、mprotect の操作により、ウイルス チェッカーとリンクを目覚めさせ、注意を喚起する可能性が高くなります。NX ビットを使用するプロセスでは、失敗する可能性があります。