zoukankan      html  css  js  c++  java
  • 消息HOOK实现记录用户敲击键盘demo

    1.  hook的分类

         可以分为两块,第一块是在 Ring3 层的 Hook,俗称应用层 Hook 技术,另外一块自然是在 Ring0 层得 Hook,俗称为内核层 Hook 技术

    (图片来自网络)

            消息 Hook 是通过 SetWindowsHookEx 可以实现将自己的钩子插入到钩子链的最前端,而对于发送给被 Hook 的窗口(也有可能是所有的窗口,即全局 Hook)的消息都会被我们的钩子处理函数所捕获到,

    也就是我们可以优先于窗体先捕获到这些消息,Windows 消息 Hook 可以实现为进程内消息 Hook 和全局消息 Hook,对于进程内消息 Hook,则可以简单的将 Hook 处理函数直接写在这个进程内,即是自己 Hook 自己。

    windows的消息流如下:

    • 发生键盘输入事件时,WM_KEYDOWN消息被添加到[系统消息队列]。
    • OS判断哪个应用程序中发生了事件,然后从[系统消息队列]取出消息,添加到相应应用程序的[application message queue]中。
    • 应用程序监视自身的[应用消息队列],发现新添加的WM_KEYDOWN消息后,调用相应的事件处理程序处理。

    所以本次测试是在窗口回调的时候设置hook回调函数,实现记录用户键盘信息

    2. 相关函数

     首先是设置hook函数:SetWindowsHookExW

    函数原型和相关参数简介: 参考https://blog.csdn.net/mmllkkjj/article/details/6627188

    WINUSERAPI HHOOK WINAPI SetWindowsHookExW(    _In_ int idHook,    _In_ HOOKPROC lpfn,    _In_opt_ HINSTANCE hmod,    _In_ DWORD dwThreadId);

    参数介绍:
    _In_ int idHook: 钩子类型,可取以下值:
    WH_MSGFILTER    = -1; 线程级; 截获用户与控件交互的消息
    WH_JOURNALRECORD  = 0; 系统级; 记录所有消息队列从消息队列送出的输入消息, 在消息从队列中清除时发生; 可用于宏记录
    WH_JOURNALPLAYBACK = 1; 系统级; 回放由 WH_JOURNALRECORD 记录的消息, 也就是将这些消息重新送入消息队列
    WH_KEYBOARD    = 2; 系统级或线程级; 截获键盘消息
    WH_GETMESSAGE   = 3; 系统级或线程级; 截获从消息队列送出的消息
    WH_CALLWNDPROC   = 4; 系统级或线程级; 截获发送到目标窗口的消息, 在 SendMessage 调用时发生

    WH_CBT       = 5; 系统级或线程级; 截获系统基本消息, 譬如: 窗口的创建、激活、关闭、最大最小化、移动等等有用的窗体控件消息
    WH_SYSMSGFILTER  = 6; 系统级; 截获系统范围内用户与控件交互的消息
    WH_MOUSE      = 7; 系统级或线程级; 截获鼠标消息
    WH_HARDWARE    = 8; 系统级或线程级; 截获非标准硬件(非鼠标、键盘)的消息
    WH_DEBUG      = 9; 系统级或线程级; 在其他钩子调用前调用, 用于调试钩子
    WH_SHELL      = 10; 系统级或线程级; 截获发向外壳应用程序的消息
    WH_FOREGROUNDIDLE = 11; 系统级或线程级; 在程序前台线程空闲时调用
    WH_CALLWNDPROCRET = 12; 系统级或线程级; 截获目标窗口处理完毕的消息, 在 SendMessage 调用后发生

    __in HOOKPROC lpfn:回调函数地址
    其回调函数原型为:
    LRESULT CALLBACK name(int nCode, WPARAM wParam, LPARAM lParam)
    name可以随便起,这里来解释一下回调函数的参数:
    int nCode:消息代码
    WPARAM wParam:附加参数一
    LPARAM lParam:附加参数二
    返回值:LRESULT类型(便于传递hook,后面详细介绍)
    __in HINSTANCE hMod:DLL实列句柄
    __in DWORD dwThreadId:要挂钩的线程ID

    释放钩子函数:UnhookWindowsHookEx

    BOOL WINAPI UnhookWindowsHookEx( __in HHOOK hhk);
    __in HHOOK hhk:要删除的hook句柄

    传递钩子函数CallNextHookEx  (我没用上,只是单一hook,看不出效果。)

    LRESULT
    WINAPI
    CallNextHookEx(
        _In_opt_ HHOOK hhk,
        _In_ int nCode,
        _In_ WPARAM wParam,
        _In_ LPARAM lParam);
    参数:
     _In_opt_ HHOOK hhk:下一个钩子子程的句柄
    _In_ int nCode: 钩子消息代码,此消息代码会被传递给下一个钩子处理
     _In_ WPARAM wParam:消息附加参数一
     _In_ LPARAM lParam:消息附加参数二

    3. 创建windows桌面程序  空项目不成功(mfc的简易框架,从网上开源博客挖来) 

    完整代码如下:(创建窗口句柄,绘制窗口和空消息队列)

    #include <windows.h>
    #include<fstream>
    #include<iomanip>
    #include<iostream>
    //消息函数
    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        //判断消息ID
        switch (uMsg) {
        case WM_DESTROY:    // 窗口销毁消息
            PostQuitMessage(0);   //  发送退出消息
            return 0;
        }
        // 其他的消息调用缺省的消息处理程序
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    // 3、注册窗口类型
    BOOL RegisterWindow(LPCTSTR lpcWndName, HINSTANCE hInstance)
    {
        ATOM nAtom = 0;
        // 构造创建窗口参数
        WNDCLASS wndClass = { 0 };
        wndClass.style = CS_HREDRAW | CS_VREDRAW;
        wndClass.lpfnWndProc = WindowProc;      // 指向窗口过程函数
        wndClass.cbClsExtra = 0;
        wndClass.cbWndExtra = 0;
        wndClass.hInstance = hInstance;
        wndClass.hIcon = NULL;
        wndClass.hCursor = NULL;
        wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wndClass.lpszMenuName = NULL;
        wndClass.lpszClassName = lpcWndName;    // 注册的窗口名称,并非标题,以后创建窗口根据此注册的名称创建
        nAtom = RegisterClass(&wndClass);
        return TRUE;
    }
    //创建窗口(lpClassName 一定是已经注册过的窗口类型)
    HWND CreateMyWindow(LPCTSTR lpClassName, HINSTANCE hInstance)
    {
        HWND hWnd = NULL;
        // 创建窗口
        hWnd = CreateWindow(lpClassName, L"HOOKdemo1", WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME, 0, 0, 1000, 800, NULL, NULL, hInstance, NULL);
        return hWnd;
    }
    //显示窗口
    void showWindow(HWND hWnd)
    {
        //获得屏幕尺寸
        int scrWidth = GetSystemMetrics(SM_CXSCREEN);
        int scrHeight = GetSystemMetrics(SM_CYSCREEN);
        RECT rect;
        GetWindowRect(hWnd, &rect);
        ShowWindow(hWnd, SW_SHOW);
        //重新设置rect里的值
        rect.left = (scrWidth - rect.right) / 2;
        rect.top = (scrHeight - rect.bottom) / 5;
        //移动窗口到指定的位置
        SetWindowPos(hWnd, HWND_TOP, rect.left, rect.top, rect.right, rect.bottom, SWP_SHOWWINDOW);
        UpdateWindow(hWnd);
    }
    
    void waitForMessage()        // 消息循环处理函数
    {
        MSG msg = { 0 };
        // 获取消息
        while (GetMessage(&msg, NULL, 0, 0)) // 当接收到WM_QIUT消息时,GetMessage函数返回0,结束循环
        {
            DispatchMessage(&msg); // 派发消息,到WindowPro函数处理
        }
    }
    //hook
    HHOOK hook;
    //hook回调函数
    LRESULT CALLBACK callbackForhook(int nCode, WPARAM wParam, LPARAM lParam)
    {
        std::ofstream ofile;               //定义输出文件
        ofile.open("d:\num.txt",std::ios::app);
        ofile << std::endl<< wParam<<std::endl;
        ofile.close();
        if (wParam)
        {
            MessageBox(NULL, L"按什么按!", L"键盘消息被触发了", 0);
        }
        return CallNextHookEx(hook, nCode, wParam, lParam);
    }
    // 入口函数
    HWND hWnd;    //progman
    int WINAPI WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nShowCmd)
    {
        HWND hWnd = NULL;
        LPCTSTR lpClassName = L"MyWnd";  // 注册窗口的名称
        RegisterWindow(lpClassName, hInstance);
        hWnd = CreateMyWindow(lpClassName, hInstance);
        //注册hook
        hook = SetWindowsHookEx(WH_KEYBOARD, callbackForhook, NULL, GetCurrentThreadId());
        showWindow(hWnd);
        waitForMessage();
        return 0;
    }

    hook回调函数:

    LRESULT CALLBACK callbackForhook(int nCode, WPARAM wParam, LPARAM lParam)
    {
        std::ofstream ofile;               //定义输出文件
        ofile.open("d:\num.txt",std::ios::app);
        ofile << std::endl<< wParam<<std::endl;
        ofile.close();
        if (wParam)
        {
            MessageBox(NULL, L"按什么按!", L"键盘消息被触发了", 0);
        }
        return CallNextHookEx(hook, nCode, wParam, lParam);
    }

    用的是c++ ios::app对文件不覆盖写入,wParam是按键信息,对应表格可参考vk键值表格  : https://wenku.baidu.com/view/58535d54a98271fe910ef993.html

    4.程序运行效果:

    运行之后,点击任意键盘,都可弹出hook提示窗口,因为没过滤,然后D盘下num.txt记录按键十进制信息:

     

  • 相关阅读:
    2017-2018-1 20155326 《信息安全系统设计基础》第六周课上作业
    20155326 2017-2018-1 《信息安全系统设计基础》缓冲区溢出漏洞实验
    2017-2018-1 201552326《信息安全技术》实验二——Windows口令破解
    《科技之巅2》序——机器智能数据智能:工具之王
    云大使成长精华指引(全)
    程序员职业规划课:如何开启"第二春"?
    明明可以靠脸吃饭偏要靠才华_你身边有女神程序员吗?
    6月19日云栖精选夜读:血泪总结!创业公司CTO要避免哪些坑?
    玩过这些经典单机游戏_就说明你已经老了
    帮程序员减压放松的10个良心网站
  • 原文地址:https://www.cnblogs.com/jentleTao/p/12654278.html
Copyright © 2011-2022 走看看