今までのものは手間はかかっているもののきちんと手順を踏んで、わりと行儀よく(?)関数のフックを実現していました。今回は豪快な関数フックの方法を試してみたいと思います。
※ なおこの手順で問題が起こっても自己責任でお願いします。前提条件として 32bitのアプリケーションとしています。
その方法とは、対象関数の先頭に、自分のフック関数へのジャンプを埋め込んでしまえばいい!というものです。
具体的には、関数先頭のアセンブラを書き換えて、自分の用意したフック関数のアドレスへの無条件ジャンプに数値を書き換えてしまう、という流れになります。
多くの場合、関数の配置されているメモリは実行可能&リード可能なメモリ領域としてマークされています。そのため、まずはメモリ領域の属性を変更する必要があります。Linuxにおいては下記のプログラムのようにして属性を変更します。
ptrdiff_t page_aligned = (ptrdiff_t) func; page_aligned &= ~(page_size-1); // ページサイズは別に取得. mprotect( (void*)page_aligned, page_size, PROT_READ | PROT_WRITE | PROT_EXEC );
ここで func が目的の関数となります。関数アドレスから、その関数が所属しているページを割り出して mprotect API で属性を変更します。
その後、関数のアドレスの場所に 5バイトほど値を書き込みます。
uint8_t* f = (uint8_t*)func; *f = 0xE9u; *(uint32_t*)( f+1 ) = (uint32_t) hookedFunc;
0xE9 [フック関数のアドレス] で5バイトです。これはアセンブラではアドレスへの無条件ジャンプとなります。
これらの手順で加工した元の関数を呼び出すと、見事に hookedFunc に実行が移ってきていると思います。今回はいきなり5バイト破壊してしまっているので、フック関数から、元の関数を呼び出すような実装となっていません。必要ならば、このあたり整合性を持つようにバイト列をコピー&加工する必要があると思います。元に戻す場合には書き換えた5バイトをバックアップから書き戻すようにすればできるでしょう。
コメント