[Dll Injection] Vtableフックはどのようにして動くのか
導入
みなさんはHorionなどのソースコードでこのような処理を見たことありますか?
これはVtableフックと呼ばれるもので、Vtableと呼ばれる仮想関数リスト
から関数のアドレスを取得してMinHookでフックしているコードです。
これはどのようにして動いているのでしょうか?
今回はこのVtableフックの原理を解説します。
Vtableってなに
そもそもVtableを知らない方のために解説をします。
VtableもしくはVftableはVirtual Function Tableの略です。
その名の通り仮想関数のリストのことです。
C++で関数を宣言した際にvirtual修飾子を関数につけると
この関数リストに入れられます。
そしてクラスのインスタンスを作成した際に最初の8バイトに
Vtableのアドレスが格納されます。
virtualは関数を継承して宣言したいときに使うみたいな
認識でとりあえず大丈夫だと思います
class IClientInstance {
public:
virtual void function_1(); // 継承するクラスで宣言をする
}class ClientInstance {
public:
void function_1() override {
// 実際に処理を書く
// この関数はVtableに自動でコンパイル時に挿入される
}
}実際に命令を見てみる
Signatureをスキャンするとこのような命令になっています
Lea命令を使ってRAXレジスタにPlayerAuthInputPacketのVtableを入れています。
Vtableフックはこの命令からVtableのアドレスを静的に見つけてフックしています。
RAXレジスタにVtableのアドレスを入れる際に、
「cpuは命令の終端からのアドレス」にオフセットを足すことで
アドレスを取得してます。
なので今回の場合は、
0x168FC70 (0x0168FC69 + 0x7)
+
0x3ABA638 (0x38 0xA6 0xAB 0x03 をInt32整数に変換したもの)
= 0x514A2A8
で、0x514A2A8がVtableのアドレスになります。
実際に見てましょう。
このようにVtableのアドレスをフックすることができました。
おまけの解説
Vtableのアドレスを取得した後にこのようなことをしていますね
uintptr_t** localPlayerVtable = ~
のuintptr_t**は今回の場合だと、関数のポインタのポインタという意味を表しています。
x64の場合、一つのポインタのサイズは8バイトなのでインデックスが
1増えていくごとに8バイトオフセットするようになっています。
なのでlocalPlayerVtable[196]は、実際は
*(uintptr_t**)(localPlayerVtableのアドレス + 196 * 8)
という処理をしているわけです。
この辺の話はやっていくうちのわかってくると思うので
今は理解しなくても大丈夫だと思います。
次回
次回もなんか書きます


コメント