windows 消息钩取
当键盘发生输入事件时,键盘消息钩子就会提前获得消息的内容、类型
并且可以对其做出修改
实现消息钩子的api:SetWindowsHookEx
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook, 设置钩子的类型.意思就是我要设置的钩子是什么钩子. 可以是监视窗口过程.可以是监视消息队列.
_In_ HOOKPROC lpfn, 根据钩子类型.设置不同的回调函数.
_In_ HINSTANCE hMod, 钩子设置的Dll实例句柄,就是DLL的句柄
_In_ DWORD dwThreadId 设置钩子的线程ID. 如果为0 则设置为全局钩子.
);
KeyBoardProc(钩子过程函数)
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
参数说明
code [in]
类型: int
挂钩过程用来确定如何处理消息的代码。 如果 代码 小于零,则挂钩过程必须将消息传递给 CallNextHookEx 函数而不进行进一步处理,并应返回 CallNextHookEx 返回的值。 此参数的取值可为下列值之一:
值 | 含义 |
---|---|
HC_ACTION 0 | WParam 和 lParam 参数包含有关击键消息的信息。 |
HC_NOREMOVE 3 | WParam 和 lParam 参数包含有关击键消息的信息,而击键消息尚未从消息队列中删除。 (名为 PeekMessage 函数的应用程序,指定 PM_NOREMOVE 标志。 ) |
wParam [in]
类型: WPARAM
生成击键消息的密钥的 虚拟键代码 。
lParam [in]
类型: LPARAM
重复次数、扫描代码、扩展键标志、上下文代码、上一个键状态标志和转换状态标志。 有关 lParam 参数的详细信息,请参阅 击键消息标志。 下表描述了此值的位数。
Bits | 说明 |
---|---|
0-15 | 重复计数。 值是用户按住该键时,击键重复的次数,即。 |
16-23 | 扫描代码。 该值取决于 OEM。 |
24 | 指示密钥是否为扩展键,如数字键盘上的函数键或键。 如果键是扩展键,则值为 1; 否则为。否则,为0。 |
25-28 | 保留。 |
29 | 上下文代码。 如果 ALT 键为关闭状态,则值为 1; 否则为。否则,为0。 |
30 | 之前的键状态。 如果在发送消息之前键关闭,则值为 1; 否则为。如果键已启动,则为0。 |
31 | 转换状态。 如果正在按下键,则该值为 0; 如果正在释放,则该值为1。 |
分析源代码
hookmain.exe
先加载keyhook.dll文件
然后调用hoolstart函数开始钩取
输入q终止
#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)();
void main()
{
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;
hDll = LoadLibraryA(DEF_DLL_NAME);//加载KeyHook.dll,获得dll的句柄
if (hDll == NULL)
{
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
}
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);//利用GetProcAddress()获得HookStart的函数地址
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);//利用GetProcAddress()获得HookStop的函数地址
HookStart();//开始钩取函数
printf("press 'q' to quit!\n");
while (_getch() != 'q');//遇到键盘输入q就退出
HookStop();//退出钩取函数
FreeLibrary(hDll);
}
KeyHook
调用导出函数HookStart时,SetWindowsHookEX函数就会将KeyBoardProc函数添加到键盘钩链
#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)
{
char szPath[MAX_PATH] = {0,};
char *p = NULL;
if( nCode >= 0 )
{
// bit 31 : 0 => key press, 1 => key release
if( !(lParam & 0x80000000) ) //释放键盘按键时
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
// 比较当前进程名称,若为notepad.exe ,则消息不会传递给应用程序(或下一个钩子)
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}
// 若非notepad.exe 则调用 CallNextHookEx() 函数,将消息传递给应用程序(或下一个钩子)
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
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
调试分析
具体看书,这里列出主要内容
先调试HookMain.exe
通过od搜索字符串找到核心的代码部分并设置断点
继续调试,先经过401006处调用函数进入KeyHook函数EP
之后在40104B处调用ebx处函数:KeyHook中的HookStart函数,进入看看
接下来的四行push指令传入SetWindowsHookEXW的第4,3,2,1个参数,根据本文上面的参数说明,第二个参数:回调函数的地址
所以回调函数地址是10001020(钩子过程的地址)
再调试notepad.exe进程中的KeyHook.dll
拖入od f9 运行
然后在选项中设置:
这样当程序有dll载入时会自动暂停调试到dll的入口
接着运行HookMain.exe
在notepad中输入,没有显示,但od自动停在了dll 的 ep
接着找到刚才获得的HookStart函数地址10001020(KeyboardProc):
堆栈中还能看到KeyboardProc参数;