標籤:hook
Hook(鉤子)就是對Windows系統的一些訊息或是API函數進行攔截和監控的處理平台,讓可以根據程式員的設定擷取其感興趣的資訊。
這裡主要是介紹一下Hook攔截滑鼠訊息和鍵盤訊息。
下面是CALLBACK Proc 回呼函數 和 CallNextHookEx函數
LRESULT CALLBACK HookProc
(
int nCode, //指定是否需要處理該訊息
WPARAM wParam,
LPARAM lParam //包含該訊息的附加訊息 ,
);
這個回呼函數的名字可以隨你取,但形式可一定要滿足以上要求,其實鉤子的回呼函數和Windows的差不多一個德行。看看鉤子函數的返回值,若是返回非0值,表示我們已經自己處理了該訊息,則訊息就不被傳遞到目標視窗過程。
看看LRESULT CallNextHookEx
(HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;)這個函數把鉤子資訊傳遞給下一個鉤子函數,也就是可以理解成把車輛允許存取到下一個檢查站,這個可以根據自己的需要進行調用。若是我們只設定了一個鉤子函數,那麼我們假設把鉤子訊息用CallNextHookEx傳給下個鉤子函數,因為不存在所以就傳遞迴了目標視窗函數。
先建立一個MFC BaseDialog 工程
XXXDlg.cpp代碼
// MbHookDlg.cpp : implementation file//#include "stdafx.h"#include "MbHook.h"#include "MbHookDlg.h"#include "afxdialogex.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialogEx{public:CAboutDlg();// Dialog Dataenum { IDD = IDD_ABOUTBOX };protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support// Implementationprotected:DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP()// CMbHookDlg dialogCMbHookDlg::CMbHookDlg(CWnd* pParent /*=NULL*/): CDialogEx(CMbHookDlg::IDD, pParent){m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}void CMbHookDlg::DoDataExchange(CDataExchange* pDX){CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CMbHookDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()END_MESSAGE_MAP()<span style="color:#3333ff;"><////////////////////////////////////////////////////////////Hook的過程函數HHOOK hook;HWND hWnd;LRESULT CALLBACK MessageBoxProc(INT nCode,WPARAM wParam,LPARAM lParam){if(nCode<0)return CallNextHookEx(hook,nCode,wParam,lParam);if (wParam == VK_F3)//當按“F3”鍵時卸載Hook{EndDialog(hWnd,NULL);//關閉視窗UnhookWindowsHookEx(hook);//卸載HOOk函數}else{return 1;}}</span>// CMbHookDlg message handlersBOOL CMbHookDlg::OnInitDialog(){CDialogEx::OnInitDialog();// Add "About..." menu item to system menu.// IDM_ABOUTBOX must be in the system command range.ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){BOOL bNameValid;CString strAboutMenu;bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);ASSERT(bNameValid);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// Set the icon for this dialog. The framework does this automatically// when the application's main window is not a dialogSetIcon(m_hIcon, TRUE);// Set big iconSetIcon(m_hIcon, FALSE);// Set small icon// TODO: Add extra initialization here<span style="color:#3333ff;">hWnd= m_hWnd;hook=SetWindowsHookEx(//WH_MOUSE//設定滑鼠鉤子WH_KEYBOARD//設定鍵盤鉤子,MessageBoxProc,NULL,GetCurrentThreadId());</span>return TRUE; // return TRUE unless you set the focus to a control}void CMbHookDlg::OnSysCommand(UINT nID, LPARAM lParam){if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}}// If you add a minimize button to your dialog, you will need the code below// to draw the icon. For MFC applications using the document/view model,// this is automatically done for you by the framework.void CMbHookDlg::OnPaint(){if (IsIconic()){CPaintDC dc(this); // device context for paintingSendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// Center icon in client rectangleint cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// Draw the icondc.DrawIcon(x, y, m_hIcon);}else{CDialogEx::OnPaint();}}// The system calls this function to obtain the cursor to display while the user drags// the minimized window.HCURSOR CMbHookDlg::OnQueryDragIcon(){return static_cast<HCURSOR>(m_hIcon);}
SetWindowsHook() 安裝Hook:
HHOOK SetWindowsHookEx(
int idHook, // 鉤子的類型,即它處理的訊息類型HOOKPROC lpfn, // 鉤子子程的地址指標。如果dwThreadId參數為0// 或是一個由別的進程建立的線程的標識,// lpfn必須指向DLL中的鉤子子程。// 除此以外,lpfn可以指向當前進程的一段鉤子子程代碼。// 鉤子函數的入口地址,當鉤子鉤到任何訊息後便調用這個函數。HINSTANCE hMod, // 應用程式執行個體的控制代碼。標識包含lpfn所指的子程的DLL。// 如果dwThreadId 標識當前進程建立的一個線程,// 而且子程代碼位於當前進程,hMod必須為NULL。// 可以很簡單的設定其為本應用程式的執行個體控制代碼。DWORD dwThreadId // 與安裝的鉤子子程相關聯的線程的標識符。// 如果為0,鉤子子程與所有的線程關聯,即為全域鉤子。);idHook參數可以是以下的選擇:
每一種類型的Hook可以使應用程式能夠監視不同類型的系統訊息處理機制。下面描述所有可以利用的Hook類型。1、
WH_CALLWNDPROC和
WH_CALLWNDPROCRET HooksWH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以監視發送到視窗過程的訊息。系統在訊息發送到接收視窗過程之前調用WH_CALLWNDPROC Hook子程,並且在視窗過程處理完訊息之後調用WH_CALLWNDPROCRET Hook子程。WH_CALLWNDPROCRET Hook傳遞指標到CWPRETSTRUCT結構,再傳遞到Hook子程。CWPRETSTRUCT結構包含了來自處理訊息的視窗過程的返回值,同樣也包括了與這個訊息關聯的訊息參數。2、
WH_CBT Hook在以下事件之前,系統都會調用WH_CBT Hook子程,這些事件包括:1)啟用,建立,銷毀,最小化,最大化,移動,改變尺寸等視窗事件;2)完成系統指令;3)來自系統訊息佇列中的移動滑鼠,鍵盤事件;4)設定輸入焦時間點事件;5)同步系統訊息佇列事件。Hook子程的返回值確定系統是否允許或者防止這些操作中的一個。3、
WH_DEBUG Hook在系統調用系統中與其他Hook關聯的Hook子程之前,系統會調用WH_DEBUG Hook子程。你可以使用這個Hook來決定是否允許系統調用與其他Hook關聯的Hook子程。4、
WH_FOREGROUNDIDLE Hook當應用程式的前台線程處於空閑狀態時,可以使用WH_FOREGROUNDIDLE Hook執行低優先順序的任務。當應用程式的前台線程大概要變成空閑狀態時,系統就會調用WH_FOREGROUNDIDLE Hook子程。5、
WH_GETMESSAGE Hook應用程式使用WH_GETMESSAGE Hook來監視從GetMessage or PeekMessage函數返回的訊息。你可以使用WH_GETMESSAGE Hook去監視滑鼠和鍵盤輸入,以及其他發送到訊息佇列中的訊息。6、
WH_JOURNALPLAYBACK HookWH_JOURNALPLAYBACK Hook使應用程式可以插入訊息到系統訊息佇列。可以使用這個Hook回放通過使用WH_JOURNALRECORD Hook記錄下來的連續的滑鼠和鍵盤事件。只要WH_JOURNALPLAYBACK Hook已經安裝,正常的滑鼠和鍵盤事件就是無效的。WH_JOURNALPLAYBACK Hook是全域Hook,它不能象線程特定Hook一樣使用。WH_JOURNALPLAYBACK Hook返回逾時值,這個值告訴系統在處理來自回放Hook當前訊息之前需要等待多長時間(毫秒)。這就使Hook可以控制即時事件的回放。WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。7、
WH_JOURNALRECORD HookWH_JOURNALRECORD Hook用來監視和記錄輸入事件。典型的,可以使用這個Hook記錄連續的滑鼠和鍵盤事件,然後通過使用WH_JOURNALPLAYBACK Hook來回放。WH_JOURNALRECORD Hook是全域Hook,它不能象線程特定Hook一樣使用。WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。8、
WH_KEYBOARD Hook在應用程式中,WH_KEYBOARD Hook用來監視WM_KEYDOWN and WM_KEYUP訊息,這些訊息通過GetMessage or PeekMessage function返回。可以使用這個Hook來監視輸入到訊息佇列中的鍵盤訊息。9、
WH_KEYBOARD_LL HookWH_KEYBOARD_LL Hook監視輸入到線程訊息佇列中的鍵盤訊息。10、
WH_MOUSE HookWH_MOUSE Hook監視從GetMessage 或者 PeekMessage 函數返回的滑鼠訊息。使用這個Hook監視輸入到訊息佇列中的滑鼠訊息。11、
WH_MOUSE_LL HookWH_MOUSE_LL Hook監視輸入到線程訊息佇列中的滑鼠訊息。12、
WH_MSGFILTER 和
WH_SYSMSGFILTER HooksWH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以監視菜單,捲軸,訊息框,對話方塊訊息並且發現使用者使用ALT+TAB or ALT+ESC 按鍵組合切換視窗。WH_MSGFILTER Hook只能監視傳遞到菜單,捲軸,訊息框的訊息,以及傳遞到通過安裝了Hook子程的應用程式建立的對話方塊的訊息。WH_SYSMSGFILTER Hook監視所有應用程式訊息。WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以在模式迴圈期間過濾訊息,這等價於在主訊息迴圈中過濾訊息。通過調用CallMsgFilter function可以直接的調用WH_MSGFILTER Hook。通過使用這個函數,應用程式能夠在模式迴圈期間使用相同的代碼去過濾訊息,如同在主訊息迴圈裡一樣。13、
WH_SHELL Hook外殼應用程式可以使用WH_SHELL Hook去接收重要的通知。當外殼應用程式是啟用的並且當頂層視窗建立或者銷毀時,系統調用WH_SHELL Hook子程。WH_SHELL 共有5鐘情況:1)只要有個top-level、unowned 視窗被產生、起作用、或是被摧毀;2)當Taskbar需要重畫某個按鈕;3)當系統需要顯示關於Taskbar的一個程式的最小化形式;4)當如今的鍵盤配置狀態改變;5)當使用者按Ctrl+Esc去執行Task Manager(或相同層級的程式)。按照慣例,外殼應用程式都不接收WH_SHELL訊息。所以,在應用程式能夠接收WH_SHELL訊息之前,應用程式必須調用SystemParametersInfo function註冊它自己。也就是說,如果你的App沒有調用如下代碼註冊自己之前,那麼它永遠也不會響應你的ShellProc:
//MINIMIZEDMETRICS mm = {sizeof(MINIMIZEDMETRICS), 0,0, ARW_HIDE};
MINIMIZEDMETRICS mm = {sizeof(MINIMIZEDMETRICS), 0,0, 0, ARW_HIDE};
SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS) , &mm, 0);
Hook 簡單的執行個體--攔截滑鼠和鍵盤訊息(一)