使用钩子仿照Spy++截取消息

1. 钩子

钩子是Windows留给我们的后门。对消息进行过滤,比方快捷键,程序监控键盘,获取键盘动作,再进行判断。

详细的前往:http://blog.csdn.net/sunears/article/details/1861568


2. 钩子用法

添加钩子:SetWindowsHookEx

HHOOK WINAPI SetWindowsHookEx(
  _In_  int idHook,
  _In_  HOOKPROC lpfn,
  _In_  HINSTANCE hMod,
  _In_  DWORD dwThreadId
);

idHook: 钩子类型,监控消息这里用到的是WH_CALLWNDPROC(SendMessage发送), WH_GETMESSAGE(PostMessage发送), 并且获取返回值WH_CALLWNDPROCRET.

lpfn: 监控另外的进程的线程,需要定义在DLL中的钩子回调函数

hMod: DLL句柄

dwThreadId: 监控的进程的线程ID


卸载钩子:UnhookWIndowsHookex

BOOL WINAPI UnhookWindowsHookEx(
  _In_  HHOOK hhk
);

hhk: SetWindowsHookEx返回的HHOOK


3. Spy++获取消息

监视->日志消息:

查找程序:找到窗口句柄;

消息:定义接收消息类型;

输出:定义消息的输出附加信息,这里我们加上“原始消息参数”,“原始返回值”;

示例图如下:

<Win32> 使用钩子仿照Spy++截取消息

前面为接收消息的句柄, P/S/R定义如下, 之后的不用说。

Spy++帮助:

代码 意义
P 使用 PostMessage 函数将消息发送到队列。没有可用的关于消息的最终处置的信息。
S 使用 SendMessage 函数发送消息。这意味着,发送方在接收方处理和返回该消息之前不会重新获取控制。因此,接收方可以将一个返回值传送回发送方。
s 消息已发送,但安全性阻止对返回值的访问。
R 每个“S”行都具有一个对应的列出消息返回值的“R”(返回)行。有时消息调用被嵌套,这意味着一个消息处理程序发送了另一个消息。

Spy++使用的也是钩子的方式截获的消息。

根据:http://www.yourdelphi.com/topic_372749_d537.htm

Win32程序对不同类型Message的处理过程不一样,对于Post过来的Message(通过PostMessage发送),会由GetMessage来处理,对于Send过来的Message(通过SendMessage发送),则由CallWndProc来处理,并在处理完成后执行CallWndRetProc,所以要HOOK并区分这两种Message需要同时处理三个HOOK:WH_GETMESSAGE、WH_CALLWNDPROC和WH_CALLWNDPROCRET。 
所以: 
1、‘P‘: 通过WH_GETMESSAGE可以得到PostMessage发送的Message 
2、‘S‘: 通过WH_CALLWNDPROC可以得到SendMessage发送的Message 
3、‘R‘: 通过WH_CALLWNDPROCRET则可以得到SendMessage的结果,也就是你要的IResult

所以仿照Spy++的思路已经出来了。

设置3个线程钩子,顺序监控WH_CALLWNDPROC, WH_CALLWNDPROCRET, WH_GETMESSAGE


4. 具体实现

MyCsdn.exe: 主调用程序,用户输入需要监控的exe

Shared.dll: 所有DLL共享的函数

CallWndProcHook.dll: WH_CALLWNDPROC监控 

CallWndProcRetHook.dll: WH_CALLWNDPROCRET监控

GetMessageHook.dll: WH_GETMESSAGE监控

涉及到boost,注意添加boost库。


MyCsdn.exe: 获取EXE进程ID,进而获取线程ID,加载DLL,启动钩子

#include <iostream>
#include <fstream>
#include <Windows.h>
#include <Shlobj.h>
#include <boost/tokenizer.hpp> 
#include <process.h>
#include <TlHelp32.h>
#include <tchar.h>
#include <math.h>

using namespace std;


// 获取程序主线程ID
DWORD GetThreadIdByProcessID(DWORD dwProcessId)
{
	HANDLE hThreadSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessId);
	if(hThreadSnap == INVALID_HANDLE_VALUE)
	{
		return -1;
	}
	THREADENTRY32 te32 = { sizeof(te32) };
	if(::Thread32First(hThreadSnap, &te32))
	{
		do
		{
			if(te32.th32OwnerProcessID == dwProcessId)
			{
				::CloseHandle(hThreadSnap);
				return te32.th32ThreadID;
			}
		}while(::Thread32Next(hThreadSnap, &te32));
	}

	::CloseHandle(hThreadSnap);
	return -1;
}
// 获取进程ID
DWORD GetProcessIdByName(LPCWSTR processName)
{
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
	PROCESSENTRY32 pe;          
	pe.dwSize = sizeof(PROCESSENTRY32); 
	if(!Process32First(hSnapShot,&pe))  
	{          
		return NULL;    
	}          
	BOOL clearprocess = FALSE;
	while (Process32Next(hSnapShot,&pe))  
	{                       
		if(!_tcsicmp(processName, pe.szExeFile))    
		{  
			::CloseHandle(hSnapShot);
			return pe.th32ProcessID;
		} 
	}      
	::CloseHandle(hSnapShot);
	return -1;
}

bool g_bThreadRunning = true;
void DllWinThread(LPVOID lP)
{
	g_bThreadRunning = true;

	wchar_t wstrExe[256] = {0};
	cout << "The program to hook: ";
	wcin >> wstrExe;

	DWORD dwProcessId = GetProcessIdByName(wstrExe);
	if (dwProcessId == -1)
	{
		g_bThreadRunning = false;
		system("pause");
		return;
	}
	DWORD dwThreadId = GetThreadIdByProcessID(dwProcessId);

	typedef void (*STARTHOOK)(int);
	HINSTANCE hinst = LoadLibrary(TEXT("CallWndProcHook.dll"));
	HINSTANCE hinst2 = LoadLibrary(TEXT("GetMessageHook.dll"));
	HINSTANCE hinst3 = LoadLibrary(TEXT("CallWndProcRetHook.dll"));


	STARTHOOK cwpSt = (STARTHOOK)GetProcAddress(hinst, "StartCallWndHook");
	STARTHOOK gmSt = (STARTHOOK)GetProcAddress(hinst2, "StartGetMessageHook");
	STARTHOOK cwprSt = (STARTHOOK)GetProcAddress(hinst3, "StartCallWndRetHook");

	cwpSt(dwThreadId);
	cwprSt(dwThreadId);
	gmSt(dwThreadId);

	int nTimeout = 500;
	while (nTimeout--)
	{
		Sleep(100);
	}

	FreeLibrary(hinst);
	FreeLibrary(hinst3);
	FreeLibrary(hinst2);
	g_bThreadRunning = false;
}


int main(int argc, char **argv)
{
	HANDLE hThread = CreateThread(NULL, 0, 
		(LPTHREAD_START_ROUTINE)DllWinThread,	// 线程函数
		NULL,			// 函数参数
		0, NULL);


	while (g_bThreadRunning)
	{
		Sleep(50);
	}
	CloseHandle(hThread);

 	system("pause");
	return 0;
}


Shared.dll: 使用导出符号的方式,用于其他DLL共用的函数,写入文件,消息翻译,使用到boost(我的博文:boost安装

Shared.h

#ifdef SHARED_EXPORTS
#define SHARED_API __declspec(dllexport)
#else
#define SHARED_API __declspec(dllimport)
#endif

#include <map>
#include <string>
using std::map;
using std::string;

SHARED_API void AppendDebug(char *pFilename, LPVOID pBuf, int length);
SHARED_API void ShowDebug(LPTSTR lpstrFormat, ...);
SHARED_API void InitSymbolicMsg(map<UINT, string>&);
SHARED_API const char * GetSymbolicMsg(UINT message, map<UINT, string>&m);

Shared.cpp: 

#include "stdafx.h"
#include "Shared.h"
#include <fstream>
#include <tchar.h>
#include <boost/tokenizer.hpp>  
using std::ifstream;



SHARED_API void AppendDebug(char *pFilename, LPVOID pBuf, int length)
{
	FILE *pFile;
	if (pFilename == NULL)
	{
		fopen_s(&pFile, "C:\\test.txt", "ab");
	}
	else
	{
		fopen_s(&pFile, pFilename, "ab");
	}
	if (pFile != NULL)
	{
		fwrite(pBuf, sizeof(char), length, pFile);
		fclose(pFile);
	}
}

SHARED_API void ShowDebug(LPTSTR lpstrFormat, ...) // 显示在VS的输出中
{
	va_list VAList;
	va_start(VAList, lpstrFormat);

	TCHAR szBuf[MAX_PATH * 2] = {0};
	_vstprintf_s(szBuf, MAX_PATH, lpstrFormat, VAList);

	OutputDebugString(szBuf);
}

// 十六进制字符串转为十进制
int HexToDec(string strHex)
{
	int nRet = 0;
	if (strHex.at(1) == 'x')
	{
		strHex = strHex.substr(2);
	}
	int nLen = strHex.size();
	int nBase = 1;
	int para = 0;
	char ch = 0;
	for (int i = nLen-1, bi = 0; i >= 0; i--, bi++)
	{
		ch = strHex.at(i);
		if (ch >= '0' && ch <= '9')
		{
			para = ch - '0';
		}
		else if (ch >= 'a' && ch <= 'f')
		{
			para = ch - 'a' + 10;
		}	
		else if (ch >= 'A' && ch <= 'F')
		{
			para = ch - 'A' + 10;
		}
		else	// 非法字符
		{
			return -1;
		}
		nRet += para * nBase;
		nBase *= 16;
	}

	return nRet;
}

SHARED_API void InitSymbolicMsg(map<UINT, string> &mapSymbolicMsgs)
{
	ifstream ifs;
	ifs.open("C:\\SymbolicMessages.h", std::ios::in);
	mapSymbolicMsgs.clear();

	if (!ifs.is_open())
	{
		return;
	}

	boost::char_separator<char> custcs(" ");	// 自己指定
	char line[256] = {0};
	string strLine;
	while (!ifs.eof())
	{
		ifs.getline(line, 255);
		strLine = line;
		// 使用" "空格进行分割
		if ( (strLine.find("#define") != string::npos) && (strLine.find("WM_") != string::npos))
		{
			boost::tokenizer<boost::char_separator<char> > tok(strLine, custcs);

			boost::tokenizer<boost::char_separator<char> >::iterator cit = tok.begin();
			cit++;
			if (cit == tok.end())
			{
				continue;
			}
			string msg(*cit);
			cit++;
			if (cit == tok.end())
			{
				continue;
			}
			string val(*cit);
			UINT nVal = HexToDec(val);
			if (nVal == -1)
			{
				continue;
			}
			mapSymbolicMsgs.insert(std::make_pair<UINT, string>(nVal, msg));
		}
		memset(line, 0, 256);
	}
	ifs.close();
}

SHARED_API const char * GetSymbolicMsg(UINT message, map<UINT, string> &mapSymbolicMsgs) // 消息转换为字符串
{
	map<UINT, string>::const_iterator cit = mapSymbolicMsgs.begin();
	
	for (; cit != mapSymbolicMsgs.end(); cit++)
	{
		if (cit->first == message)
		{
			return (cit->second).c_str();
		}
	}

	return ("User_Define");
}


CallWndProc.dll: 监控WH_CALLWNDPROC

dllmain.cpp:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <stdio.h>

void ShowDebug(LPTSTR lpstrFormat, ...);
void StopCallWndHook();
extern HINSTANCE g_hCwInst;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:		
		g_hCwInst = hModule;
		printf("<CallWndProc>process attach\n");  // 静态链接,动态链接LoadLibrary
		break;
	case DLL_THREAD_ATTACH:
		printf("<CallWndProc>thread attach\n");  // 线程中加载
		break;
	case DLL_THREAD_DETACH:
		printf("<CallWndProc>thread detach\n");  // 线程中卸载
		break;
	case DLL_PROCESS_DETACH:
		printf("<CallWndProc>process detach\n");   // 静态链接结束,动态链接FreeLibrary,程序退出
		StopCallWndHook();  // 卸载钩子
		break;
	}
	return TRUE;
}

CallWndProc.cpp: 

#include "stdafx.h"
#include <tchar.h>
#include <string>
#include <Windows.h>
#include <stdio.h>

#include "../Shared/Shared.h"
#pragma comment(lib, "../Debug/Shared.lib")

HINSTANCE g_hCwInst;
HHOOK g_hookCallWndProc;

LRESULT CALLBACK CallWndHookProc(
	int code,
	WPARAM wParam,
	LPARAM lParam)
{
	static map<UINT, string> mapSymbolicMsgs;   // 不能共享DLL的全局变量
	if (code == HC_ACTION) 
	{
		PCWPSTRUCT pMsg = (PCWPSTRUCT)lParam;		// WH_CALLWNDPROC
		char buffer[200] = {0};
		if (mapSymbolicMsgs.size() == 0)
		{
			InitSymbolicMsg(mapSymbolicMsgs);
		}
		sprintf_s(buffer, "%08X S Msg: %s(%04X), wParam: %08X, lParam: %08X\n", pMsg->hwnd, 
			GetSymbolicMsg(pMsg->message, mapSymbolicMsgs), pMsg->message, (int)pMsg->wParam, (int)pMsg->lParam);
		AppendDebug(NULL, buffer, strlen(buffer));
	}
	return CallNextHookEx(NULL, code, wParam, lParam);
}


void StartCallWndHook(int threadId)
{
	g_hookCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, CallWndHookProc, g_hCwInst, threadId);
}

void StopCallWndHook()
{
	UnhookWindowsHookEx(g_hookCallWndProc);
}

CallWndProc.def: 输出函数, 用于主程序调用

LIBRARY
EXPORTS
StartCallWndHook @1


CallWndProcRetHook.dll, GetMessageHook.dll同理,不再赘述。

获取而得的文件片段:

00000000 P Msg: WM_TIMER(0113), wParam: 00006CFA, lParam: 76C718B2
00000000 P Msg: WM_TIMER(0113), wParam: 00006CFA, lParam: 76C718B2
000A0956 S Msg: User_Define(036A), wParam: 00000000, lParam: 00000000
000A0956 R Msg: User_Define(036A), lResult: 00000000
00060938 P Msg: WM_TIMER(0113), wParam: 00000001, lParam: 00000000
000A0956 S Msg: User_Define(036A), wParam: 00000000, lParam: 00000000
000A0956 R Msg: User_Define(036A), lResult: 00000000
00060938 S Msg: WM_WINDOWPOSCHANGING(0046), wParam: 00000000, lParam: 004BF6B4
00060938 R Msg: WM_WINDOWPOSCHANGING(0046), lResult: 00000000
000C0944 S Msg: WM_WINDOWPOSCHANGING(0046), wParam: 00000000, lParam: 004BF6B4

详见:工程下载地址


<Win32> 使用钩子仿照Spy++截取消息

上一篇:C# 命名空间与语句


下一篇:WPF利用通过父控件属性来获得绑定数据源RelativeSource