目標:從我們的應用程式攔截另一個進程 (MSN Messenger) 的視窗訊息。難點:雙方擁有獨立虛擬位址空間,無法直接修改目標進程的視窗程序。
解法:**Windows Hook + DLL注入**。Hook機制允許將自訂DLL注入目標進程位址空間,在該進程上下文中執行攔截邏輯。
#pragma data_seg("Shared") // 命名共享區段
HHOOK g_hhook = NULL;
DWORD g_dwThreadIdMsn = 0;
POINT g_MouseLoc = {0,0};
char g_text[256] = {0};
HWND g_Hwnd = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws") // 告訴連結器:可讀寫共享
所有注入進程的DLL實例映射到同一塊物理記憶體,實現跨進程全域變數共享。
HINSTANCE g_hinstDll = NULL;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason, LPVOID) {
if (ul_reason == DLL_PROCESS_ATTACH) g_hinstDll = (HINSTANCE)hModule;
return TRUE;
}
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam); // 前置宣告
BOOL WINAPI SetMsnHook(DWORD dwThreadId) {
if (dwThreadId == 0) return FALSE;
g_dwThreadIdMsn = GetCurrentThreadId(); // 記錄註入者執行緒ID
// 安裝WH_MOUSE Hook,攔截滑鼠訊息
g_hhook = SetWindowsHookEx(WH_MOUSE, GetMsgProc, g_hinstDll, dwThreadId);
if (!g_hhook) return FALSE;
// 發送空訊息確保Hook鏈啟動
return PostThreadMessage(dwThreadId, WM_NULL, 0, 0);
}
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
MOUSEHOOKSTRUCT* pMouse = (MOUSEHOOKSTRUCT*)lParam;
g_MouseLoc = pMouse->pt; // 記錄滑鼠位置
// 取得滑鼠下的視窗控制代碼
HWND hWnd = WindowFromPoint(pMouse->pt);
if (hWnd) {
// 取得視窗文字
SendMessage(hWnd, WM_GETTEXT, 256, (LPARAM)g_text);
// 竄改視窗文字 (示範用)
SendMessage(hWnd, WM_SETTEXT, 256, (LPARAM)g_text);
g_Hwnd = hWnd;
}
}
return CallNextHookEx(g_hhook, nCode, wParam, lParam); // 必須傳遞給下一個Hook
}
void WINAPI GetText(int& x, int& y, char** ptext) {
x = g_MouseLoc.x; y = g_MouseLoc.y; *ptext = g_text;
}
HWND WINAPI GetMyHwnd() { return g_Hwnd; }
#include "MSNHook.h"
#pragma comment(lib, "MSNHook.lib") // 連結導入庫
__declspec(dllimport) BOOL WINAPI SetMsnHook(DWORD);
__declspec(dllimport) void WINAPI GetText(int&, int&, char**);
__declspec(dllimport) HWND WINAPI GetMyHwnd();
void CTestMSNHookDlg::OnBnClickedOk() {
// Spy++可見MSN聊天視窗類別為 "IMWindowClass"
CWnd* pMsnWin = FindWindow(_T("IMWindowClass"), NULL);
if (!pMsnWin) return;
// 取得該視窗所屬執行緒ID
DWORD dwThreadId = GetWindowThreadProcessId(pMsnWin->GetSafeHwnd(), NULL);
// 安裝Hook (此時DLL注入MSN進程)
SetMsnHook(dwThreadId);
MSG msg; GetMessage(&msg, NULL, 0, 0); // 訊息迴路保持Hook存活
SetTimer(101, 100, NULL); // 定時器輪詢共享資料
}
void CTestMSNHookDlg::OnTimer(UINT_PTR) {
char* pText = NULL; int x=0, y=0;
GetText(x, y, &pText);
if (x==0 && y==0) return;
CString str; str.Format(_T("%d:%d:%s"), x, y, pText);
m_Edit = str; UpdateData(FALSE);
// 同步顯示視窗文字
HWND hWnd = GetMyHwnd();
if (hWnd) {
CWnd* pWnd = CWnd::FromHandle(hWnd);
pWnd->GetWindowText(m_Edit);
}
}
void CTestMSNHookDlg::OnBnClickedButton1() { // 停止
KillTimer(101);
SetMsnHook(0); // 協助DLL卸載
OnCancel();
}
| 技術點 | 說明 |
|---|---|
| WH_MOUSE | 攔截滑鼠訊息 (移動、按下、釋放、雙擊等) |
| SetWindowsHookEx | 參數:Hook類型、處理函數、DLL實例、目標執行緒ID (0=全系統) |
| 共享資料區 | #pragma data_seg + linker /section:rws 實現跨進程全域變數 |
| CallNextHookEx | **必須呼叫**,維持Hook鏈完整性,否則系統不穩定 |
| DLL_PROCESS_ATTACH | 每個被注入進程皆會觸發,初始化g_hinstDll |
| PostThreadMessage(WM_NULL) | 喚醒目標執行緒訊息迴路,確保Hook生效 |
WH_GETMESSAGE攔截所有訊息、WH_CALLWNDPROC監控視窗程序呼叫、結合UI Automation/API Hooking取得編輯控制項內容Windows Hook + DLL注入是跨進程訊息攔截的經典技術。核心在於:共享資料區實現跨進程通訊、SetWindowsHookEx將DLL注入目標執行緒、Hook處理函數在目標進程上下文執行。範例雖簡單,但完整展示了從DLL建立、共享記憶體設計、Hook安裝/卸載到主程式協調的完整生命週期。