全局钩子 实例(不使用DLL和使用DLL两种)

出处: CSDN 作者: xstring 

不知道大家是通过什么开始对钩子(Hook)有了解的,我是看过Jeffrey Richter的《WINDOWS 高级编程指南》(新版的中文译名为《Windows核心编程》)。在这本书里作者介绍了三种将代码注入其他进程的方法,其中一种就是使用的全局消息钩子。我就是从这本书里对全局钩子有了最初的认识。

大家应该都知道,全局消息钩子要依赖于一个DLL才能够正常工作。于是呢,我也就理所当在地认为全局钩子都要依赖于一个DLL才能正常工作的,我想大部分人肯定和我一样也这么认为的。

但实际上不是这样的。有某些全局钩子可以不依赖于任何DLL而正常工作的。这些钩子包括,WH_JOURNALPLAYBACK,WH_JOURNALRECORD,WH_KEYBOARD_LL,WH_MOUSE_LL。为什么这些钩子可以不依赖于DLL而正常工作呢?我们可以从MSDN中得到答案,MSDN中对于这四种钩子都这样的描述“This hook is called in the context of the thread that installed it.”,翻译成中文意思是钩子函数的调用是在安装钩子的线程上下文中进行的,说得更明白些,意思就是这些钩子是在哪个线程当中安装的,其钩子函数就在哪个线程中执行。所以使用这四种钩子是达不到代码注入的效果的,当然也就可以不依赖于任何DLL了。MSDN中只对个别钩子指出了必须还是没有必要使用DLL。

下面是我给出的一个底层键盘钩子的代码示例,当然是不需要DLL的。

/*

kbhook.cpp

*/

#define _WIN32_WINNT    0400

#define STRICT

#define WIN32_LEAN_AND_MEAN

#include <stdio.h>

#include <stdlib.h>

#include <windows.h>

DWORD   g_main_tid = 0;

HHOOK   g_kb_hook   = 0;

BOOL CALLBACK con_handler (DWORD)

    {

        PostThreadMessage (g_main_tid, WM_QUIT, 0, 0);

        return TRUE;

    };

LRESULT CALLBACK kb_proc (int code, WPARAM w, LPARAM l)

    {

        PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;

        const char *info = NULL;

        if (w == WM_KEYDOWN)

            info = "key dn";

        else if (w == WM_KEYUP)

            info = "key up";

        else if (w == WM_SYSKEYDOWN)

            info = "sys key dn";

        else if (w == WM_SYSKEYUP)

            info = "sys key up";

        printf ("%s - vkCode [%04x], scanCode [%04x]\n",

            info, p->vkCode, p->scanCode);

        // always call next hook

        return CallNextHookEx (g_kb_hook, code, w, l);

    };

int main (void)

    {

        g_main_tid = GetCurrentThreadId ();

        SetConsoleCtrlHandler (&con_handler, TRUE);

        g_kb_hook = SetWindowsHookEx (

            WH_KEYBOARD_LL,

            &kb_proc,

            GetModuleHandle (NULL), // 不能为NULL,否则失败

            0);

        if (g_kb_hook == NULL)

        {

            fprintf (stderr,

                "SetWindowsHookEx failed with error %d\n",

                ::GetLastError ());

            return 0;

        };

        // 消息循环是必须的,想知道原因可以查msdn

        MSG msg;

        while (GetMessage (&msg, NULL, 0, 0))

        {

            TranslateMessage (&msg);

            DispatchMessage (&msg);

        };

        UnhookWindowsHookEx (g_kb_hook);

        return 0;

    };



下面的文章是介绍钩子处理函数放在DLL中实现全局钩子

-----------------------------------
给DLL初学者——全程键盘钩子的一种简单实现

作者:未知 来源:从互联网收集整理并转载 发布时间:2005-3-6 0:49:30


全局钩子 实例(不使用DLL和使用DLL两种)_第1张图片


随着中间件技术的发展, DLL越来越为程序员所关注,因为使用DLL具有一系列优点,所以程序设计人员可能更多的在自己的软件中采用这种技术。

下面我就把以前做过的一个简单的全程键盘钩子分析一下。

钩子[以下简称Hook]是应用程序在Microsoft Windows 消息处理过程中设置的用来监控消息流并且处理系统中尚未到达目的窗口的某一类型消息过程的机制。如果Hook过程在应用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中动态调用它,它能实时对系统进行监控。根据需要,我们采用的是在DLL中实现Hook的方式[关于HOOK更详细的资料请查阅资料]。

在VC中新建一Win32 Dynamic-Link Library 工程,工程名为KBLock。AppWizard会生成相关文件,编译生成的KBLock.cpp:

#include "stdafx.h"

#include "KBLock.h"

HHOOK hhkHook=NULL;                            //定义钩子句柄

HINSTANCE hInstance=NULL;                  //程序实例

//下面的DLLMain相当于Win32程序中的WinMain函数,是入口点

BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD ul_reason_for_call,

                       LPVOID lpReserved

                                    )

{

    switch (ul_reason_for_call)

       {

              case DLL_PROCESS_ATTACH:

              case DLL_THREAD_ATTACH:

              case DLL_THREAD_DETACH:

              case DLL_PROCESS_DETACH:

                     break;

    }

       hInstance=(HINSTANCE)hModule;          //得到DLL实例

    return TRUE;

}

//这是处理键盘消息的主要函数,在其中进行禁止操作

LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam)

{

       if (nCode < 0)

       {

              return CallNextHookEx(hhkHook,nCode,wParam,lParam);

       }

       if (nCode != HC_ACTION)

       {

              return CallNextHookEx(hhkHook,nCode,wParam,lParam);

       }

//给出提示:键盘已经被锁定,要进行判断,看是否已有提示窗口,否则会弹个没完

       if (!::FindWindow(0, "KeyBoard Locked"))

       {

              ::MessageBox(0,"键盘已经锁定!!!","KeyBoard Locked",MB_OK);

       }

    return 1;   //没有return CallNextHookEx(hhkHook,nCode,wParam,lParam)则不会把消息//传递下去,所以我们的键盘就不起作用了

}

// This is an example of an exported variable

//导出函数:启动键盘锁定

BOOL EnableKeyboardCapture()

{

       if(!(hhkHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)HookProc,hInstance,0)))

              return FALSE;

       return TRUE;

}

//导出函数:解除键盘锁定

BOOL DisableKeyboardCapture()

{

        return UnhookWindowsHookEx(hhkHook);

}

上面就是DLL中最重要的代码,当然要使DLL能正常工作还要编辑KBLock.h文件:

__declspec(dllexport) BOOL EnableKeyboardCapture();       //加载钩子

__declspec(dllexport) BOOL DisableKeyboardCapture();       //卸载钩子

再编辑KBLock.def

; KBLock.def : Declares the module parameters for the DLL.

LIBRARY      "KBLock"

DESCRIPTION 'KBLock Windows Dynamic Link Library'

EXPORTS

    ; Explicit exports can go here

EnableKeyboardCapture @1

DisableKeyboardCapture @2

这样我们用Depends.exe查看这个DLL时,就会发现这两个导出函数了。

DLL方面的工作已经完成,这样我们就可以在程序中调用它了。

虽然DLL是由VC开发的,但调用它的前台程序可以用任何其它支持DLL调用的语言如:VB、VC、DELPHI、Win32asm实现,下面还是以VC为例,实现DLL的调用。

建一基于Dialog的工程,在其中加入两个按钮:“Lock KeyBoard”“UnLock”

在CexeDlg类中加入一个成员函数:

/*sign = TRUE 锁定 sign = FALSE解锁*/

BOOL CExeDlg::KBLock(BOOL sign)

{

       hDLL=::LoadLibrary(_T("KBLock.dll"));       //加载DLL

       if (hDLL!=NULL)

       {loadhook=(LOADHOOK)::GetProcAddress (hDLL,"EnableKeyboardCapture");

       unloadhook=(UNLOADHOOK)::GetProcAddress (hDLL,"DisableKeyboardCapture");

      

       if(loadhook==NULL||unloadhook==NULL)

       {::MessageBox(0,"对不起,本功能不能使用!!!","Somthing Wrong",MB_OK);

       return 0;

       }

       if(sign)

              loadhook();

       else

       {

              unloadhook();

              ::FreeLibrary(hDLL);

       }

       return 1;

       }

       ::MessageBox(0,"动态库加载失败!!!","Somthing Wrong",MB_OK);

       return 0;

}

其中用到了事先定义好的全局变量:

typedef BOOL (CALLBACK *LOADHOOK)();

typedef BOOL (CALLBACK *UNLOADHOOK)();

HINSTANCE hDLL=NULL;

LOADHOOK loadhook;

UNLOADHOOK unloadhook;


这样我们在两个按钮中分别加入KBLock(TRUE); 和 KBLock(FALSE);即可。

当然这里需要把dll文件放到相应的目录才行的。。


------

全局钩子的一定要用DLL吗?

4 楼kugou123(酷狗)(彪悍的人生,不需要解释 www.xiaozhou.net) 回复于 2004-07-11 23:17:04 得分 0 

钩子分为全局钩子和局部钩子。 
如果你只HOOK本进程得消息,可以把消息回调函数和调用钩子的函数写在一起,也就是只需要写个EXE就可以了。 
如果要HOOK全局消息,需要全局钩子,这样,需要把你的HOOK代码注入到系统每个进程里面去。而实现这个最好的方法,就是用DLL来实现。系统会自动把该DLL注入到所有的进程空间中。所以,不一定是必须要写DLL来HOOK,看你用的范围来决定!!!

--
26 楼codewarrior(会思考的草) 回复于 2005-02-09 16:01:12 得分 0

这要从头说起,在98下,内存划分为高2G和低2G,这两部分的作用就不用我罗嗦了吧,基本上和2k是差不多的。但是,有一个区别,对于dll,9x可能出于效率的考虑,把它装在高2G,这样可以在多个进程之间共享代码,所以像那3个著名的dll,都是在高2G里,所有进程使用同一份拷贝。这就使得全局api hook比较方便,只要把dll加载到高2G,用一个简单的jmp在api入口的地方跳到我们的hook dll里面,处理完了再跳回去即可。之所以做成dll,是因为dll可以方便地加载于高2G的地址空间,现在你明白了?只要能把一个模块加载到高2G的空间去,不一定要做成dll。 
但是同样的情况在2K下不再成立,dll装载于低2G中,即使是user32.dll这样的系统dll,也是每个进程有一个拷贝,所以你即使修改了本进程的dll,也不能影响到其他进程,要完成全局hook,就必须要将我们的hook dll自动加载到系统中每个进程当中去,对于将来运行的进程,也必须要自动加载才行,注册表中有一个表项,可以指定一个dll在每个进程启动的时候都必须自动加载,这样对于完成api hook稍微方便一点:)

你可能感兴趣的:(windows,null,dll,callback,hook,keyboard)