EasyHook とは
以下の2点を与えるだけでインラインフック(Detour)を行える関数を提供してくれるライブラリ。
- フックする関数のアドレス
- フックした後実行されるオリジナルの関数へのポインタ
手動でやる場合、トランポリンのコードをレジスタの整合性を考えながら書かないといけなかったり、アンフックする時に元のコードを覚えて置いたりしないといけなかったりするが、その手間を省ける。
Visual C# (マネージドコード) や 普通のC++ (アンマネージドコード) どちらでも使える。
EasyHook のダウンロード
EasyHookを導入するには二通りのやり方がある。
- Visual Studio の NuGetパッケージマネージャー を使ってインストールする方法
- 手動でバイナリをダウンロードし、includeパスを通したりして使う方法
自分 (visual Studio 2019 の DLLプロジェクト) は1の方法だと上手く行かなかったので、この記事では2の方法を使う。
EasyHookのダウンロードリンクから、下記のように Binaries って書いてある最新版のをダウンロードする。ダウンロード場所は後でVisualStudioでincludeパスを追加する時にそこを指定するので、覚えていれば何でも良い。
Visual Studio で EasyHook を使うための設定
VisualStudioを立ち上げ、C++のDLLのプロジェクトを立ち上げる。
立ち上げた後、以下の用にしてソリューションのプロパティ画面を開き、インクルードディレクトリを編集する。
先ほどダウンロードしたEasyHookのフォルダの中に、NetFX3.5
というフォルダがあるはずなので、そこへのパスを追加する。
同様にして、ライブラリディレクトリも編集し、上記と同じパスを追加する。
終ったら、これはEasyHookとは関係ないが、構成プロパティ > 詳細 > 文字セット
の所を「マルチバイト文字セットを使用する」に変えて置く。
サンプルプログラム
※ このサンプルは、本家のチュートリアルを参考にしています。
まず、上記の工程が上手く行ってるかどうかを確かめるために、以下のように include
文を追加し、赤線が出ない事を確認しましょう。
ちなみに上図で分かる通り、今回は32bitのDLLでビルドします。
iostream
は、EasyHookのフックが失敗した時のエラー文を表示するに、エラー文の文字列の型変換とかするのに使うので入れてます。
全体のサンプルプログラムは以下になります。
#include "pch.h"
#include <iostream>
#include <easyhook.h>
#pragma comment(lib, "EasyHook32.lib")
using namespace std;
BOOL WINAPI myBeepHook(DWORD dwFreq, DWORD dwDuration)
{
MessageBox(NULL, "called from myBeepHook!", "success", MB_OK);
return Beep(dwFreq + 800, dwDuration);
}
DWORD WINAPI ThreadMain(LPVOID params) {
HOOK_TRACE_INFO hHook = { NULL };
void* pBeep = GetProcAddress(GetModuleHandle("kernel32"), "Beep");
Beep(500, 500); // フック前のBeep呼び出し
// 1: フックの用意
NTSTATUS res = LhInstallHook(pBeep, myBeepHook, NULL, &hHook);
if (FAILED(res)) {
wstring s(RtlGetLastErrorString());
MessageBoxW(NULL, s.c_str(), L"error", MB_OK);
return -1;
}
// 2: フックの有効化 (ここでフックされる)
ULONG ACLEntries[1] = { 0 };
LhSetInclusiveACL(ACLEntries, 1, &hHook);
Beep(500, 500); // フック後のBeep呼び出し
// 3: アンフック
LhUninstallHook(&hHook);
LhWaitForPendingRemovals(); // アンフックが完了するまで待つ
return 0;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
CreateThread(0, 0, ThreadMain, hModule, 0, 0);
}
return TRUE;
}
ビルドと実行
ビルド自体は Ctrl-b
すればできますが、ビルド後にやるべき事があります。
ビルドした出力結果のDLLがあるディレクトリに、EasyHook32.dllを入れる必要があります。EasyHook32.dllは、インクルードディレクトリで指定したパス(NetFX3.5
フォルダの中) にあります。
これを上図のように入れた後、以下のようにして実行してみましょう。以下のようにメッセージボックスが表示されればOKです。
※ rundll32.exeは任意のDLLを実行するためのアプリですが、カンマの後ろにエクスポート関数を指定する必要があり、今回はDLLMainしか要らないため、適当にhogeを入れてるのでエラーが出てますが、気にしなくて大丈夫です。