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
钩子回调函数:
typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);
回调函数自定义消息处理。参看
Windows示例。
调用完成之后,依据需要调用 CallNextHookEx , 传给其他钩子。
卸载钩子:UnhookWIndowsHookex
BOOL WINAPI UnhookWindowsHookEx(
_In_ HHOOK hhk
);
hhk: SetWindowsHookEx返回的HHOOK
3. 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
详见:工程下载地址