Windows消息钩取

@author: dlive

@date: 2016/12/19

0x01 SetWindowsHookEx()

HHOOK SetWindowsHookEx(
	int idHook,   //hook type
	HOOKPROC lpfn, //hook procedure(回调函数)
	HINSTANCE hMod, //hook procedure所属的DLL句柄
	DWORD dwThreadId //想要挂钩的线程ID
);

使用SetWindowsHookEx() API可以轻松实现消息钩子。用于将制定的“钩子过程”注册到钩链中。无论在DLL内部还是外部都可以调用。(下面的例子是在DLL内部调用的)

hook procedure(钩子过程) 是由操作系统调用的回调函数,安装消息钩子时,钩子过程需要存在于某个DLL内部,且该DLL的instance handle(示例句柄)即是hMod

若dwThreadID参数被设置为0,则安装的钩子为全局钩子,它会影响到运行中的所有进程

0x02 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;

	//加载DLL
	hDll = LoadLibraryA(DEF_DLL_NAME);
    if( hDll == NULL )
    {
        printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
        return;
    }
	
  	//从DLL中获取函数地址
	HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
	HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
	
  	//执行HookStart函数
	HookStart();

	printf("press 'q' to quit!
");
	while( _getch() != 'q' )	;

	HookStop();
	
  	//卸载DLL
	FreeLibrary(hDll);
}

0x03 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;

//DLL中的Main函数
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
	switch( dwReason )
	{
        //DLL加载的时候
        case DLL_PROCESS_ATTACH:
        	//钩子过程(KeyboardProc)所属DLL句柄,即本DLL
			g_hInstance = hinstDLL;
			break;
		//DLL卸载的时候
        case DLL_PROCESS_DETACH:
			break;	
	}

	return TRUE;
}

//钩子过程
//MSDN对KeyboardProc的定义:https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx
//nCode :HC_ACTION(0), HC_NOREMOVE(3)
//wParam : 虚拟键值(virtual key code),对于键盘而言a和A具有相同的虚拟键值
//lParam 额外信息
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) )
              	//终止KeyboardProc函数,意味着截获并删除信息
				return 1;
		}
	}
	//Passes the hook information to the next hook procedure in the current hook chain.
	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
//关于为何使用ifdef __cplusplus
//参考:http://blog.csdn.net/miyunhong/article/details/4589541
extern "C" {
#endif
  	//DLL的导出函数
	__declspec(dllexport) void HookStart()
	{
      	//设置消息钩子
      	//WH_KEYBOARD 钩子类型 
      	//参考:https://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx#wh_keyboardhook
      	//KeyboardProc 钩子过程(回调函数)
      	//g_hInstance 钩子过程所在DLL的句柄
      	//0 想要挂钩的线程ID,若为0则为全局Hook
		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

0x04 调试KeyLogger.dll

首先运行notepad.exe

然后再OD的”调试选择“中选择Event->Break on new moudle(DLL)

开启该选项后,每当有新的DLL装入被调试进程时就会停止调试

0x05 参考资料

  1. MSDN Hook

    https://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx