第21章:Windows消息钩取

Windows向用户提供GUI,以事件驱动的方式工作.

 

常规的Windows消息流:

发生键盘输入事件时,WM_KeyDown消息被添加到OS消息队列.

OS判断哪个应用程序发生了事件,然后从OS消息队列重取出消息,添加到应用程序的消息队列中.

应用程序监视自身的消息队列,发现新添加的消息后,调用相应的事件处理程序处理.

第21章:Windows消息钩取

SetWindowsHookEx()可以实现消息钩取.

第21章:Windows消息钩取

第一个参数是钩子类型,例如可以为 WH_KeyBoard ,键盘钩子.

第二个参数是钩子过程,是由操作系统调用的回调函数,说白了就是处理程序.

第三个参数是钩子过程所属的DLL句柄,因为钩子过程需要存在于某个DLL内部.

第四个参数代表想要影响的进程,若为0则影响所有进程.

 

可执行程序加载并安装钩子 -> 其它进程发生硬盘输入事件 -> OS将Dll文件加载到相应得进程内存 -> 调用回调函数

重点看一下HookMain.exe文件的源代码.

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#define    DEF_DLL_NAME        "KeyHook.dll"
#define    DEF_HOOKSTART        "HookStart"
#define    DEF_HOOKSTOP        "HookStop"

typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();   //声明一个返回值为NULL的函数指针

void main()
{
    HMODULE            hDll = NULL;
    PFN_HOOKSTART    HookStart = NULL;    //定义函数指针
    PFN_HOOKSTOP    HookStop = NULL;    //函数指针赋值为NULL,避免成为野指针
    char            ch = 0;

    // 加载 KeyHook.dll
    hDll = LoadLibraryA(DEF_DLL_NAME);
    if( hDll == NULL )
    {
        printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
        return;
    }

    // 获取导出函数地址
    HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);   // 将函数返回值转换为自定义的类型
    HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

    // 开始钩取
    HookStart();
printf("press ‘q‘ to quit!\n"); while( _getch() != q ) ; // 终止钩取 HookStop(); // 卸载KeyHook.dll FreeLibrary(hDll); }
#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME   "notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
    switch( dwReason )
    {
        case DLL_PROCESS_ATTACH:
            g_hInstance = hinstDLL;
            break;

        case DLL_PROCESS_DETACH:
            break;    
    }

    return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)    //此处猜测是覆写了KeyBoardProc()函数
{
    char szPath[MAX_PATH] = {0,};
    char *p = NULL;

    if( nCode >= 0 )
    {
        // bit 31 : 0 => press, 1 => release
        if( !(lParam & 0x80000000) )
        {
            GetModuleFileNameA(NULL, szPath, MAX_PATH);
            p = strrchr(szPath, \\);     //返回字符串中从右侧搜索首次出现字符 \\ 的地址。

           
            if( !_stricmp(p + 1, DEF_PROCESS_NAME) )    //字符串相等时,返回值为0
                return 1;                               //程序为notepad.exe时,直接返回1即不将键盘按键数据传到应用程序
        }
    }

    // 若当前进程名称不是notepad.exe则调用 CallNextHookEx() 函数,将消息传递给应用程序或下一个钩子
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
extern "C" {
#endif
    __declspec(dllexport) void HookStart()             //declspec是针对编译器的关键字,指出相应函数为导出函数
    {
        g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
    }

    __declspec(dllexport) void HookStop()
    {
        if( g_hHook )
        {
            UnhookWindowsHookEx(g_hHook);
            g_hHook = NULL;
        }
    }
#ifdef __cplusplus
}
#endif

第21章:Windows消息钩取

第21章:Windows消息钩取

 

安装好键盘钩子之后,无论哪个进程发生键盘输入事件,OS会将KeyHook.dll注入到相应进程,而此进程发生键盘事件时也会首先调用执行 KeyHook.KeyboardProc()

Printf()函数看着简单,其实挺复杂的.

 

第21章:Windows消息钩取

上一篇:用WinRAR压缩软件和计划任务备份重要文件


下一篇:Atcoder Grand Contest 024 E - Sequence Growing Hard(dp+思维)