首页
社区
课程
招聘
[原创]Windows消息钩取(简单DLL注入)
发表于: 2021-4-6 00:13 13409

[原创]Windows消息钩取(简单DLL注入)

2021-4-6 00:13
13409

简单的说,消息钩取就是半路截取信息。

1、发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue];


2、OS判断哪个应用程序中发生了事件,然后从[OS message queue]中取出消息,添加到相应应用程序的[application message queue]中;


3、应用程序监视自身的[application message queue],发现新添加的WM_KEYDOWN消息后,调用相应的事件处理程序处理。

OS消息队列和应用程序消息队列之间存在一条钩链(Hook Chain),设置好键盘消息钩子后,处于钩链中的键盘消息钩子会比应用程序先一步看到相应信息。在键盘消息钩子函数的内部,除了可以查看消息之外,还可以修改消息本身,而且还能对消息实施拦截,阻止消息传递。可以同时设置多个相同的键盘消息钩子,按照设置的顺序依次调用,从而组成的链条称为钩链。

image

在Windows编程中,使用SetWindowsHookEx() API可以简便地实现消息钩子,其用于将指定的钩子注册到钩链中,无论在DLL内部或外部都可调用。


函数原型:

HHOOK SetWindowsHookEx(

   int idHook,   //hook type

   HOOKPROC lpfn,  //hook procedure

   HINSTANCE hMod,  //hook procedure所属的DLL句柄

   DWORD dwThreadId //将要挂钩的目标线程ID

);

HHOOK:返回值,钩子句柄,需要保留,等不使用钩子时通过UnhookWindowsHookEx函数卸载钩子。

idHook:钩子的拦截消息类型,选择钩子程序的拦截范围,具体值参考文章结尾的消息类型。

Lpfn:消息的回调函数地址,一般是填函数名。

hMod:钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。在dll中可通过AfxInitExtensionModule(MousehookDLL, hInstance)获得DLL句柄。

dwThreadId:钩子所监视的线程的线程号,可通过GetCurrentThreadId()获得线程号。对于全局钩子,该参数为NULL(或0)


钩子过程(hook procedure)是由OS调用的回调函数。安装消息钩子时,钩子过程需要存在于某个DLL内部,并且该DLL的实例句柄即是hMod。

使用SetWindowsHookEx()设置好钩子后,在某个进程中生成指定消息时,OS会将相关的DLL文件强制注入相应的进程,然后调用注册的钩子过程。

回调函数:简言之,就是某个特定的事件发生时被指定调用的函数。窗口Windows过程(WndProc)就是一个典型的回调函数(键盘,鼠标等事件发生时OS会调用注册的窗口过程)

KeyHook.dll文件是含有钩子过程的DLL文件,HookMain.exe是最先加载KeyHook.dll并安装键盘钩子的程序。HookMain.exe加载KeyHook.dll文件后SetWindowsHookEx()安装键盘钩子。若其他进程中发生键盘输入事件,则OS会强制将KeyHook.dll加载到相应进程的内存,然后调用KeyboardProc()函数。注意的是,OS会将KeyHook.dll加载到发生键盘输入事件的所有进程,即消息钩取技术是一种DLL注入技术。

//HookMain.cpp

#include "stdio.h"

#include "conio.h"  //控制台输入输出头文件

#include "windows.h"


#define DEF_DLL_NAME "KeyHook.dll"    //define一个dll的名称

#define DEF_HOOKSTART "HookStart"

#define DEF_HOOKSTOP "HookStop"      //define两个GetProcAdress要查找的函数的名称


typedef void(*PFN_HOOKSTART)();      //typedef一个名叫PFN_HOOKSTART的函数指针类型

typedef void(*PFN_HOOKSTOP)();       //同上


void main()

{

    HMODULE hDll = NULL;      //声明并初始化HMODULE类型的变量hDll

    PFN_HOOKSTART HookStart = NULL;  //使用PFN_HOOKSTART定义一个名叫HookStart的函数指针

    PFN_HOOKSTOP HookStop = NULL;   //同上

    char ch = 0;


    hDll = LoadLibraryA(DEF_DLL_NAME);     //调用LoadLibraryA函数加载DLL并获取DLL的句柄,此时dll中的DLLMain就会自动被执行


    HookStart = (PFN_HOOKSTART)GetProcAddress(hDllDEF_HOOKSTART); //调用GetProcAddress函数获取Dll中HookStart函数的地址,并将其赋给函数指针HookStart

    HookStop = (PFN_HOOKSTOP)GetProcAddress(hDllDEF_HOOKSTOP);    //调用GetProcAddress函数获取Dll中HookStop函数的地址,并将其赋给函数指针HookStop


    HookStart();    //调用DLL中的HookStart函数


    printf("press 'q' to quit!\n");

    while(_getch() != 'q');   //带下划线_的函数一般是函数库内部的函数,而不带下划线的一般是提供给用户使用的函数。带下划线的目的是为了防止用户定义的函数和函数库的函数重名冲突,所以直接使用也是可以的。


    HookStop();    //调用DLL中的HookStop函数


    FreeLibrary(hDll);     //调用FreeLibrary来释放指定的动态链接库

}


#include "stdio.h"

#include "windows.h"


#define DEF_PROCESS_NAME "notepad.exe"     //定义目标的进程名为notepad.exe


HINSTANCE g_hInstance = NULL;    

HHOOK g_hHook = NULL;

HWND g_hWnd = NULL;



//DllMain()函数在DLL被加载到进程后会自动执行

/*

hinstDLL:指向自身的句柄

dwReason: 调用原因

lpvReserved:隐式加载和显示加载

*/

BOOL WINAPI DllMain(HINSTANCE hinstDLLDWORD dwReasonLPVOID lpvReserved)

{

    switchdwReason )

    {

        case DLL_PROCESS_ATTACH:

            g_hInstance = hinstDLL;

            break;

 

        case DLL_PROCESS_DETACH:

            break;

    }

    return TRUE;

}


/*

nCode: 根据这个数值决定怎样处理消息

wParam: 按键的虚拟键值消息,例如:VK_F1

lParam: 根据不同的位数具有多种不同的含义

LRESULT是一个数据类型,指的是从窗口程序或者回调函数返回的32位值。

CALLBACK是由用户设计却由windows系统呼叫的函数,统称为callback函数。某些API函数要求以callback作为你参数之一。

*/

LRESULT CALLBACK KeyboardProc(int nCodeWPARAM wParamLPARAM lParam)

{

    char szPath[MAX_PATH= {0,};        //MAX_PATH定义了编译器所支持的最长全路径名的长度

    char *p = NULL;


    ifnCode >= 0 )

    {

 

        //释放键盘按键时,bit 31 : 0 => press, 1 => release

        if!(lParam & 0x80000000) )   //10000000 00000000 00000000 00000000,即当第31位是0的时候表示press,第31位是1的时候表示release

        {

            GetModuleFileNameA(NULLszPathMAX_PATH);   //当前进程的全路径储存到szPath中

            p = strrchr(szPath'\\');     //p为字符'\\'之后到szPath字符串的结尾,说明取的是当前程序的进程名

 

            //比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序或下一个钩子函数

            //_stricmp()函数用于比较字符串,i表示不区分大小写,若两个值相等则返回0

            if!_stricmp(p + 1DEF_PROCESS_NAME) )

            {

                return 1;        //如果比较来两个值相等,则return 1

            }

        }

    }

 

    //比较当前进程名称,若非notepad.exe,则消息传递给应用程序或下一个钩子函数

    return CallNextHookEx(g_hHooknCodewParamlParam);

}


#ifdef __cplusplus    //如果这是一段cpp代码,那么就加入extern "C"{

extern "C"{

#endif


__declspec(dllexport) void HookStart() //__declspec(dllexport),显式的声明这是一个导出函数

    {

        g_hHook = SetWindowsHookEx(WH_KEYBOARDKeyboardProcg_hInstance0);

    } //当调用HookStart函数的时候,SetWindowsHookEx函数就将KeyboardProc添加到钩链


__declspec(dllexport) void HookStop()

{

    if(g_hHook)

    {

        UnhookWindowsHookEx(g_hHook);

        g_hHook = NULL;

    }

}

#ifdef __cplusplus  //如果这是一段cpp代码,那么就加入}

}

#endif

函数原型:

BOOL WINAPI DllMain(


[注意]APP应用上架合规检测服务,协助应用顺利上架!

收藏
免费 9
支持
分享
最新回复 (3)
雪    币: 434
活跃值: (72)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
这样科普类的作品太好了,谢谢楼主!
2021-7-5 21:36
1
雪    币: 225
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
2023-4-7 11:24
0
游客
登录 | 注册 方可回帖
返回
// // 统计代码