C++钩子(Hook)实现初探


闲来无事,大四了边找工作边开发。一直以来对钩子很感兴趣,为什么呢,因为许多人用它来做“坏事”,我就不明说了。研究这个只是兴趣而已,纯技术交流。

下面开始正题,所谓钩子可以百度下里面有很多解释,我可以不必在这里啰嗦了。研究了下,个人理解就是用来截获WINDOWS消息的API,基于消息机制的WINDOWS程序所产生的消息可以用钩子来截获。钩子把截获的消息做什么呢?这个要看程序员的心情了,可以把消息继续向下传递,也可以修改掉甚至不传递。在截获的过程中也可以把消息记录下来,实现键盘记录鼠标记录等等。

那么我以鼠标键盘记录为例来讲讲我的经验:
在网上搜索“键盘记录C++”实现可以找到很多相关文章,我也是照着上面的介绍去研究去试着做的,从懂到不懂。那么为什么有那么多材料我还要去写这样一篇文章,我想这个是我个人需要关心的问题,我不是那种ctrl+c ctrl+v的复制党,我只想将我所学的东西做个记录。我想每个人对知识的理解是站在不同角度的,希望我的文章可以给读者一些新的理解。
实现钩子的主要API函数:SetWindowsHookEx()。对于没有过研究的人(0基础),建议去百度搜索下这个函数,里面讲解的很详细了。其实有很多时候一些常用的函数在百度百科里都可以搜到的,讲解的很详细很好了。那么针对这个函数,我还是先照顾那些不爱去动手搜索的人,说说它的几个参数。
HHOOK SetWindowsHookEx(          
    int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
)
int idHook :int型的参数,一般我们在传递参数时用定义好的宏进行传递,该参数表示安装钩子的类型,比如要安装一个鼠标的钩子那么参数可以写WH_MOUSE或者WH_MOUSE_LL,他们的区别是前者是当调用GetMessage 或者 PeekMessage 函数所触发的鼠标消息,后者是当有鼠标消息事件产生即触发。同理WH_KEYBOARD与WH_KEYBOARD_LL。还有其他类型的钩子,那么可以根据你的程序需要去定制了。
HOOKPROC lpfn :传递一个处理过程(子程),也就是说当触发了你的钩子,该处理过程负责对产生的消息处理。也就是说,当你安装了键盘钩子去监视键盘消息,当产生了消息就需要调用这个处理过程了。这个过程是自己写的,但它有预先定义好的形式,在下面会讲到。
HINSTANCE hMod :应用程序实例句柄,传递的是所调用的子程的指针地址。如果子程是在程序内部定义的那么该参数必须为NULL。
DWORD dwThreadId:线程标识符,该参数表示与子程相关联的线程ID。通常情况下该参数写0时为全局钩子。

钩子子程:不同类型的钩子对应不同的处理子程,子程也是一个函数,函数内部由程序员自己设计对消息的处理。看下定义:
LRESULT CALLBACK HookProc(
    int nCode, 
    WPARAM wParam, 
    LPARAM lParam
)
int nCode :nCode参数是Hook代码,Hook子程使用这个参数来确定任务。该参数取不同的值代表不同类型的钩子以调用不同的子程。你可以暂时忽略这个参数,照写就行。
WPARAM wParam 、LPARAM lParam :分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。可以说这两个参数包含了我们所要处理的鼠标、键盘、以及其他类型钩子的信息。
当钩子子程的返回值为1时表示该子程已经对消息进行过了处理,将不再向下传递。在鼠标、键盘钩子中的效果就是屏蔽了所有的鼠标点击和键盘按下消息,对于该类型的输入操作程序将不予响应。

下面帖上个程序代码:
#define _WIN32_WINNT 0x0500
#include <Windows.h>

using namespace std;

HHOOK g_hMouse ;//鼠标钩子全局变量
HHOOK g_hKeyb ;
//键盘钩子全局变量

LRESULT CALLBACK MouseProc(          
         int nCode,
         WPARAM wParam,
         LPARAM lParam
         )
{

//添加任意自定义代码,对wParam、lParam消息进行处理。


//将消息向下一个钩子传递

return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK KeybdProc(          
         int nCode,
         WPARAM wParam,
         LPARAM lParam
         )
{
     //添加任意自定义代码,对wParam、lParam消息进行处理。


     //将消息向下一个钩子传递
     return CallNextHookEx(NULL, nCode, wParam, lParam);
}

void SetHook() {
//钩子的安装
g_hMouse = SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("HookDll"),0);
g_hKeyb = SetWindowsHookEx(WH_KEYBOARD_LL,KeybdProc,GetModuleHandle("HookDll"),0);
//GetModuleHandle函数用来获取调用子程DLL的指针地址。
}
void UnHook(){
//卸载鼠标、键盘钩子
UnHookWindowsHookEx(g_hMouse);
UnHookWindowsHookEx(g_hKeyb);

}
如上代码是写在一个简单的钩子DLL中的框架,当然它是不能运行的了,可以用来参考学习。
里面有两个函数是上面没说过的:
CallNextHookEx():将消息向下一个钩子传递。
UnHookWindowsHookEx():卸载钩子。

以上就是我对C++下实现钩子的理解,我想对于初学者来说可能会有点用。因为我讲的很直白,我也是个初学者,都是我的个人理解。希望能给需要的人带来帮助。也希望有不对的地方朋友们帮我指正。

目前最好的EasyHook的完整Demo程序,包括了Hook.dll动态库和Inject.exe注入程序。 Hook.dll动态库封装了一套稳定的下钩子的机制,以后对函数下钩子,只需要填下数组表格就能实现了,极大的方便了今后的使用。 Inject.exe是用MFC写的界面程序,只需要在界面上输入进程ID就能正确的HOOK上相应的进程,操作起来非常的简便。 这个Demo的代码风格也非常的好,用VS2010成功稳定编译通过,非常值得下载使用。 部分代码片段摘录如下: //【Inject.exe注入程序的代码片段】 void CInjectHelperDlg::OnBnClickedButtonInjectDllProcessId() { ////////////////////////////////////////////////////////////////////////// //【得到进程ID值】 UINT nProcessID = 0; if (!GetProcessID(nProcessID)) { TRACE(_T("%s GetProcessID 失败"), __FUNCTION__); return; } ////////////////////////////////////////////////////////////////////////// //【得到DLL完整路径】 CString strPathDLL; if (!GetDllFilePath(strPathDLL)) { TRACE(_T("%s GetDllFilePath 失败"), __FUNCTION__); return; } ////////////////////////////////////////////////////////////////////////// //【注入DLL】 NTSTATUS ntStatus = RhInjectLibrary(nProcessID, 0, EASYHOOK_INJECT_DEFAULT, strPathDLL.GetBuffer(0), NULL, NULL, 0); if (!ShowStatusInfo(ntStatus)) { TRACE(_T("%s ShowStatusInfo 失败"), __FUNCTION__); return; } } //【Hook.dll动态库的代码片段】 extern "C" __declspec(dllexport) void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* InRemoteInfo) { if (!DylibMain()) { TRACE(_T("%s DylibMain 失败"), __FUNCTION__); return; } } FUNCTIONOLDNEW_FRMOSYMBOL array_stFUNCTIONOLDNEW_FRMOSYMBOL[]= { {_T("kernel32"), "CreateFileW", (void*)CreateFileW_new}, {_T("kernel32"), "CreateFileA", (void*)CreateFileA_new}, {_T("kernel32"), "ReadFile", (void*)ReadFile_new} }; BOOL HookFunctionArrayBySymbol() { /////////////////////////////////////////////////////////////// int nPos = 0; do { /////////////////////////////// FUNCTIONOLDNEW_FRMOSYMBOL* stFunctionOldNew = &g_stFUNCTIONOLDNEW_FRMOSYMBOL[nPos]; if (NULL == stFunctionOldNew->strModuleName) { break; } /////////////////////////////// if (!HookFunctionBySymbol(stFunctionOldNew->strModuleName, stFunctionOldNew->strNameFunction, stFunctionOldNew->pFunction_New)) { TRACE(_T("%s HookFunctionBySymbol 失败"), __FUNCTION__); return FALSE; } } while(++nPos); /////////////////////////////////////////////////////////////// return TRUE; } HANDLE WINAPI CreateFileW_new( PWCHAR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { TRACE(_T("CreateFileW_new. lpFileName = %s"), lpFileName); return CreateFileW( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值