1. 程式人生 > >C++鉤子技術,攔截帶有某些關鍵字的窗體

C++鉤子技術,攔截帶有某些關鍵字的窗體

這次想寫一篇,自己曾經做過的一個Hook程式,溫故而知新。

作為一個C++程式設計師,肯定對鉤子(Hook)技術有所瞭解:訊息鉤子,API鉤子。

基本概念:

    鉤子(Hook),是Windows訊息處理機制的一個平臺,應用程式可以在上面設定子程以監視指定視窗的某種訊息,而且所監視的視窗可以是其他程序所建立的。當訊息到達後,在目標視窗處理函式之前處理它。鉤子機制允許應用程式截獲處理window訊息或特定事件。

    鉤子實際上是一個處理訊息的程式段,通過系統呼叫,把它掛入系統。每當特定的訊息發出,在沒有到達目的視窗前,鉤子程式就先捕獲該訊息,亦即鉤子函式先得到控制權。這時鉤子函式即可以加工處理(改變)該訊息,也可以不作處理而繼續傳遞該訊息,還可以強制結束訊息的傳遞。

對於標題所描述的攔截帶有某些關鍵字窗體,基本思路是:我們需要建立一個全域性鉤子,然後捕獲到窗體顯示訊息,捕獲到後利用Windows訊息機制,丟擲去到外層獲取視窗標題,當匹配關鍵字時,給對應視窗傳送關閉訊息。

對於建立全域性鉤子,我們需要採用DLL注入的方式,Hook的DLL工程主程式碼如下:

#include <Windows.h>

#pragma data_seg("HookWnd")
HHOOK	  g_HookWnd		 = NULL;	//鉤子控制代碼
HWND      g_DestWnd		 = NULL;	//目的視窗控制代碼,就是截獲的訊息要發往什麼視窗
HINSTANCE g_hInst		 = NULL;
#pragma data_seg()

#pragma comment( linker, "/section:HookWnd,RWS" )

__declspec(dllexport) BOOL SetHook(HWND hWnd);
__declspec(dllexport) void DestroyHook();

#define WM_MYMESSAGE (WM_USER + 1)

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
{
	g_hInst = hinstDll;//儲存應用程式例項
	return TRUE;
}


LRESULT CALLBACK MyWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	CWPSTRUCT *pCwp = reinterpret_cast<CWPSTRUCT*>(lParam);
	if (pCwp->message == WM_SHOWWINDOW)
	{
		::SendMessage(g_DestWnd, WM_MYMESSAGE, wParam, reinterpret_cast<LPARAM>(pCwp->hwnd));
		return 0;
	}
	else
		return CallNextHookEx(g_HookWnd, nCode, wParam, lParam);
}

BOOL SetHook(HWND hWnd)
{
	if (NULL == hWnd)
		return FALSE;
	
	g_DestWnd = hWnd;

	//第三個引數還可通過些方法獲得:GetModuleHandle("HookTest.dll")
	return (NULL != (g_HookWnd = ::SetWindowsHookEx(WH_CALLWNDPROC, MyWndProc, g_hInst, 0)));
}

void DestroyHook()
{
	if (NULL != g_HookWnd) 
	{
		UnhookWindowsHookEx(g_HookWnd);
		g_DestWnd = NULL;
	}
}

SetWindowsHookEx(WH_CALLWNDPROC, MyWndProc, g_hInst, 0));最後一個引數0,代表建立的是全域性鉤子。

WH_CALLWNDPROC裝載的是:視窗鉤子,當系統向目標視窗傳送訊息時將觸發此鉤子 進入到MyWndProc回撥函式處理;

 當回撥函式裡面捕獲到:WM_SHOWWINDOW訊息,傳送訊息到外層處理;

外層訊息處理函式為:

LRESULT CHookTestDlg::OnMyMessage( WPARAM wParam, LPARAM lParam )
{
	HWND hwnd = reinterpret_cast<HWND>(lParam);
	UINT nTitleLen = ::GetWindowTextLength(hwnd);
	TCHAR *pStr = new TCHAR[nTitleLen + 1]();
	ZeroMemory(pStr, nTitleLen + 1);
	::GetWindowText(hwnd, pStr, nTitleLen + 1);
	CString str(pStr);
	delete []pStr;

	if (str.Find(m_str) != -1)	//CString查詢字串未找到返回-1
	{
		::SendMessage(hwnd, WM_CLOSE, NULL, NULL);
		return 1;
	}

	return 0;
}

獲取對應視窗控制代碼的視窗標題,和設定的關鍵字匹配,當滿足條件則傳送關閉訊息;

外層呼叫Hook的DLL函式例項:

HINSTANCE hIst = NULL;
// 建立全域性鉤子,監控所有程序
void CHookTestDlg::OnCreateDllHook() 
{
	m_edit.GetWindowText(m_str);
	m_edit.EnableWindow(FALSE);
	hIst = ::LoadLibrary("..\\HookTest\\HookDll.dll"); // 載入dll
	if (NULL != hIst)
	{
		typedef BOOL (*pFunSetHook)(HWND);
		pFunSetHook pSetHook = (pFunSetHook)GetProcAddress(hIst, "SetHook");
		if (NULL != pSetHook)
		{
			if(pSetHook(m_hWnd))
				AfxMessageBox("建立全域性鉤子成功...");
		}
	}
}

//解除安裝全域性鉤子
void CHookTestDlg::OnDestroyDllHook() 
{
	if (NULL != hIst)
	{
		typedef void (*pFunDestroyHook)();
		pFunDestroyHook pDestryHook = (pFunDestroyHook)GetProcAddress(hIst, "DestroyHook");
		if (NULL != pDestryHook)
		{
			pDestryHook();
			::FreeLibrary(hIst);
			hIst = NULL;
			AfxMessageBox("銷燬全域性鉤子成功!");
		}
	}
	m_edit.EnableWindow(TRUE);
}

.........

至此,一個簡單的攔截視窗程式,已經實現。