Windows消息钩取

  1. 为了偷看或截取来往信息而在中间设置岗哨的行为称为挂钩,实际上,偷看或操作信息的行为就是人们常说的钩取。

消息钩子

  1. Windows操作用户向用户提供GUI,它以事件驱动方式工作,在操作系统中借助键盘、鼠标、选择菜单、按钮以及鼠标移动、改变窗口大小与位置等都是事件,发生这样的事件时,OS会把是西安定义好的消息发送给相应的应用程序,应用程序分析收到的信息后执行相应的动作。
  2. 常规Windows消息流:
  • 发生键盘输入事件时,WM_KEYDOWN消息被添加到[OS message queue].
  • OS判断时哪个应用程序中发生了事件,然后从[OS message queue]取出消息,添加到相应应用程序的[application message queue]中。
  • 应用程序监视自身的[application message queue]消息,发现新添加的WM_KEYDOWN消息后,调用相应的事件处理程序处理。
  1. OS消息队列与应用程序消息队列之间存在一条钩链,设置好键盘消息钩子之后,处于钩链中的键盘消息钩子会比应用程序先看到相应信息,在键盘消息钩子函数内部,除了可以查看消息之外,还可以修改消息本身,而且还能对消息实施拦截,阻止消息传递。
    在这里插入图片描述
  2. SetWindowsHookEx()
  • SetWindowsHookEx() API的定义如下:
HHOOK SetWindowsHookEx(
	int idHook,  //hook type
	HOOKPROC lpfn, //hook procedure
	HINSTANCE hMod, //hook procedure所属的DLL句柄
	DWORD dwThread //想要挂钩的线程ID
);
  • 钩子过程是由操作系统调用的回调函数,安装消息钩子时,钩子过程需要存在于某个DLL内部,该DLL的句柄时hMod。

键盘消息钩取练习

在这里插入图片描述

  1. KeyHook.dll文件是一个含有钩子过程的DLL文件,HookMain.exe是最先加载KeyHook.dll并安装键盘钩子的程序,HookMain.exe加载KeyHook.dll文件后使用SetWindowsHookEx()安装键盘钩子,若其他进程中发生键盘输入事件,OS就会强制将KeyHook.dll加载到相应进程的内存,然后调用KeyboardProc()函数。
  2. 拦截notepad.exe进程的键盘消息,使之无法显示在记事本中。
  • 运行Hook Main.exe程序
    在这里插入图片描述
  • 运行notepad.exe程序,用键盘输入,Notepad.exe进程忽视用户的键盘输入,使用Process Explorer查看notepad.exe进程,可以看到KeyHook.dll已经加载其中。
    在这里插入图片描述
  • 在ProcessExplorer中检索注入KeyHook.dll的所有进程,一个进程开始运行并发生键盘事件时,KeyHook.dll就会注入到其中:
    在这里插入图片描述
  • HookMain.exe程序中输入q命令,HookMain.exe将拆除键盘钩子,拆除键盘钩子后,相关进程就会将KeyHook.dll文件全部卸载。
  1. 分析源代码
  • 查看一下HookMain.exe文件的源代码:
#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;

    // 加载KeyHook.dll
	hDll = LoadLibraryA(DEF_DLL_NAME);
    if( hDll == NULL )
    {
        printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
        return;
    }

    //获取到处函数地址
	HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
	HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

    // 开始钩取
	HookStart();

    //等待用户输入q
	printf("press 'q' to quit!\n");
	while( _getch() != 'q' )	;

    // 终止钩取
	HookStop();
	
    // 卸载KeyHook.dll
	FreeLibrary(hDll);
}
  • 先加载KeyHook.dll文件,然后调用HookStart()函数开始钩取,用户输入q时,调用HookStop()函数终止钩取。
  • 查看KeyHook.dll文件的源代码:
#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 => press, 1 => 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
  • DLL代码也非常简单,调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()田间到键盘钩链。
  • 安装了键盘钩子后,无论哪个进程,只要发生键盘输入事件,OS就会强制将KeyHook.dll注入相应进程,加载了KeyHook.dll的进程,发生键盘事件时首先调用执行KeyHook.KeyboardProc().
  • KeyboardProc()函数中发生键盘输入事件时,就会比较当前进程的名称与“notepad.exe”字符串,若相同,则返回1,终止KeyboardProc()函数,这意味着截获且删除消息,这样,键盘消息就不会传递到notepad.exe程序的消息队列。
  • 即当前进程名称非“notepad.exe"时,执行return CallNextHookEx(g_hHook, nCode, wParam, lParam);语句,消息会被传递到另一个应用程序或钩链中的另一个钩子函数。
  1. 调试HookMain.exe
    在这里插入图片描述
  • 上图显示的是HookMain.exe的EP代码,可以使用以下几种方法查找核心代码:
    1. 逐行跟踪。
    2. 检索相关API。
    3. 检索相关字符串。
  • 点击右键->Search for - All referenced text strings项:
    在这里插入图片描述
  • 40104D地址处的指令引用了要查找的字符串,双击字符串,转到相应地址处:
    在这里插入图片描述
  • 上图显示的代码就是HookMain.exe程序的main()函数。
  • 在401000地址处设置断点,然后运行程序,到断点处停下来,开始调试,从断点开始依次跟踪调试代码,现在401006地址处调用LoadLibrary(KeyHook.dll),然后由40104B地址处的CALL EBX命令调用KeyHook.HookStart()函数,跟踪40104B地址处的CALL EBX命令:
    在这里插入图片描述
  • 上图中的代码是被加载到HookMain.exe进程中的KeyHook.dll的HookStart()函数,在地址100010EF地址可以看到CALL SetWindowsHookExW()指令,其上方10010E8与100010ED地址的2条PUSH指令用于把SetWindowsHookExW()API的第1、2两个参数压入栈。
  • SetWindowsHookExW()API的第一个参数(idHook)值为WH_KEYBOARD(2),第二个参数值为10001020,该值即使钩子过程的地址,后面调试KeyHook.dll时再仔细看该地址,HookMain.exe的main()函数的其余代码接收用户输入的q命令后终止钩取。
  1. 调试Notepad.exe进程内的KeyHook.dll
  • 首先使用Ollydbg打开Notepad.exe程序,执行F9将程序运行起来。
  • 在OllyDbg的Debugging options,点选Break on new module(DLL)复选框,开启该选项后,每当新的DLL装入被调试的进程时就会自动暂停调试。
    在这里插入图片描述
  • 此时运行HookMain.exe,然后在notepad.exe中使用键盘输入,此时OllyDbg暂停.。
  • KeyHook.dll被加载到10000000地址处。
    在这里插入图片描述
  • 双击KeyHook.dll转到KeyHook.dll的EP地址处,我们已经知道了钩子过程地址为10001020,下面直接转到该地址。
    在这里插入图片描述
  • 像钩子过程10001020设置断点,每当notepad.exe发生键盘输入事件,调试就停在该处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值