初步理解全局钩子注入
初步理解全局钩子注入
0x00 前言
这篇估计暂时没有demo代码,因为还没理解透。先从钩子开始回顾。
0x01 介绍
1.hook解释
百度百科解释如下:
https://baike.baidu.com/item/钩子程序
钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程序以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理Windows消息或特定事件。
个人理解是,Windows平台的一种机制,我们可以通过挂钩的方式,捕获或者传递(控制修改)消息。
假设一种场景,如果挂钩了某个程序运行的api,当api被调用的同时,并执行了我们的自写函数,也就是hookapi,应该能完成挺多事情的(待学习)。
2.全局钩子解释
理解这个全局钩子,首先想尝试下全局钩子具体怎么设置的,所以看了下他的具体代码实现步骤。
某大佬记:
1.调用SetWindowsHookEx设置钩子.
2.在设置过程中.需要一个回调.所以我们填入一个回调.(个人tips:这个地方可以设置自写函数,自写功能)
3.回调函数中调用CallNextHookEx函数. 如果不调用.那么相当于我们设置了不反悔.程序可能出现问题.当然是按需返回.(个人tips:如果不调用,这里可能就打乱程序原有的后续代码逻辑)
4.取消HOOK设置.(个人tips:降低性能,所以需要取消)
3.全局钩子部分需要的api和自写函数
- 设置钩子API
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook, //设置钩子的类型.意思就是我要设置的钩子是什么钩子. 可以是监视窗口过程.可以是监视消息队列.
_In_ HOOKPROC lpfn, //根据钩子类型.设置不同的回调函数.
_In_ HINSTANCE hMod, //钩子设置的Dll实例句柄,就是DLL的句柄
_In_ DWORD dwThreadId //设置钩子的线程ID. 如果为0 则设置为全局钩子.
);
//HHOOK 返回值. 是一个钩子过程句柄.
- 获取模块句柄API
HMODULE WINAPI GetModuleHandle(
_In_opt_ LPCTSTR lpModuleName //获取的实例句柄的文件名.可以是Dll可以使exe 如果为NULL 这是当前dll/exe的实例句柄
); //返回值 返回实例句柄.
- 取消设置钩子API
BOOL WINAPI UnhookWindowsHookEx(
_In_ HHOOK hhk //参数一是 SetWindowHookEx的返回值.也就是钩子过程句柄.
); //返回值: 返回值是BOOL类型.表示设置是否成功或者失败.
- 继续调用钩子链中的钩子过程.
LRESULT WINAPI CallNextHookEx(
_In_opt_ HHOOK hhk, //保存的钩子过程,也就是SetWindowsHookEx返回值.
_In_ int nCode, //根据SetWindowsHookEx设置的钩子回调而产生的不同的nCode代码. 什么意思? 意思就是如果设置的钩子类型是鼠标消息.那么那个nCode就是鼠标消息.如果是键盘这是键盘
_In_ WPARAM wParam, //同2参数一样.附加参数. 根据钩子回调类型.附加参数有不同的意义.比如如果是鼠标.那么这个有可能代表的就是鼠标的x位置.键盘就可能是键代码
_In_ LPARAM lParam //同3参数一样.附加参数.
);
- 钩子回调
钩子回调根据SetWindowsHookEx参数1来设定的。
idHook 要安装的钩子(HOOK)的类型,它决定了 HOOKPROC 被调用的时机,可选参数如下。
值 | 含义 |
---|---|
WH_MSGFILTER = -1 | 线程级,截获用户与控件交互的消息 |
WH_JOURNALRECORD = 0 | 系统级,记录所有消息队列送出的输入消息 |
WH_JOURNALPLAYBACK = 1 | 系统级,回放由WH_JOURNALRECORD记录的消息 |
WH_KEYBOARD = 2 | 系统级或线程级,截获键盘消息 |
WH_GETMESSAGE = 3 | 系统级或线程级,截获从消息队列送出的消息 |
WH_CALLWNDPROC = 4 | 系统级或线程级,截获发送到目标窗口的消息 |
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被调用后发生 |
WH_KEYBOARD_LL = 13 | 系统级,截获全局键盘消息 |
WH_MOUSE_LL = 14 | 系统级,截获全局鼠标消息 |
LRESULT CALLBACK MyProc( //这个回调函数里面写我们的代码.
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
例:
自编写的dll中,主要是用类似以下代码,自写功能逻辑在回调MyProc中去完成:
hook.h
#pragma once
//共享内存
#pragma data_seg("mydata")
#pragma comment(linker,"/SECTION:mydata,RWS")
#define MYWINDAPIEXPORT __declspec(dllexport)
HMODULE g_hModule; //钩子句柄
HHOOK g_HookProc = NULL;; //定义为全局
//必须赋初值,否则微软编译器会把没有初始化的数据放到普通的未初始化数据段中
//而不是放在shared中,从而导致多个进程之间的共享行为失败 返回的钩子过程
BOOL MYWINDAPIEXPORT SetHook();//设置HOOK钩子. //我们的启动函数.导出函数.外部设置HOOK
BOOL MYWINDAPIEXPORT UnHook(); //取消设置HOOK
LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam); //设置HOOK过程中需要的回调函数
Hook.cpp
#include <Windows.h>
#include "HOOK.H"
BOOL MYWINDAPIEXPORT SetHook()
{
g_HookProc = ::SetWindowsHookEx(WH_CBT, MyProc,g_hModule,0); //参数1.HOOK的类型. Hook的回调地址 模块句柄. 线程ID,为0代表是全局钩子
if (g_HookProc == NULL)
{
return FALSE;
}
return TRUE;
}
BOOL MYWINDAPIEXPORT UnHook() //取消设置HOOK
{
if (NULL != g_HookProc)
{
::UnhookWindowsHookEx(g_HookProc);
}
return TRUE;
}
LRESULT CALLBACK MyProc(int nCode, WPARAM wParam, LPARAM lParam) //我们自己的程序处理
{
/*
执行我们的程序
*/
::MessageBox(NULL, NULL, NULL, NULL);
return CallNextHookEx(g_HookProc, nCode, wParam, lParam); //继续调用钩子过程
}
而主程序main.cpp
//定义函数指针和函数指针变量
typedef BOOL(*ty_SetGlobalHook)();
ty_SetGlobalHook fpSetGlobalHook = NULL;
//获取要注入的DLL的加载基址
m_hDll= LoadLibrary("xxx.dll");
if (!m_hDll)
{
MessageBox(L"Dll加载失败");
}
//获取设置全局钩子函数的函数地址。赋值给函数指针变量
fpSetGlobalHook = (ty_SetGlobalHook)GetProcAddress(m_hDll, "SetHook");
if (!fpSetGlobalHook)
{
MessageBox(L"加载设置全局钩子函数地址失败");
}
//设置全局钩子
BOOL bRet = fpSetGlobalHook();
if (bRet)
{
MessageBox(L"全局钩子设置成功");
}
else
{
MessageBox(L"全局钩子设置失败");
}
0x02 全局钩子原理
仔细看了下书跟大佬的文章,还是粗略的理解了下,SetWindowsHookEx就是在程序执行过程中.替我们在加了一层逻辑. 去通知我们的回
调,然后我们的回调利用CallNextHookEx再去执行原有的逻辑。
大佬这里举了个例子。
比如:
A函数 -> B函数 -> C函数. 正常执行流程是 A函数调用B B调用C.
而我们添加了一层.
A - 我们 (利用CallNextHookEx决定是否调用下一个过程) 是 - >B ->C
A ->我们(不是的情况下) 不调用B.
原理就是我们的窗口程序. 直接使用DLL里面的SetWindowsHookEx设置了一个回调. 而这个API是给全局所有应用程序都设置了这个回调
(自写的函数)如果有应用程序触发到了这个步骤,那么我们的dll就会注到当前应用程序中。
感觉还是理解的很草率,这篇水文部分加入了自己的理解,利用方面的思路部分感觉搞的差不多清楚了,就是在回调的地方,加入自己的程序逻辑,实现的效果就可以自定义,而全局钩子注入dll只是一种手法,或者技术,这次的demo代码都是扣的大佬们的,先继续学了。