<Win32> 使用钩子仿照Spy++截取消息
2015-01-21 12:06
369 查看
1. 钩子
钩子是Windows留给我们的后门。对消息进行过滤,比方快捷键,程序监控键盘,获取键盘动作,再进行判断。详细的前往:http://blog.csdn.net/sunears/article/details/1861568
2. 钩子用法
添加钩子:SetWindowsHookExHHOOK 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”(返回)行。有时消息调用被嵌套,这意味着一个消息处理程序发送了另一个消息。 |
根据: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: 主调用程序,用户输入需要监控的exeShared.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
详见:工程下载地址
相关文章推荐
- < merge />的使用方法
- 从外部的js文件中使用<%=%>获取ASPX页面的ClientID获取后台代码
- 解决 spring mvc 3.0 结合 hibernate3.2 使用<tx:annotation-driven>声明式事务无法提交的问题
- vs2005中使用'查找和替换'将 (&lt;link &gt;) 替换为 (&lt;link /&gt;)
- android_应用开发之(使用<include>标签重用布局)
- <mx:itemRenderer>使用
- <转>学习java反编译工具的使用
- Maatkit工具使用<二>之mysql重复索引检测工具
- JSP中使用哪个标签允许向<include/>标签传递参数
- thinkphp 在循环内使用<php></php>
- android中使用TextView来显示某个网址的内容,使用<ScrollView>来生成下拉列表框
- Android UI 优化 使用<include/>和 <merge />标签
- <Ibatis in action>中使用动态SQL的一个小细节提示(与CDATA)
- <activity>元素的android:launchMod属性的使用
- 为何要使用<merge\>标签
- <配置> Ubuntu gcc 安装 使用方法
- 在使用struts标签时,把<s:if>中的test写成了text
- <备忘1000> ffmpeg命令行使用例子
- <WP7>(六)手把手教你写天气预报程序:使用Isolatedstorage保存设置
- &lt;转载自刘佳ID:freedom0203和waret&gt; C++中成员初始化列表的使用