CodeZine(コードジン)

特集ページ一覧

SEXYHOOKの実装部
とある関数の接合部(2)

関数、クラスメソッド、APIをフックする方法

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2010/03/02 14:00

ダウンロード sexyhook0.8 (80.0 KB)

目次

呼び出し元への帰り方:back to the 呼び出し元

 2ページ目で少し触れましたが、フックした関数からどうやって、呼び出し元に戻るかを解説します。例えば、次のようなadd関数をフックした場合した場合です。

//定義
int add(int a,int b)
{
        return a + b;
}

//関数のフックのテスト
{
        //add関数を書き換えて引き算にする。
        SEXYHOOK_FUNC_HOOK_2_BEGIN(int,SEXYHOOK_CDECL,add,int a,int b)
        {
                return a - b;
        }
        SEXYHOOK_FUNC_END();

        int cc = add(10,20);
        printf("\r\n関数のフックのテスト\r\n");
        printf("cc: %d\r\n",cc);
        SEXYHOOK_ASSERT(cc == -10);
}

 この場合の関数呼び出し図は、このようになります。

//最初の呼び出し
394:          int cc = add(10,20);
00402137   push        14h
00402139   push        0Ah
0040213B   call        @ILT+165(add) (004010aa)----ILT経由でadd関数呼び出し
                                                   |
00402140   add         esp,8    <<<<<<<<<   <<<    | <<<<<<<<<<<<<<--------------------- 戻り値
00402143   mov         dword ptr [cc],eax          |                                   |
                                                   |                                   |
                                                   |                                   |
//ILT                                              |                                   |
@ILT+165(?add@@YAHHH@Z):                           |                                   |
004010AA   jmp         add (00401470)  <------------                                   |
                                |add関数の実体呼び出し                                 |
                                |                                                      |
                                |                                                      |
                                |                                                      |
9:    //関数のテスト            |                                                      |
10:   int add(int a,int b)      |                                                      |
11:   {                         /                                                      |
           //強引に書き換えてフックルーチンへ飛ばす                                    |
00401470   jmp         `main'::`83'::SEXYHOOKFunc388::HookFunction (00402a5e)          |
...                                                                  |                 |
00401477   inc         ebp     //ここは実行されない                  |                 |
00401478   or          al,5Dh  //壊れたアセンブラ                    |                 |
0040147A   ret                 //ここは実行されない                  |                 |
12    }                                                              |                 |
                                                                     |                 |
                                                                     |                 |
                                                                     |                 |
//フックルーチンのインナークラス                                     |                 |
class SEXYHOOKFunc388 : public SEXYHOOKFuncBase                      |                 |
{                                                                    |                 |
//中略                                                               |                 |
//足し算を引き算に書き換え                                           |                 |
389:          {                                                      |                 |
00402A5E   push        ebp                          //←ここに飛ぶ <--                 |
00402A5F   mov         ebp,esp                                                         |
390:              return a - b;                                                        |
00402A61   mov         eax,dword ptr [a]                                               |
00402A64   sub         eax,dword ptr [b]                                               |
391:          }                                                                        |
00402A67   pop         ebp                                                             |
00402A68   ret      //←ここの ret 命令で、                                            |
                    //最初に add命令を呼び出したところ(00402140)に戻る. >--------------|
                    //フック関数と 呼び出し規約と引数をそろえているため、
                    //スタックは破壊されない。
//中略
}

 参考までに通常の呼び出しはこんな感じです。

394:          int cc = add(10,20);
00402137   push        14h
00402139   push        0Ah
0040213B   call        @ILT+165(add) (004010aa)----ILT経由でadd関数呼び出し
                                                   |
00402140   add         esp,8    <<<<<<<<<   <<<    | <<<<<<<<<<<<<<--------------------- 戻り値
00402143   mov         dword ptr [cc],eax          |                                   |
                                                   |                                   |
                                                   |                                   |
//ILT                                              |                                   |
@ILT+165(?add@@YAHHH@Z):                           |                                   |
004010AA   jmp         add (00401470)  <------------                                   |
                                |add関数の実体呼び出し                                 |
                                |                                                      |
                                |                                                      |
                                |                                                      |
                                |                                                      |
9:    //関数のテスト            |                                                      |
10:   int add(int a,int b)      |                                                      |
11:   {                         |                                                      |
00401470   push        ebp <----                                                       |
00401471   mov         ebp,esp                                                         |
12:       return a + b;                                                                |
00401473   mov         eax,dword ptr [a]                                               |
00401476   add         eax,dword ptr [b]                                               |
13:   }                                                                                |
00401479   pop         ebp                                                             |
0040147A   ret             //最初にadd関数を呼び出し場所(0040213B)に戻る>>--------------

 フックルーチンのretを利用して、呼び出し元に戻っています。フックしたいルーチンと、フックするルーチンの呼び出し規約をそろえるのは、これがやりたいためです。フックしたいルーチンと、フックするルーチンの呼び出し規約が違えば、フックルーチンのretで戻ったときにスタックが壊れてしまいます。

APIフック

 SEXYHOOKでは、APIフックは関数フックとは別の機構で行っています。SEXYHOOKAPIBaseクラスが担当しています。これについては、一般的に行われるWindowsのAPIフックの作法どおりなので、特に変なことは行っていません。

 このルーチンは、SEXYHOOKの前に作ったオブジェクトリークチェッカー「obcheck」からそのまま流用したものです。obcheckで該当クラスを作るときに、次の2つのサイトが大変参考になりましたので、この場をお借りして紹介させていただきます。

 オブジェクトリークチェッカー「obcheck」はオブジェクトリークを検出してくれるツールです。よろしければご利用ください。

まとめ

 SEXYHOOKがどのようにして関数、クラスメソッド、APIをフックしているかを解説しました。SEXYHOOK自体は2009年の夏ぐらいから構想と作成に取り掛かり、途中の停滞期を過ぎて、2010年1月の後半に公開できました。

 テストを作成するためには接合部を作り出すことが必要であるというアイディアは、レガシーコード改善ガイド(Working Effectively With Legacy Code)からいただきました。この本に出会っていなければ、SEXYHOOKは誕生しませんでした。著者と翻訳者の方ありがとうございます。

 SEXYHOOKを作ることができたのも、いろいろなサイトに書かれていた貴重な資料、アイディアがあったからです。貴重な資料をネットに公開していただいた皆様にお礼を述べさせていただきます。

 バグの指摘やアドバイスを教えていただいたcppllの皆様、おかげでSEXYHOOKはさらにSEXYになれました。ありがとうございます。うまく動作しないときの愚痴を聞いてくれたtwitterの方たち、どうもすみません。

 最後に、内容に間違い、バグなどありましたら、SVN/wikiを修正していただいて結構です。お気軽にどうぞ。ついでによろしければ、twitterで@super_rtiまでご連絡ください。

 この文章やドキュメントを英訳してくれる方を募集しています。一緒にcode projectに殴りこみに行きましょう。

 happy hacking!

参考資料

 brute_cast参考

 マシン語関係

 仮想関数関係

 APIフック



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:「SEXYHOOK」 とある関数の接合部

著者プロフィール

  • rti(あーるてぃーあい)

    働いたら負けだと思っていた元ニートのプログラマ 歌って踊れてさくらたんにもハァハァできます。 love:C++,アセンブラ,PHP,javascript好きのOO厨房 低レイヤープログラムやネットワークサーバからサーバサイドプログラム、ソフトウェア設計、インフラ設計運用までと幅広くやってます。...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5