home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

日記/2010/06/30/SetWindowsHookEx()の実験その2

日記/2010/06/30/SetWindowsHookEx()の実験その2

日記 / 2010 / 06 / 30 / SetWindowsHookEx()の実験その2
id: 689 所有者: msakamoto-sf    作成日: 2010-06-30 17:14:41
カテゴリ: Windows プログラミング 

前回( 日記/2010/06/30/SetWindowsHookEx()の実験その1 )に引き続きWindowsでの"HOOK"の実験です。

前回は同じデスクトップ上の全てのスレッドでHOOKが発動してしまいました。今回は任意のプロセスにのみHOOKできるようにしてみます。といっても、SetWindowsHookEx()APIにはそこまでの柔軟性はありません。global-hookを使う以上は、どうしても全てのプロセスでHOOK-DLLがロードされてしまいますし、HOOK本体も実行されてしまいます。

そこで、HOOK対象となるプロセスIDをDLLの共有メモリ機能を使って予め登録しておき、HOOK本体の中で現在のプロセスIDがHOOK対象と同じ場合にのみ、HOOK処理を実行するような分岐を組み込みます。他のプロセス内で発動した場合は、分岐により何もせずSKIPするようになります。


サンプルコードの構成

前回と同様、3つのEXE/DLLより構成されます。

  1. foo.exe : HOOK「される」のを確認する為のダミーEXE
  2. hook.dll : HOOK本体が格納されるDLL
  3. hookctrl.exe : HOOK ON/OFF を切り替えるEXE

各DLLの役割も前回と同じです。
では早速ソースとコンパイル方法の紹介に移ります。

foo.exe (前回と同じ)

foo.exeを構成するファイルとコンパイル方法は前回( 日記/2010/06/30/SetWindowsHookEx()の実験その1 )と同じ為、省略します。

hook.dll

前回と同様、hook.cとhook.defにより構成されています。変更点はファイルのコメントを参照して下さい。

  • hook.c
  • hook.def

hook.c:

#include <windows.h>
#include <stdio.h>
 
// プロセス間で共有出来る領域を用意します。
#pragma data_seg(".shared")
// 実際にHOOK処理を実行したいプロセスID
static DWORD dwTargetProcessId = 0;
#pragma data_seg()
 
// これが hookctrl.exe から呼ばれ、HOOK対象のプロセスIDを共有領域に保存します。
void SetMyKeyboardProcTarget(DWORD dwPid)
{
    dwTargetProcessId = dwPid;
}
 
LRESULT CALLBACK MyKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    char buf[200];
    DWORD dwPid = GetCurrentProcessId();
    if (dwTargetProcessId == dwPid) {
        // 現在のプロセスIDが対象と同じ時のみ、HOOK処理を実行します。
        _snprintf(buf, 200, "virtual-key code is 0x%08X\n", wParam);
        MessageBox(NULL, buf, buf, MB_OK);
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}
 
BOOL WINAPI DllMain(
        HINSTANCE hInst, DWORD dwReason, LPVOID lpvReserved)
{
    char buf[200];
    DWORD dwPid = GetCurrentProcessId();
 
    switch (dwReason) {
        case DLL_PROCESS_ATTACH:
            if (dwTargetProcessId == dwPid) {
                // attach/detachされるプロセスIDが対象と同じ場合のみ、
                // メッセージボックスを表示します。
                _snprintf(buf, 200, "ATTACH to 0x%08X.", dwPid);
                MessageBox(NULL, buf, buf, MB_OK);
            }
            break;
        case DLL_PROCESS_DETACH:
            if (dwTargetProcessId == dwPid) {
                // attach/detachされるプロセスIDが対象と同じ場合のみ、
                // メッセージボックスを表示します。
                _snprintf(buf, 200, "DETACH to 0x%08X.", dwPid);
                MessageBox(NULL, buf, buf, MB_OK);
            }
            break;
    }
    return TRUE;
}

hook.def:SetMyKeyboardProcTargetを追加しています。

LIBRARY hook.dll
EXPORTS
    MyKeyboardProc
    SetMyKeyboardProcTarget

コンパイル方法:

> cl /LD hook.c user32.lib /link /def:hook.def /SECTION:.shared,rws

"/SECTION:.shared,rws" リンカオプションで data_seg(".shared") pragmaで指定したセクションを読み書き共有可能にしています。

hookctrl.exe

前回と同様、3ファイル構成となります。

  • resource.h
  • hookctrl.rc
  • hookctrl.c

resource.h, hookctrl.rc については前回と同じですので省略します。
hookctrl.cは次のようになります。変更点についてはコメントを参照して下さい。

#include <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include "resource.h"
 
// hook.dll からインポート
LRESULT __declspec(dllimport) CALLBACK MyKeyboardProc(int, WPARAM, LPARAM);
void __declspec(dllimport) SetMyKeyboardProcTarget(DWORD);
 
HHOOK hHook;
 
// 指定されたEXEファイルで実行中のプロセスIDを取得する
DWORD find_proc_id(const char *exe_filename)
{
    HANDLE hProcessSnap;
    HANDLE hProcess;
    HMODULE hModule;
    DWORD cbNeeded;
    PROCESSENTRY32 pe32;
    DWORD dwProcId = 0;
    char szProcName[MAX_PATH];
 
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hProcessSnap) {
        return 0;
    }
 
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (!Process32First(hProcessSnap, &pe32)) {
        return 0;
    }
 
    do {
        hProcess = OpenProcess(
                PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                FALSE,
                pe32.th32ProcessID);
        if (NULL != hProcess) {
            if (EnumProcessModules(
                        hProcess, &hModule, sizeof(hModule), &cbNeeded)) {
                GetModuleBaseName(hProcess, hModule, szProcName, 
                        sizeof(szProcName) / sizeof(szProcName[0]));
                if (!strcmp(szProcName, exe_filename)) {
                    dwProcId = pe32.th32ProcessID;
                }
            }
            CloseHandle(hProcess);
        }
    } while (Process32Next(hProcessSnap, &pe32));
    CloseHandle(hProcessSnap);
    return dwProcId;
}
 
// ダイアログの処理は前回と同じです。
BOOL CALLBACK DialogProc(
        HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
        case WM_COMMAND:
            switch (LOWORD(wParam)) {
                case ID_HOOK_ON:
                    hHook = SetWindowsHookEx(
                            WH_KEYBOARD,
                            MyKeyboardProc,
                            GetModuleHandle("hook.dll"),
                            0);
                    if (NULL == hHook) {
                        MessageBox(hWnd, "failed.", "hook on", MB_OK);
                    } else {
                        MessageBox(hWnd, "success.", "hook on", MB_OK);
                    }
                    return TRUE;
                case ID_HOOK_OFF:
                    if (!UnhookWindowsHookEx(hHook)) {
                        MessageBox(hWnd, "failed.", "hook off", MB_OK);
                    } else {
                        MessageBox(hWnd, "success.", "hook off", MB_OK);
                    }
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hWnd, wParam);
                    return TRUE;
            }
    }
    return FALSE;
}
 
int main(int argc, char *argv[])
{
    HWND hWndDesktop = NULL;
    HINSTANCE hCurrentInst = NULL;
    UINT_PTR uDlgRet = 0;
 
    // "foo.exe"のプロセスIDを取得します。
    DWORD dwTargetPid = find_proc_id("foo.exe");
    if (0 == dwTargetPid) {
        MessageBox(NULL, 
                "foo.exe does not exists.", "hook target missing", MB_OK);
        return 1;
    }
 
    // hook.dllの共有領域に、HOOK対象プロセスIDを保存します。
    SetMyKeyboardProcTarget(dwTargetPid);
 
    // 以降は前回と同じ。
    hCurrentInst = GetModuleHandle(NULL);
    hWndDesktop = GetDesktopWindow();
    DialogBox(
            hCurrentInst, 
            MAKEINTRESOURCE(IDD_DIALOG1), 
            hWndDesktop, 
            (DLGPROC)DialogProc);
    return 0;
}

コンパイル方法:プロセスID取得の関数追加に伴い、psapi.libを追加。

> rc hookctrl.rc
> cl hookctrl.c hookctrl.res user32.lib hook.lib psapi.lib

サンプルで遊んでみる

前回と同様の手順で遊ぶことができます。
ただし、今回のサンプルではfoo.exe以外のプロセスではHOOK処理が発動しないようになりました。実際にはhook.dllはロードされてMyKeyboardProc()も呼ばれていますが、foo.exe以外のプロセスでは分岐処理でSKIPするようになります。


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-06-30 17:46:13
md5:3983c0da12ba2bc22df90a7377226a2c
sha1:6585fbf5415feacbd04c9169e79b895e7a3bf14e
コメント
コメントを投稿するにはログインして下さい。