Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1683500
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: WINDOWS

2011-01-19 12:23:27

        Windows系统都是通过消息机制来管理应用程序的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来 完成普通应用程序难以实现的功能。钩子可以监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理。这样,我们就可以在系统中安装自定义的钩 子,监视系统中特定事件的发生,完成特定的功能,比如截获键盘、鼠标的输入,屏幕取词,日志监视等等。可见,利用钩子可以实现许多特殊而有用的功能。因 此,对于高级编程人员来说,掌握钩子的编程方法是很有必要的。
钩子的类型一共分为十三种,如日志钩子,键盘钩子,鼠标钩子,消息钩子等等。按作用 域分钩子又可以分为全局钩子和局部钩子,也就是大家常说的线程钩子和系统钩子,线程钩子只有在当前线程中有效,如果要实现系统钩子就必须把钩子放到DLL 文件中。由于系统钩子要用到DLL所以我们先用C++Builder的向导生成一个空的DLL文件,并加入自己的代码,在讲代码之前我先向大家讲关于编写 HOOK的几相基本的API函数
我们就最常用的键盘钩子为例,给大家讲讲编写钩子的基本过程,下面我们分三步讲解在C++Builder下如何安装钩子,定义钩子和卸载钩子。
1.安装钩子
  在程序初始化的时候,调用函数SetWindowsHookEx安装钩子。其函数原型为:
HHOOK SetWindowsHookEx(
    int idHook,          // type of hook to install
    HOOKPROC lpfn,      // address of hook procedure
    HINSTANCE hMod,  // handle of application instance
    DWORD dwThreadId   // identity of thread to install hook for
   );
这 个API函数在CB的帮助文档中有介绍,参数idHook指的是钩子类型,指的是键盘钩子或是鼠标钩子还是其它。我们这里要写的是一个键盘钩子,所以是 WH_KEYBOARD。参数lpfn,是指向执行这个钩子的一个过程的地址。参数hMod是指这个调用钩子应用程序实例。参数dwThreadId就是 线程钩子用到的线程ID,如果是全局钩子规定这个参数为0。
消息如果被钩子拦截不能继续传下去,务必会造成系统不能正常运行,所以就要用到了函数CallNextHookEx, CallNextHookEx.可以把钩子信息传递给钩子链的下一个钩子函数。其函数原型为:
    LRESULT CallNextHookEx(
    HHOOK hhk,       // handle to current hook
    int nCode,         // hook code passed to hook procedure
    WPARAM wParam, // value passed to hook procedure
    LPARAM lParam   // value passed to hook procedure
   );
参数hhk是钩子句柄。nCode、wParam和lParam个是钩子函数。当然也可以通过直接返回TRUE来丢弃该消息,就阻止了该消息的传递。
2.定义钩子函数
  我们要钩子做什么呢?所以我们必须定义钩子要做的事,钩子监视的特定事件发生后,会调用钩子函数进行相应的处理。钩子函数也是一种特殊的回调函数。钩子监视的特定事件发生后,系统会调用钩子函数进行处理。其函数原型为:
     LRESULT CALLBACK KeyboardProc(
    int code,           // hook code
    WPARAM wParam,   // virtual-key code
    LPARAM lParam    // keystroke-message information
   );
参数wParam和 lParam包含所钩消息的信息,键盘按键状态。nCode包含有关消息本身的信息。
3.卸载钩子
 当不再使用钩子时,必须及时卸载。简单地调用函数UnhookWindowsHookEx即可,其函数原型为:
BOOL UnhookWindowsHookEx(
    HHOOK hhk   // handle of hook procedure to remove
   );
参数hhk为钩子句柄。
好了,准备知识已经学完了,让我们开始编写个全局的钩子程序吧!我们写一个捕捉Ctrl+V的键盘钩子。让其粘贴出来的总是我们事先定义好的内容。
下面是后面要用的三个全局变量。
static HHOOK hkb = NULL;
TCHAR buf[] = "都是钩子惹的祸";
HINSTANCE hinstDLL;
在DLL入口函数中我们初始化钩子所属实例的句柄。
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
        hinstDLL = hinst;
        return 1;
}
下面这个就是定义钩子的过程了,在这个函数里面写下自己的代码,只要有键盘动作就会触发这个函数,这里我用了条件,只捕捉Ctrl+V键。无论你剪贴板里是什么内容,粘出来的是“都是钩子惹的祸”。
extern "C" __declspec(dllexport) __stdcall LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
       if (wParam == 0x56 && GetAsyncKeyState(VK_CONTROL) < 0 && lParam >= 0)
        {
          HGLOBAL hMem;
          char* pStr;
          //分配内存空间,这里返回一个内存块句柄。
          hMem = GlobalAlloc(GHND | GMEM_SHARE, sizeof(buf));
          //pStr指向锁定的一段内存地址,只能通过pStr来修改这块内存
          pStr = (char*)GlobalLock(hMem);
          //更新这段地址内容
          strcpy(pStr, buf);
          //内存块解锁
          GlobalUnlock(hMem);
          //打开剪贴板,如果没有打开剪贴板执行到EmptyClipboard就会出错
          OpenClipboard(NULL);
          EmptyClipboard();
          //设置剪贴板文本
          SetClipboardData(CF_TEXT, hMem);
          CloseClipboard();
          //释放内存空间
          GlobalFree(hMem);      
  }
//将钩子传下去,如果不写这么一句那么Ctrl+V就失灵了。
        LRESULT RetVal = CallNextHookEx(hkb,
                                        nCode,
                                        wParam,
                                        lParam);
return  RetVal;
}
下面的这个installhook是我提供给外界的一个接口,只要其它应用程序调用这个接口就可以完成钩子的安装。
extern "C"  __declspec(dllexport) __stdcall BOOL installhook()
{
hkb=SetWindowsHookEx(WH_KEYBOARD,               //键盘钩子
                             (HOOKPROC)KeyboardProc,   //处理钩子的事件
                             hinstDLL,                     //钩子所属实例的句柄
                             0);                           //全局钩子
        if (hkb == NULL)
          return FALSE;
return TRUE;
}
用完了记得一定要释放,不然DLL会常驻内存,直到你注销或是重启,所以编程的时候一定要养成释放内存空间的好习惯。直接调用UnHook来释放钩子。
extern "C" __declspec(dllexport) __stdcall BOOL UnHook()
{    
BOOL unHooked = UnhookWindowsHookEx(hkb);
return unHooked;
}
DLL写完了,保存。打开菜单Project->Build All Project编译。会在原目录下产生一个相应的DLL文件和一个Lib文件。
好了,现在我们就验证这个HOOK的功能,为了方便我先用静态方法调用链接库。
1、 现在新建一个Application程序,放两个按钮,在菜单Project->Add to Project添加这个Lib文件。
2、 再在程序中前面先声明一下
extern "C" __declspec(dllimport) __stdcall BOOL UnHook();
extern "C" __declspec(dllimport) __stdcall BOOL installhook();
3、 然后分别在两个按钮事件中写下installhook();和UnHook();就可以了。一个装载钩子,一个卸载钩子。简单吧?此程序稍做修改就可以达到像记忆按键的功能,有兴趣的同学可以自己动手修改一下。
4、 加载钩子后再试试打开记事本或是Word按一下Ctrl+V看看有什么变化?
阅读(1239) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~