在Windows系统中,消息钩子是一种能够获取和修改系统消息的机制。其中,WH_GETMESSAGE钩子是最常用的一种,它允许钩子程序获取发送到某个窗口的消息,包括用户输入、系统事件等。
下面的示例使用带有WH_GETMESSAGE钩子类型的SetWindowsHookEx将DLL注入到找到的第一个notepad进程中,并监视输入的所有键。这些键被发送到监视应用程序,该应用程序有效地查看用户在记事本中所做的每个按键。
这个示例包含两个项目。注入可执行文件(HookInject)和通过SetWindowsHookEx间接注入的DLL(HookDll).
要测试这个示例,首先运行notepad,然后运行HookInject。在记事本中输入。将在HookInject的控制台窗口中看到相同的文本.
其中,HookInject代码如下:
// HookInject.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <stdio.h>
#include <windows.h>
#include <assert.h>
#include <TlHelp32.h>
#include <Psapi.h>
int Error(const char* text) {
printf("%s (%u)\n", text, ::GetLastError());
return 1;
}
DWORD FindMainNotepadThread() {
auto hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return 0;
DWORD tid = 0;
THREADENTRY32 th32;
th32.dwSize = sizeof(th32);
::Thread32First(hSnapshot, &th32);
do {
auto hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, th32.th32OwnerProcessID);
if (hProcess) {
WCHAR name[MAX_PATH];
if (::GetProcessImageFileName(hProcess, name, MAX_PATH) > 0) {
auto bs = ::wcsrchr(name, L'\\');
if (bs && ::_wcsicmp(bs, L"\\notepad.exe") == 0) {
tid = th32.th32ThreadID;
}
}
::CloseHandle(hProcess);
}
} while (tid == 0 && ::Thread32Next(hSnapshot, &th32));
::CloseHandle(hSnapshot);
return tid;
}
int main() {
DWORD tid = FindMainNotepadThread();
if (tid == 0)
return Error("Failed to locate Notepad");
auto hDll = ::LoadLibrary(L"HookDll");
if (!hDll)
return Error("Failed to locate Dll\n");
using PSetNotify = void (WINAPI*)(DWORD, HHOOK);
auto setNotify = (PSetNotify)::GetProcAddress(hDll, "SetNotificationThread");
if (!setNotify)
return Error("Failed to locate SetNotificationThread function in DLL");
auto hookFunc = (HOOKPROC)::GetProcAddress(hDll, "HookFunction");
if (!hookFunc)
return Error("Failed to locate HookFunction function in DLL");
auto hHook = ::SetWindowsHookEx(WH_GETMESSAGE, hookFunc, hDll, tid);
if (!hHook)
return Error("Failed to install hook");
setNotify(::GetCurrentThreadId(), hHook);
::PostThreadMessage(tid, WM_NULL, 0, 0);
MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
if (msg.message == WM_APP) {
printf("%c", (int)msg.wParam);
if (msg.wParam == 13)
printf("\n");
}
}
::UnhookWindowsHookEx(hHook);
::FreeLibrary(hDll);
return 0;
}
HookDll代码如下:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#pragma data_seg(".shared")
DWORD g_ThreadId = 0;
HHOOK g_hHook = nullptr;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,RWS")
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, PVOID pReserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
::DisableThreadLibraryCalls(hModule);
break;
case DLL_PROCESS_DETACH:
::PostThreadMessage(g_ThreadId, WM_QUIT, 0, 0);
break;
}
return TRUE;
}
extern "C" LRESULT CALLBACK HookFunction(int code, WPARAM wParam, LPARAM lParam) {
if (code == HC_ACTION) {
auto msg = (MSG*)lParam;
if (msg->message == WM_CHAR) {
::PostThreadMessage(g_ThreadId, WM_APP, msg->wParam, msg->lParam);
// prevent A characters from getting to the app
//if (msg->wParam == 'A' || msg->wParam == 'a')
// msg->wParam = 0;
}
}
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
extern "C" void WINAPI SetNotificationThread(DWORD threadId, HHOOK hHook) {
g_ThreadId = threadId;
g_hHook = hHook;
}