EasyHookで簡単にインラインフックする

# EasyHook とは
以下の2点を与えるだけでインラインフック(Detour)を行える関数を提供してくれるライブラリ。
* フックする関数のアドレス
* フックした後実行されるオリジナルの関数へのポインタ

手動でやる場合、トランポリンのコードをレジスタの整合性を考えながら書かないといけなかったり、アンフックする時に元のコードを覚えて置いたりしないといけなかったりするが、その手間を省ける。
**Visual C# (マネージドコード)** や **普通のC++ (アンマネージドコード)** どちらでも使える。

# EasyHook のダウンロード
EasyHookを導入するには二通りのやり方がある。
1. Visual Studio の NuGetパッケージマネージャー を使ってインストールする方法
2. 手動でバイナリをダウンロードし、includeパスを通したりして使う方法

自分 (visual Studio 2019 の DLLプロジェクト) は1の方法だと上手く行かなかったので、**この記事では2の方法を使う**。<br/>
[EasyHookのダウンロードリンク](http://easyhook.github.io/downloads.html)から、下記のように Binaries って書いてある最新版のをダウンロードする。ダウンロード場所は後でVisualStudioでincludeパスを追加する時にそこを指定するので、覚えていれば何でも良い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/80a97eb3-4ac8-3890-a313-d9cc1d7ba015.png)

# Visual Studio で EasyHook を使うための設定
VisualStudioを立ち上げ、C++のDLLのプロジェクトを立ち上げる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/419a3200-8610-fd1a-ede2-d0c28f4a2499.png)
立ち上げた後、以下の用にしてソリューションのプロパティ画面を開き、**インクルードディレクトリ**を編集する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/f1d96c91-4ac7-129c-4e8b-65b02d1498f1.png)
先ほどダウンロードしたEasyHookのフォルダの中に、`NetFX3.5` というフォルダがあるはずなので、そこへのパスを追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/827e571e-d258-07b7-345a-32b0f9c279a8.png)
同様にして、**ライブラリディレクトリ**も編集し、**上記と同じパス**を追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/cf067f76-5183-5e93-1bdc-328694416254.png)
終ったら、これはEasyHookとは関係ないが、`構成プロパティ > 詳細 > 文字セット` の所を「マルチバイト文字セットを使用する」に変えて置く。


# サンプルプログラム
※ このサンプルは、[本家のチュートリアル](http://easyhook.github.io/tutorials/nativehook.html)を参考にしています。
まず、上記の工程が上手く行ってるかどうかを確かめるために、以下のように `include`文を追加し、**赤線が出ない事を確認**しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/4c3353b0-15dc-e6a0-3de4-6232246b2d5d.png)
ちなみに上図で分かる通り、今回は**32bitのDLL**でビルドします。
`iostream` は、EasyHookのフックが失敗した時のエラー文を表示するに、エラー文の文字列の型変換とかするのに使うので入れてます。<br/>
**全体のサンプルプログラム**は以下になります。

```c++
#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`フォルダの中) にあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/c66ff999-101f-a3a1-efbe-2cf43e7b92fc.png)
これを上図のように入れた後、以下のようにして実行してみましょう。以下のようにメッセージボックスが表示されればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535664/2b999ee1-8cb7-c852-9d48-4bd19be00c93.png)
※ rundll32.exeは任意のDLLを実行するためのアプリですが、カンマの後ろにエクスポート関数を指定する必要があり、今回はDLLMainしか要らないため、適当にhogeを入れてるのでエラーが出てますが、気にしなくて大丈夫です。