天天看點

hook使用指南(一)

1.Hooks

 hook指出了系統消息處理機制。利用hook,可以在應用程式中安裝子程式監視系統和程序之間的消息傳遞,這個監視過程是在消息到達目的視窗過程之前。

 下面簡述hook,并且解釋在Win32系統下,如何使用hook程式設計。

2.About Hooks

 hook将使程式效率降低,因為它們增加了系統必須處理的消息總數。你應該在需要時才使用,并及時删除它。我将以下面的主題描述hook。

 Hook Chains(hook鍊)

    系統支援很多不同類型的hooks;不同的hook提供不同的消息處理機制。比如,應用程式可以使用WH_MOUSE_hook來監視滑鼠消息的傳遞。

    系統為不同類型的hook提供單獨的hook鍊。hook鍊是一個指針清單,這個清單的指針指向指定的,應用程式定義的,被hook過程調用的回調函數。當與指定的hook類型關聯的消息發生時,系統就把這個消息傳遞到hook過程。一些hook過程可以隻監視消息,或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個hook過程或者目的視窗。

 Hook Procedures(hook過程)

    為了利用特殊的hook類型,開發者提供了hook過程,使用SetWindowsHookEx函數來把hook過程安裝到關聯的hook鍊。hook過程必須按照以下的文法:

    LRESULT CALLBACK HookProc(

     int nCode,

     WPARAM wParam,

     LPARAM lParam

        );

    HookProc是應用程式定義的名字。

    nCode參數是hook代碼,hook過程使用這個參數來确定任務。這個參數的值依賴于hook類型,每一種hook都有自己的hook代碼特征字元集。wParam和lParam參數的值依賴于hook代碼,但是它們的典型值是包含了關于發送或者接收消息的資訊。

    SetWindowsHookEx函數總是在hook鍊的開頭安裝hook過程。當指定類型的hook監視的事件發生時,系統就調用與這個hook關聯的hook鍊的開頭的hook過程。每一個hook鍊中的hook過程都決定是否把這個事件傳遞到下一個hook過程。hook過程傳遞事件到下一個hook過程需要調用CallNextHookEx函數。

    有些類型hook的hook過程隻能監視消息,不管是否調用了CallNextHookEx函數,系統都把消息傳遞到每一個hook過程。

    全局hook監視同一桌面的所有線程。而特定線程的hook隻能監視單獨的線程。全局hook過程可以被同一桌面的任何應用程式調用,就象調用線程一樣,是以這個過程必須和DLL子產品分開。特定線程hook過程隻可以被相關線程調用。隻有在有調試目的的時候才使用全局hook,應該避免使用,全局hook損害了系統性能。

 Hook Types

    每一種類型的hook可以使應用程式能夠監視不同類型的系統消息處理機制。下面描述所有可以利用的hook類型。

 WH_CALLWNDPROC and WH_CALLWNDPROCRET Hooks

    WH_CALLWNDPROC and WH_CALLWNDPROCRET Hook使你可以監視發送到視窗過程的消息。系統在消息發送到接收視窗過程之前調用WH_CALLWNDPROC hook過程,并且在視窗過程處理完消息之後調用WH_CALLWNDPROCRET Hook過程。

    WH_CALLWNDPROCRET Hook傳遞指針到CWPRETSTRUCT結構,再傳遞到hook過程。CWPRETSTRUCT結構包含了來自處理消息的視窗過程的傳回值,同樣也包括了與這個消息關聯的消息參數。

 WH_CBT Hook

    在以下事件之前,系統都會調用WH_CBT Hook過程,這些事件包括:激活,建立,銷毀,最小化,最大化,移動,改變尺寸等視窗事件;完成系統指令;來自系統消息隊列中的移動滑鼠,鍵盤事件;設定輸入焦點事件;同步系統消息隊列事件。hook過程的傳回值确定系統是否允許或者防止這些操作中的一個。

 WH_DEBUG Hook

    在系統調用系統中與其他hook關聯的hook過程之前,系統會調用WH_DEBUG Hook過程。你可以使用這個hook來決定是否允許系統調用與其他hook關聯的hook過程。

 WH_FOREGROUNDIDLE Hook

      當應用程式的前景線程處于空閑狀态時,可以使用WH_FOREGROUNDIDLE Hook執行低優先級的任務。當應用程式的前景線程大概要變成空閑狀态時,系統就會調用WH_FOREGROUNDIDLE Hook過程。

 WH_GETMESSAGE Hook

    應用程式使用WH_GETMESSAGE Hook來監視從GetMessage or PeekMessage函數傳回的消息。你可以使用WH_GETMESSAGE Hook去監視滑鼠和鍵盤輸入,以及其他發送到消息隊列中的消息。

 WH_JOURNALPLAYBACK Hook

    WH_JOURNALPLAYBACK Hook使應用程式可以插入消息到系統消息隊列。可以使用這個hook回放通過使用WH_JOURNALRECORD hook記錄下來的連續的滑鼠和鍵盤事件。隻要WH_JOURNALPLAYBACK hook已經安裝,正常的滑鼠和鍵盤事件就是無效的。WH_JOURNALPLAYBACK hook是全局hook,它不能象線程特定hook一樣使用。WH_JOURNALPLAYBACK hook傳回逾時值,這個值告訴系統在處理來自回放hook目前消息之前需要等待多長時間(毫秒)。這就使hook可以控制實時事件的回放。

 WH_JOURNALRECORD Hook

    WH_JOURNALRECORD Hook用來監視和記錄輸入事件。典型的,可以使用這個hook記錄連續的滑鼠和鍵盤事件,然後通過使用WH_JOURNALPLAYBACK Hook來回放。WH_JOURNALRECORD hook是全局hook,它不能象線程特定hook一樣使用。

 WH_KEYBOARD Hook

    在應用程式中,WH_KEYBOARD Hook用來監視WM_KEYDOWN and WM_KEYUP消息,這些消息通過GetMessage or PeekMessage function傳回。可以使用這個hook來監視輸入到消息隊列中的鍵盤消息。

 WH_KEYBOARD_LL Hook

    WH_KEYBOARD_LL Hook監視輸入到線程消息隊列中的鍵盤消息。

 WH_MOUSE Hook

    WH_MOUSE Hook監視從GetMessage or PeekMessage function傳回的滑鼠消息。使用這個hook監視輸入到消息隊列中的滑鼠消息。

 WH_MOUSE_LL Hook

    WH_MOUSE_LL Hook監視輸入到線程消息隊列中的滑鼠消息。

 WH_MSGFILTER and WH_SYSMSGFILTER Hooks

    WH_MSGFILTER and WH_SYSMSGFILTER Hooks使我們可以監視菜單,滾動條,消息框,對話框消息并且發現使用者使用ALT+TAB or ALT+ESC 組合鍵切換視窗。WH_MSGFILTER hook隻能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到通過安裝了hook過程的應用程式建立的對話框的消息。WH_SYSMSGFILTER Hook監視所有應用程式消息。

    WH_MSGFILTER and WH_SYSMSGFILTER Hooks使我們可以在模式循環期間過濾消息,這等價于在主消息循環中過濾消息。

    通過調用CallMsgFilter function可以直接的調用WH_MSGFILTER hook。通過使用這個函數,應用程式能夠在模式循環期間使用相同的代碼去過濾消息,如同在主消息循環裡一樣。

 WH_SHELL Hook

    外殼應用程式可以使用WH_SHELL Hook去接收重要的通知。當外殼應用程式是激活的并且當頂層視窗建立或者銷毀時,系統調用WH_SHELL Hook過程。

    按照慣例,外殼應用程式都不接收WH_SHELL消息。是以,在應用程式能夠接收WH_SHELL消息之前,應用程式必須調用SystemParametersInfo function注冊它自己。

3.Using Hooks

 Installing and Releasing Hook Procedures

  可以使用SetWindowsHookEx function安裝hook過程并且指定hook類型,指定是否需要把hook過程與所有線程關聯,或者關聯指定的線程,并且指向hook過程入口點。

  必須把全局hook過程放進DLL,以和應用程式安裝的hook過程分開。在應用程式安裝hook過程之前,它必須有一個指向DLL子產品的句柄。為了得到這個句柄,可以在調用LoadLibrary函數時使用DLL名字參數。在得到這個句柄以後,可以調用GetProcAddress函數來得到hook過程的指針。最後,使用SetWindowsHookEx函數安裝hook過程位址進應用程式hook鍊。這個過程可以用下面的事例說明:

 HOOKPROC hkprcSysMsg;

 static HINSTANCE hinstDLL;

 static HHOOK hhookSysMsg;

 hinstDLL = LoadLibrary((LPCTSTR) "c://windows//sysmsg.dll"); file://loading DLL

 hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); file://get address

 hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,hkprcSysMsg,hinstDLL,0); file://install hook

  當應用程式不再需要與特定線程相關hook時,需要調用UnhookWindowsHookEx函數删除相關的hook過程。對于全局hook,也需要調用UnhookWindowsHookEx函數,但是這個函數不能釋放DLL包含的hook過程。這是因為全局hook過程是被所有應用程式程序調用的,這就導緻了所有的程序都隐性的調用了LoadLibrary函數。是以必須調用FreeLibrary函數釋放DLL。

 Monitoring System Events

  下面的例子使用了不同的特定線程hook過程去監視系統事件。它示範了怎樣使用下面的hook過程去處理事件:

   WH_CALLWNDPROC

   WH_CBT

   WH_DEBUG

   WH_GETMESSAGE

   WH_KEYBOARD

   WH_MOUSE

   WH_MSGFILTER

  使用者可以通過使用菜單安裝或者移走hook過程。當hook過程已經安裝并且過程監視的時間發生時,hook過程将在應用程式主視窗客戶區寫出事件資訊。原代碼如下:

#define NUMHOOKS 7

// Global variables

typedef struct _MYHOOKDATA

{

    int nType;

    HOOKPROC hkprc;

    HHOOK hhook;

} MYHOOKDATA;

MYHOOKDATA myhookdata[NUMHOOKS];

LRESULT WINAPI MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam,

    LPARAM lParam)

{

    static BOOL afHooks[NUMHOOKS];

    int index;

    static HMENU hmenu;

    switch (uMsg)

    {

        case WM_CREATE:

            // Save the menu handle.

            hmenu = GetMenu(hwndMain);

            // Initialize structures with hook data. The menu-item

            // identifiers are defined as 0 through 6 in the

            // header file. They can be used to identify array

            // elements both here and during the WM_COMMAND

            // message.

            myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC;

            myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc;

            myhookdata[IDM_CBT].nType = WH_CBT;

            myhookdata[IDM_CBT].hkprc = CBTProc;

            myhookdata[IDM_DEBUG].nType = WH_DEBUG;

            myhookdata[IDM_DEBUG].hkprc = DebugProc;

            myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE;

            myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc;

            myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD;

            myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc;

            myhookdata[IDM_MOUSE].nType = WH_MOUSE;

            myhookdata[IDM_MOUSE].hkprc = MouseProc;

            myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER;

            myhookdata[IDM_MSGFILTER].hkprc = MessageProc;

            // Initialize all flags in the array to FALSE.

            memset(afHooks, FALSE, sizeof(afHooks));

            return 0;

        case WM_COMMAND:

            switch (LOWORD(wParam))

            {

                 // The user selected a hook command from the menu.

                case IDM_CALLWNDPROC:

                case IDM_CBT:

                case IDM_DEBUG:

                case IDM_GETMESSAGE:

                case IDM_KEYBOARD:

                case IDM_MOUSE:

                case IDM_MSGFILTER:

                    // Use the menu-item identifier as an index

                    // into the array of structures with hook data.

                    index = LOWORD(wParam);

                    // If the selected type of hook procedure isn't

                    // installed yet, install it and check the

                    // associated menu item.

                    if (!afHooks[index])

                    {

                        myhookdata[index].hhook = SetWindowsHookEx(

                            myhookdata[index].nType,

                            myhookdata[index].hkprc,

                            (HINSTANCE) NULL, GetCurrentThreadId());

                        CheckMenuItem(hmenu, index,

                            MF_BYCOMMAND | MF_CHECKED);

                        afHooks[index] = TRUE;

                    }

                    // If the selected type of hook procedure is

                    // already installed, remove it and remove the

                    // check mark from the associated menu item.

                    else

                    {

                        UnhookWindowsHookEx(myhookdata[index].hhook);

                        CheckMenuItem(hmenu, index,

                            MF_BYCOMMAND | MF_UNCHECKED);

                        afHooks[index] = FALSE;

                    }

                default:

                    return (DefWindowProc(hwndMain, uMsg, wParam,

                        lParam));

            }

            break;

            //

            // Process other messages.

            //

        default:

            return DefWindowProc(hwndMain, uMsg, wParam, lParam);

    }

    return NULL;

}

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szCWPBuf[256];

    CHAR szMsg[16];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process message

        return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,

                wParam, lParam);

    // Call an application-defined function that converts a message

    // constant to a string and copies it to a buffer.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);

    switch (nCode)

    {

        case HC_ACTION:

            cch = wsprintf(szCWPBuf,

               "CALLWNDPROC - tsk: %ld, msg: %s, %d times   ",

                wParam, szMsg, c++);

            TextOut(hdc, 2, 15, szCWPBuf, cch);

            break;

        default:

            break;

    }

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,

        wParam, lParam);

}

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szMSGBuf[256];

    CHAR szRem[16];

    CHAR szMsg[16];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0) // do not process message

        return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,

            wParam, lParam);

    switch (nCode)

    {

        case HC_ACTION:

            switch (wParam)

            {

                case PM_REMOVE:

                    lstrcpy(szRem, "PM_REMOVE");

                    break;

                case PM_NOREMOVE:

                    lstrcpy(szRem, "PM_NOREMOVE");

                    break;

                default:

                    lstrcpy(szRem, "Unknown");

                    break;

            }

            // Call an application-defined function that converts a

            // message constant to a string and copies it to a

            // buffer.

            LookUpTheMessage((PMSG) lParam, szMsg);

            hdc = GetDC(hwndMain);

            cch = wsprintf(szMSGBuf,

                "GETMESSAGE - wParam: %s, msg: %s, %d times   ",

                szRem, szMsg, c++);

            TextOut(hdc, 2, 35, szMSGBuf, cch);

            break;

        default:

            break;

    }

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,

        wParam, lParam);

}

LRESULT CALLBACK DebugProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szBuf[128];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process message

        return CallNextHookEx(myhookdata[DEBUG].hhook, nCode,

            wParam, lParam);

    hdc = GetDC(hwndMain);

    switch (nCode)

    {

        case HC_ACTION:

            cch = wsprintf(szBuf,

                "DEBUG - nCode: %d, tsk: %ld, %d times   ",

                nCode,wParam, c++);

            TextOut(hdc, 2, 55, szBuf, cch);

            break;

        default:

            break;

    }

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam,

        lParam);

}

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szBuf[128];

    CHAR szCode[128];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process message

        return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,

            lParam);

    hdc = GetDC(hwndMain);

    switch (nCode)

    {

        case HCBT_ACTIVATE:

            lstrcpy(szCode, "HCBT_ACTIVATE");

            break;

        case HCBT_CLICKSKIPPED:

            lstrcpy(szCode, "HCBT_CLICKSKIPPED");

            break;

        case HCBT_CREATEWND:

            lstrcpy(szCode, "HCBT_CREATEWND");

            break;

        case HCBT_DESTROYWND:

            lstrcpy(szCode, "HCBT_DESTROYWND");

            break;

        case HCBT_KEYSKIPPED:

            lstrcpy(szCode, "HCBT_KEYSKIPPED");

            break;

        case HCBT_MINMAX:

            lstrcpy(szCode, "HCBT_MINMAX");

            break;

        case HCBT_MOVESIZE:

            lstrcpy(szCode, "HCBT_MOVESIZE");

            break;

        case HCBT_QS:

            lstrcpy(szCode, "HCBT_QS");

            break;

        case HCBT_SETFOCUS:

            lstrcpy(szCode, "HCBT_SETFOCUS");

            break;

        case HCBT_SYSCOMMAND:

            lstrcpy(szCode, "HCBT_SYSCOMMAND");

            break;

        default:

            lstrcpy(szCode, "Unknown");

            break;

    }

    cch = wsprintf(szBuf, "CBT - nCode: %s, tsk: %ld, %d times   ",

        szCode, wParam, c++);

    TextOut(hdc, 2, 75, szBuf, cch);

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,

        lParam);

}

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szBuf[128];

    CHAR szMsg[16];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process the message

        return CallNextHookEx(myhookdata[MOUSE].hhook, nCode,

            wParam, lParam);

    // Call an application-defined function that converts a message

    // constant to a string and copies it to a buffer.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);

    cch = wsprintf(szBuf,

        "MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times   ",

        nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++);

    TextOut(hdc, 2, 95, szBuf, cch);

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam,

        lParam);

}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szBuf[128];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process message

        return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode,

            wParam, lParam);

    hdc = GetDC(hwndMain);

    cch = wsprintf(szBuf, "KEYBOARD - nCode: %d, vk: %d, %d times ",

        nCode, wParam, c++);

    TextOut(hdc, 2, 115, szBuf, cch);

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam,

        lParam);

}

LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam)

{

    CHAR szBuf[128];

    CHAR szMsg[16];

    CHAR szCode[32];

    HDC hdc;

    static int c = 0;

    int cch;

    if (nCode < 0)  // do not process message

        return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,

            wParam, lParam);

    switch (nCode)

    {

        case MSGF_DIALOGBOX:

            lstrcpy(szCode, "MSGF_DIALOGBOX");

            break;

        case MSGF_MENU:

            lstrcpy(szCode, "MSGF_MENU");

            break;

        case MSGF_SCROLLBAR:

            lstrcpy(szCode, "MSGF_SCROLLBAR");

            break;

        default:

            wsprintf(szCode, "Unknown: %d", nCode);

            break;

    }

    // Call an application-defined function that converts a message

    // constant to a string and copies it to a buffer.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);

    cch = wsprintf(szBuf,

        "MSGFILTER  nCode: %s, msg: %s, %d times    ",

        szCode, szMsg, c++);

    TextOut(hdc, 2, 135, szBuf, cch);

    ReleaseDC(hwndMain, hdc);

    return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,

        wParam, lParam);

}

未完待續。

繼續閱讀