サイト移転のお知らせ

株式会社ソフトゲートのドメインにウェブサイトを構築しました。

これに伴い、当サイトは、softgate.jp ドメインが失効する 2013 年 4 月をもって閉鎖いたします。

当サイトのコンテンツで必要な情報がありましたら、ローカルに保存してくださるようお願いいたします。

WinAPIOverride32 入門 印刷
投稿者:手賀   
2009年 7月 17日(金曜日) 21:35

当サイトのコラムで現在「マケスピで見るネイティブコード解析」というシリーズを書いていますが、その中で紹介した WinAPIOverride32 に関する日本語の情報があまりないようですので、初めて利用する方のために簡単なステップバイステップをまとめておくことにします。

「マケスピで見るネイティブコード解析」を読んでいない方には何の話か分からないと思いますので、最初に WinAPIOverride32 を簡単に紹介しておきましょう。

WinAPIOverride32 は、一言で言えば API モニタとか API フックとか API スパイと呼ばれるような種類のソフトウェアということになります。API スパイツールは世の中にいろいろ出回っていますが、WinAPIOverride32 の特徴的なところは、任意の DLL 関数または EXE 内の内部関数に対して、簡単にユーザー定義のフック関数を設定する仕組みが提供されている点にあると思います。

また、ソースが公開されていることから、万が一作者が WinAPIOverride32 のメンテに手が回らなくなっても、私たちが勝手に改良したりバグを直したりできるという安心感があります。

同梱されている便利ツール

WinAPIOverride32 の Zip 形式の配布ファイルを入手して展開すると、中にはいくつもの EXE ファイルが入っています。その名の通り、WinAPIOverride32.exe というのがメインのアプリケーションですが、これ以外の EXE ファイルにもなかなか便利なものがありますので、それぞれ一言で紹介しておきます。

  • Dumper.exe - プロセス空間をダンプして EXE または DLL ファイルに出力するツールです。アンパッカーと呼ばれる種類のツールに類似しています。
  • DllExportFinder.exe - たくさんの DLL の中からエクスポートされている関数を名前で検索することが出来ます。
  • DebugInfosViewer.exe - デバッグ情報(.pdb ファイル)が入手可能な場合に情報を表示するツールです。
  • HeapWalker.exe - 特定のプロセスのヒープからデータを検索することが出来ます。
  • MonitoringFileBuilder.exe - WinAPIOverride32 でモニタするべき DLL や関数を指定するモニタリングファイルを作る補助ツールです。

DebugInfosViewer.exe はいまいち使う場面がないでしょうが、その他のツールは何かの機会に活用できるかもしれませんので、これらの存在だけは認識しておいて損はないと思います。

メインアプリとなる WinAPIOverride32 には、もちろん通常の API スパイとしての機能も付いていますが、その使い方はさほど難しくありませんので、ここでは割愛します。ターゲットアプリをロード(ただし停止状態)してからモニタリングファイルや Overriding Hook の指定をするという点と、プログラムに同梱されているモニタリングファイルを参考にしながらモニタする DLL や関数を指定してやる必要があるということだけ理解しておけば、あとは問題なく使いこなせるはずです。

それでは、次節から WinAPIOverride32 を使ったカスタム API フック作成に取りかかりましょう。

Pre Hook を作ってみる

これからフックするターゲットアプリ(より正確にいえば、フック対象となるのはあくまで DLL 関数であり、アプリは関係ないといえば関係ありません)は、私が作った小さなダイアログアプリケーションです。実行すると次のようなダイアログが出てきます。「OK」ボタンを押すとメッセージボックスが表示され、「Cancel」を押すと終了します。

   

ターゲットアプリと後述するフック DLL のソースおよび実行ファイルは弊社ウェブサイト http://www.softgate.jp/directdl/HookSample.zip からダウンロードすることができます。フックターゲットのコンパイルには WTL が必要ですが、ターゲットについてはご自分でコンパイルしていただく必要もないので、同梱されている EXE ファイルを使ってください。

さて、Pre Hook を行うカスタム API フック DLL を作成する手順を見ていきましょう。

プロジェクトの作成

VC++ の Win32 プロジェクトを作成します。サンプルではプロジェクト名は MyHook にしました。

プロジェクトの種類は DLL を選び、「空のプロジェクト」にチェックしてください。

たったいまプロジェクトを作成したフォルダを $(MyHook) と呼びます。また、WinAPIOverride32 がインストールされているフォルダを $(WAO32) と呼ぶことにします。次の要領で $(WAO32) から $(MyHook) にサンプルファイルをコピーしてください。

  • $(WAO32)\Overriding Dll SDK\_Common_Files をフォルダごと $(MyHook) 以下にコピー
  • $(WAO32)\Overriding Dll SDK\API\PrePostHooksMsgBox\FakeAPI.cpp を $(MyHook) 以下にコピー

コピーしたファイル群の中に .cpp ファイルが二つ(FakeAPI.cpp と _Common_Files\GenericFakeAPI.cpp)ありますので、それらをプロジェクトに追加します。これから私たちが書き換えていくのが前者のファイルで、後者はそのまま流用します。DLL からエクスポートするべき関数は後者に実装されていますので、私たちユーザーが DLL から関数をエクスポートすることを意識する必要はありません。

現時点では、FakeAPI.cpp の #include 文のせいでコンパイルが通らないはずです。

fatal error C1083: include ファイルを開けません。'../../_Common_Files/GenericFakeAPI.h': No such file or directory

そこで、FakeAPI.cpp の該当箇所を #include "_Common_Files/GenericFakeAPI.h" に書き換えてビルドできることを確認してください。

Pre Hook の中身をいじる

ビルドが通ることを確認したら、早速 Pre Hook を独自のものにカスタマイズしてみましょう。具体的には、ターゲットアプリが表示するメッセージボックス内の文字列を、別の文字列にすり替えることで、Pre Hook が正しく動作していることを確認することにします。

先ほども述べたとおり、私たちがいじるファイルは FakeAPI.cpp というファイルですので、これを VC++ で開いてください。

47 行目からの pArrayBeforeAPICall 配列のエントリのうち、 {_T("User32.dll"),_T("MessageBoxA") で始まる行は、今回は使わないので削除しましょう(放置しておいても今回のターゲットアプリをフックする分には無害ですが)。私たちがこれからフックするのは MessageBoxW という Unicode 版のメッセージボックス関数だけとします。これを指示しているのが次の行です:

  {_T("User32.dll"),_T("MessageBoxW"),(FARPROC)MessageBoxesPreApiCall, ...

見ればお分かりのとおり、「User32.dll の MessageBoxW という関数が呼ばれるときには、Pre Hook として MessageBoxesPreApiCall という関数を呼びだしてください」ということを WinAPIOverride32 に指示していることになります。

ちなみに、ほかの DLL 関数もフックしたければ、同じような要領で、この配列にエントリを追加する必要があります。スタックサイズを指定する四つ目のパラメータだけ若干難しいかもしれませんが、基本的には、サンプルソースを参考にして、フック対象関数の引数の型を StackSizeOf マクロで囲んで加えれば問題ないでしょう。

さて、MessageBoxW に渡される文字列のポインタを書き換えたいので、MessageBoxW のプロトタイプを確認します。

int MessageBox(
  HWND hWnd,
  LPCWSTR lpText,
  LPCWSTR lpCaption,
  UINT uType);

メッセージボックス内部に表示される文字列のポインタは二つ目の引数ですので、こいつを書き換えることにします。MessageBoxesPreApiCall 関数の一つ目の引数 pEspArgs はスタックの先頭(厳密に言えば先頭ではなくリターンアドレスの次ですが)にある第一引数のアドレスを指していますので、二つ目の引数のアドレスは pEspArgs+sizeof(ポインタ) という風に計算することができます。そこで、二つ目の引数のアドレスに格納されている値(すなわち文字列へのポインタ)を、私たちが差し替えたい文字列のポインタで上書きしてやれば、表示される文字列の差し替えができるものと考えられます。

BOOL __stdcall MessageBoxesPreApiCall(PBYTE pEspArgs,REGISTERS* pBeforeCallRegisters,
  PRE_POST_API_CALL_HOOK_INFOS* pHookInfos,PVOID UserParam)
  {
    *((PBYTE*)(pEspArgs+1*sizeof(PBYTE))) = (PBYTE)_T("これは差し替えたテキストです。");
    // continue chain (if any other func)
    return TRUE;
  }

これでうまくいくはずですので、早速ビルドして確認をしたいと思いますが、たったこれだけの簡単な手順で API フックが実現できているのはある意味驚くべきかもしれません。

フックの動作を確認する

フックの動作を確認するには、まずターゲットアプリをロードして実行する必要があります。WinAPIOverride32 を立ち上げて、下の図のようにターゲットアプリのパスを指定したら実行ボタン(左端にある緑色の再生マークのアイコンです)を押してください。

ターゲットアプリの実行が開始されて、ダイアログが表示されたら、次にフック DLL をロードします。下の図に示したように MyHook.dll のパスを指定し、「Load」ボタンを押して DLL をターゲットアプリのプロセスにロードしてください。

これで準備はすべて整いました。ターゲットアプリの「OK」ボタンを押すと、MessageBoxW が呼ばれるので、理論的には私たちのフック関数に制御が移ってくるはずです。

上の図のように差し替えた文字列が表示されれば、Pre Hook は成功です。

今回示したサンプルでは、文字列を差し替えるようなことをしましたが、一般的には関数に渡される引数をログに記録するというような使い方が多いかと思います。また、引数ではなくて返値を記録したい場合には Post Hook を使用します。もちろん、Pre/Post Hook を併用しても構いません。

このほかにも、DLL 関数を完全に置き換えてしまう Overriding Hook というフックを作ることもできますが、興味のある方はこの入門コラムで解説した内容を応用して、自分で実装してみてください。